Automatic Configuration Backup With Version Control


There are a variety of devices that have a multitude of ways to backup their configuration, although often you find yourself needing to purchase additional tools from a vendor to do so, or be forever stuck with copying that pesky switch, router, proxy or other configuration to some TFTP server on some (hopefully) regular schedule.  In 2009, I set Matt Heckman on the task to create a process to create an automatic configuration backup for a variety of our network devices and the results have served us well ever since.

The automatic configuration backup will connect to a network device, execute a series of commands

Requirements:  WinSCP (for sftp support), Windows Powershell (with appropriate script execution policies), putty suite, SVN server

 

CbAddDeviceCred.ps1

# 
# Author: Matt Heckman, 2009
# 
# Credits: 
#
# Published with permission at www.atumvirt.com
#
#

Import-Module ".CBFunctions.ps1"

Write-Host "**** Config Backup: Add Device Credential ****"
$DeviceName = Read-Host "Device Name"

if (Test-Path "$DeviceCredPath$DeviceName.xml") {
 $Overwrite = Read-Host "Credential for $DeviceName already exists. Overwrite? (Y/N)" 
 if ($Overwrite.ToUpper() -ne "Y") {
 Exit
 }
 
 Remove-Item "$DeviceCredPath$DeviceName.xml" -Force
}

$LoginCred = $host.ui.PromptForCredential("Login Credential", "Please enter the username and password to login to the device.", "", "")
$EnableCred = $host.ui.PromptForCredential("Enable Credential", "Please enter the username and/or password for enable mode.", "", "")

$EncDeviceCred = New-Object PSObject -Property @{
 LoginUser = $LoginCred.UserName.TrimStart('') | ConvertTo-SecureString -AsPlainText -Force -ErrorAction "SilentlyContinue" | ConvertFrom-SecureString -ErrorAction "SilentlyContinue"
 LoginPass = $LoginCred.Password | ConvertFrom-SecureString -ErrorAction "SilentlyContinue"
 EnableUser = $EnableCred.UserName.TrimStart('') | ConvertTo-SecureString -AsPlainText -Force -ErrorAction "SilentlyContinue" | ConvertFrom-SecureString -ErrorAction "SilentlyContinue"
 EnablePass = $EnableCred.Password | ConvertFrom-SecureString -ErrorAction "SilentlyContinue" 
}

$EncDeviceCred | Export-Clixml "$DeviceCredPath$DeviceName.xml"

Run this script first to output securely stored credential files.  The files can only be decrypted by the user who encrypts them, so make sure you run powershell as whatever service account you intend to run this process.

CBFunctions.ps1

 

# 
# Author: Matt Heckman, 2009
# 
# Credits: Some portions may be reused from other published sources
#
# Published with permission at www.atumvirt.com
#
#

$Repository = "https://svn.contoso.local/svn/ConfigBackups"
$SvnClient = "C:Program Files (x86)VisualSVN Serverbinsvn.exe"
$DeviceCredPath = "C:ScriptsConfigBackupDeviceCreds"
$DeviceScriptPath = "C:ScriptsConfigBackupDeviceScripts"
$DeviceConfigPath = "C:ScriptsConfigBackupDeviceConfigs"
$ConfigFileName = "Config.txt"
$ErrorLogPath = "Errors.txt"

function Get-CbDeviceScript {
 param(
 [string] $DeviceName
 )

 if (Test-Path "$DeviceScriptPath$DeviceName.txt") {
 $DeviceScript = Get-Content "$DeviceScriptPath$DeviceName.txt"

 return $DeviceScript
 } else {
 Throw "No script file exists for device ""$DeviceName"""
 }
}

function Get-CbDeviceCred {
 param(
 [string] $DeviceName
 )

 if (Test-Path "$DeviceCredPath$DeviceName.xml") {
 $EncDeviceCred = Import-Clixml "$DeviceCredPath$DeviceName.xml"
 $DeviceCred = @{}

 $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(($EncDeviceCred.LoginUser | ConvertTo-SecureString))
 $LoginUser = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
 [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
 $DeviceCred.Add('LoginUser', $LoginUser)

 $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(($EncDeviceCred.LoginPass | ConvertTo-SecureString))
 $LoginPass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
 [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
 $DeviceCred.Add('LoginPass', $LoginPass)

 $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(($EncDeviceCred.EnableUser | ConvertTo-SecureString))
 $EnableUser = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
 [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
 $DeviceCred.Add('EnableUser', $EnableUser)

 $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(($EncDeviceCred.EnablePass | ConvertTo-SecureString))
 $EnablePass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
 [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
 $DeviceCred.Add('EnablePass', $EnablePass)

 return $DeviceCred
 } else {
 Throw "No cred file exists for device ""$DeviceName"""
 }
}

function Checkout-CbDeviceBackup {
 param(
 [string] $DeviceName
 )

 if ((. $SvnClient list "$Repository/$DeviceName") -ne $null) {
 $RemoteFolderExists = $true
 } else {
 $RemoteFolderExists = $false
 }
 $LocalFolderExists = Test-Path "$DeviceConfigPath$DeviceName"

 if ($LocalFolderExists) {
 Remove-Item "$DeviceConfigPath$DeviceName*" -Recurse -Force
 }

 if ($RemoteFolderExists -eq $true) { 
 . $SvnClient checkout "$Repository/$DeviceName" "$DeviceConfigPath/$DeviceName" --force
 } else {
 New-Item -type Directory -path "$DeviceConfigPath$DeviceName"
 }
}

function Checkin-CbDeviceBackup {
 param(
 [string] $DeviceName
 )

 $RemoteFolderExists = (. $SvnClient list "$Repository/$DeviceName") -ne $null
 
 if ($RemoteFolderExists -ne $true) { 
 . $SvnClient import "$DeviceConfigPath/$DeviceName" "$Repository/$DeviceName" -m "Initial import for $DeviceName."
 } else {
 . $SvnClient commit "$DeviceConfigPath/$DeviceName" -m "Revised."
 }
}

function Backup-CbSshDevice {
 param(
 [string] $DeviceName,
 [string] $RemoteHost
 )
 
 $DeviceCred = Get-CbDeviceCred $DeviceName
 $DeviceScript = Get-CbDeviceScript $DeviceName
 Checkout-CbDeviceBackup $DeviceName
 $DeviceConfig = @()

 for ($i = 0; $i -lt $DeviceScript.Count; $i++) {
 if ($DeviceScript[$i] -match '###LoginUser###') {
 $DeviceScript[$i] = $DeviceScript[$i] -replace '###LoginUser###',$DeviceCred.LoginUser
 } 
 
 if ($DeviceScript[$i] -match '###LoginPass###') {
 $DeviceScript[$i] = $DeviceScript[$i] -replace '###LoginPass###',$DeviceCred.LoginPass
 } 
 
 if ($DeviceScript[$i] -match '###EnableUser###') {
 $DeviceScript[$i] = $DeviceScript[$i] -replace '###EnableUser###',$DeviceCred.EnableUser
 }
 
 if ($DeviceScript[$i] -match '###EnablePass###') {
 $DeviceScript[$i] = $DeviceScript[$i] -replace '###EnablePass###',$DeviceCred.EnablePass
 }
 }

 $DeviceScript | Out-File -FilePath "$DeviceScriptPath/Temp.txt" -Encoding ascii
 echo y | .puttyplink.exe -ssh $RemoteHost -l $DeviceCred.LoginUser -pw $DeviceCred.LoginPass -m "$DeviceScriptPath/Exit.txt"
 .puttyplink.exe -ssh $RemoteHost -l $DeviceCred.LoginUser -pw $DeviceCred.LoginPass -m "$DeviceScriptPath/Temp.txt" | Out-File -FilePath "$DeviceConfigPath/$DeviceName/$ConfigFileName"
 Remove-Item -Path "$DeviceScriptPath/Temp.txt" -Force
 Checkin-CbDeviceBackup $DeviceName
}

function Backup-CbSftpDevice {
 param(
 [string] $DeviceName,
 [string] $RemoteHost,
 [string] $Fingerprint,
 [string] $Directory,
 [string] $Filename
 )

 Checkout-CbDeviceBackup $DeviceName
 $DeviceCred = Get-CbDeviceCred $DeviceName
 Sync-WinScp -Protocol 'Sftp' -HostName $RemoteHost -SshHostKeyFingerprint $Fingerprint -UserName $DeviceCred.LoginUser -Password $DeviceCred.LoginPass -RemoteDirectory $Directory -RemoteFilename $Filename -LocalDirectory "$DeviceConfigPath/$DeviceName"
 Checkin-CbDeviceBackup $DeviceName
}

 

The meat, this file contains the necessary pieces to backup different types of devices

ConfigBackup.ps1

# 
# Author: Matt Heckman, 2009
# 
# Credits: 
#
# Published with permission at www.atumvirt.com
#
#

Import-Module ".CbFunctions.ps1"
Import-Module ".WinScpFunctions.ps1"

Backup-CbSshDevice -DeviceName "BlueCoatProxySG900-30" -RemoteHost "proxy.contoso.local"
Backup-CbSshDevice -DeviceName "CiscoASA5520" -RemoteHost "asa.contoso.local"
Backup-CbSftpDevice -DeviceName "Netscaler" -RemoteHost "netscaler.contoso.local" -Fingerprint "ssh-rsa 1024 8e:ff:ff:ff:ff:ff:ff:aa:ff:ff:ff:ff:ff:ff:ff:ef" -Directory "/nsconfig" -Filename ""
Backup-CbSftpDevice -DeviceName "Netscaler2" -RemoteHost "netscaler2.contoso.local" -Fingerprint "ssh-rsa 1024 88e:ff:ff:ff:ff:ff:ff:aa:ff:ff:ff:ff:ff:ff:ff:ff" -Directory "/nsconfig" -Filename ""

This file defines the jobs you wish to run.  As you see, you can backup SSH or Sftp devices fairly easily.  You must have WinScp installed to take advantage of the SFTP functionality, and you need and SVN client (in our case, this runs from the same server that runs SVN server, which has a SVN client built in)

WinSCPFunctions.ps1

# 
# Author: Matt Heckman, 2009
#
# Credits: http://winscp.net/eng/docs/library_powershell
#
# Published with permission at www.atumvirt.com
#
#


# Load WinSCP .NET assembly
[Reflection.Assembly]::LoadFrom("C:Program Files (x86)WinSCPWinSCP.dll") | Out-Null

function Sync-WinScp {
 param(
 [string] $Protocol,
 [string] $HostName,
 [string] $SshHostKeyFingerprint,
 [string] $UserName,
 [string] $Password,
 [string] $RemoteDirectory,
 [string] $RemoteFilename,
 [string] $LocalDirectory
 )
 
 if ($Protocol -eq 'Sftp') {
 try
 { 
 # Setup session options
 $sessionOptions = New-Object WinSCP.SessionOptions
 $sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
 $sessionOptions.HostName = $HostName
 $sessionOptions.SshHostKeyFingerprint = $SshHostKeyFingerprint
 $sessionOptions.UserName = $UserName
 $sessionOptions.Password = $Password
 
 $session = New-Object WinSCP.Session
 $syncResult = $null

 try
 {
 # Connect
 $session.Open($sessionOptions)
 
 $fileName = $RemoteFilename
 $remotePath = $RemoteDirectory
 $localPath = $LocalDirectory
 
 $transferOptions = New-Object WinSCP.TransferOptions
 $transferOptions.FileMask = $fileName
 $syncResult = $session.SynchronizeDirectories([WinSCP.SynchronizationMode]::Local, $LocalDirectory, $RemoteDirectory, $true, $true, [WinSCP.SynchronizationCriteria]::Either, $transferOptions)
 }
 finally
 {
 # Disconnect, clean up
 $session.Dispose()
 }

 return $syncResult
 }
 catch [Exception]
 {
 Write-Host $_.Exception.Message
 }
 } else {
 Write-Host "Protocol $Protocol unsupported"
 }
}

The bits necessary for SFTP support.

 

/DeviceScripts/*

This directory contains example device scripts of commands to issue for each device type.  The CSV format is used to issue a command, expected response in the case of unusual console output.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.