{"id":2691,"date":"2014-05-01T17:06:57","date_gmt":"2014-05-02T01:06:57","guid":{"rendered":"http:\/\/www.atumvirt.com\/?p=2691"},"modified":"2014-05-01T17:06:57","modified_gmt":"2014-05-02T01:06:57","slug":"automatic-configuration-backup-with-version-control","status":"publish","type":"post","link":"https:\/\/avtempwp.azurewebsites.net\/2014\/05\/automatic-configuration-backup-with-version-control\/","title":{"rendered":"Automatic Configuration Backup With Version Control"},"content":{"rendered":"

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. \u00a0In 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.<\/p>\n

The automatic configuration backup will connect to a network device, execute a series of commands<\/p>\n

Requirements: \u00a0WinSCP (for sftp support), Windows Powershell (with appropriate script execution policies), putty suite, SVN server<\/p>\n

 <\/p>\n

CbAddDeviceCred.ps1<\/h3>\n

<\/h3>\n
# \n# Author: Matt Heckman, 2009\n# \n# Credits: \n#\n# Published with permission at www.atumvirt.com\n#\n#\n\nImport-Module \".CBFunctions.ps1\"\n\nWrite-Host \"**** Config Backup: Add Device Credential ****\"\n$DeviceName = Read-Host \"Device Name\"\n\nif (Test-Path \"$DeviceCredPath$DeviceName.xml\") {\n $Overwrite = Read-Host \"Credential for $DeviceName already exists. Overwrite? (Y\/N)\" \n if ($Overwrite.ToUpper() -ne \"Y\") {\n Exit\n }\n \n Remove-Item \"$DeviceCredPath$DeviceName.xml\" -Force\n}\n\n$LoginCred = $host.ui.PromptForCredential(\"Login Credential\", \"Please enter the username and password to login to the device.\", \"\", \"\")\n$EnableCred = $host.ui.PromptForCredential(\"Enable Credential\", \"Please enter the username and\/or password for enable mode.\", \"\", \"\")\n\n$EncDeviceCred = New-Object PSObject -Property @{\n LoginUser = $LoginCred.UserName.TrimStart('') | ConvertTo-SecureString -AsPlainText -Force -ErrorAction \"SilentlyContinue\" | ConvertFrom-SecureString -ErrorAction \"SilentlyContinue\"\n LoginPass = $LoginCred.Password | ConvertFrom-SecureString -ErrorAction \"SilentlyContinue\"\n EnableUser = $EnableCred.UserName.TrimStart('') | ConvertTo-SecureString -AsPlainText -Force -ErrorAction \"SilentlyContinue\" | ConvertFrom-SecureString -ErrorAction \"SilentlyContinue\"\n EnablePass = $EnableCred.Password | ConvertFrom-SecureString -ErrorAction \"SilentlyContinue\" \n}\n\n$EncDeviceCred | Export-Clixml \"$DeviceCredPath$DeviceName.xml\"<\/pre>\n

Run this script first to output securely stored credential files. \u00a0The 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.<\/p>\n

CBFunctions.ps1<\/p>\n

 <\/p>\n

# \n# Author: Matt Heckman, 2009\n# \n# Credits: Some portions may be reused from other published sources\n#\n# Published with permission at www.atumvirt.com\n#\n#\n\n$Repository = \"https:\/\/svn.contoso.local\/svn\/ConfigBackups\"\n$SvnClient = \"C:Program Files (x86)VisualSVN Serverbinsvn.exe\"\n$DeviceCredPath = \"C:ScriptsConfigBackupDeviceCreds\"\n$DeviceScriptPath = \"C:ScriptsConfigBackupDeviceScripts\"\n$DeviceConfigPath = \"C:ScriptsConfigBackupDeviceConfigs\"\n$ConfigFileName = \"Config.txt\"\n$ErrorLogPath = \"Errors.txt\"\n\nfunction Get-CbDeviceScript {\n param(\n [string] $DeviceName\n )\n\n if (Test-Path \"$DeviceScriptPath$DeviceName.txt\") {\n $DeviceScript = Get-Content \"$DeviceScriptPath$DeviceName.txt\"\n\n return $DeviceScript\n } else {\n Throw \"No script file exists for device \"\"$DeviceName\"\"\"\n }\n}\n\nfunction Get-CbDeviceCred {\n param(\n [string] $DeviceName\n )\n\n if (Test-Path \"$DeviceCredPath$DeviceName.xml\") {\n $EncDeviceCred = Import-Clixml \"$DeviceCredPath$DeviceName.xml\"\n $DeviceCred = @{}\n\n $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(($EncDeviceCred.LoginUser | ConvertTo-SecureString))\n $LoginUser = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)\n [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)\n $DeviceCred.Add('LoginUser', $LoginUser)\n\n $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(($EncDeviceCred.LoginPass | ConvertTo-SecureString))\n $LoginPass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)\n [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)\n $DeviceCred.Add('LoginPass', $LoginPass)\n\n $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(($EncDeviceCred.EnableUser | ConvertTo-SecureString))\n $EnableUser = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)\n [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)\n $DeviceCred.Add('EnableUser', $EnableUser)\n\n $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(($EncDeviceCred.EnablePass | ConvertTo-SecureString))\n $EnablePass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)\n [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)\n $DeviceCred.Add('EnablePass', $EnablePass)\n\n return $DeviceCred\n } else {\n Throw \"No cred file exists for device \"\"$DeviceName\"\"\"\n }\n}\n\nfunction Checkout-CbDeviceBackup {\n param(\n [string] $DeviceName\n )\n\n if ((. $SvnClient list \"$Repository\/$DeviceName\") -ne $null) {\n $RemoteFolderExists = $true\n } else {\n $RemoteFolderExists = $false\n }\n $LocalFolderExists = Test-Path \"$DeviceConfigPath$DeviceName\"\n\n if ($LocalFolderExists) {\n Remove-Item \"$DeviceConfigPath$DeviceName*\" -Recurse -Force\n }\n\n if ($RemoteFolderExists -eq $true) { \n . $SvnClient checkout \"$Repository\/$DeviceName\" \"$DeviceConfigPath\/$DeviceName\" --force\n } else {\n New-Item -type Directory -path \"$DeviceConfigPath$DeviceName\"\n }\n}\n\nfunction Checkin-CbDeviceBackup {\n param(\n [string] $DeviceName\n )\n\n $RemoteFolderExists = (. $SvnClient list \"$Repository\/$DeviceName\") -ne $null\n \n if ($RemoteFolderExists -ne $true) { \n . $SvnClient import \"$DeviceConfigPath\/$DeviceName\" \"$Repository\/$DeviceName\" -m \"Initial import for $DeviceName.\"\n } else {\n . $SvnClient commit \"$DeviceConfigPath\/$DeviceName\" -m \"Revised.\"\n }\n}\n\nfunction Backup-CbSshDevice {\n param(\n [string] $DeviceName,\n [string] $RemoteHost\n )\n \n $DeviceCred = Get-CbDeviceCred $DeviceName\n $DeviceScript = Get-CbDeviceScript $DeviceName\n Checkout-CbDeviceBackup $DeviceName\n $DeviceConfig = @()\n\n for ($i = 0; $i -lt $DeviceScript.Count; $i++) {\n if ($DeviceScript[$i] -match '###LoginUser###') {\n $DeviceScript[$i] = $DeviceScript[$i] -replace '###LoginUser###',$DeviceCred.LoginUser\n } \n \n if ($DeviceScript[$i] -match '###LoginPass###') {\n $DeviceScript[$i] = $DeviceScript[$i] -replace '###LoginPass###',$DeviceCred.LoginPass\n } \n \n if ($DeviceScript[$i] -match '###EnableUser###') {\n $DeviceScript[$i] = $DeviceScript[$i] -replace '###EnableUser###',$DeviceCred.EnableUser\n }\n \n if ($DeviceScript[$i] -match '###EnablePass###') {\n $DeviceScript[$i] = $DeviceScript[$i] -replace '###EnablePass###',$DeviceCred.EnablePass\n }\n }\n\n $DeviceScript | Out-File -FilePath \"$DeviceScriptPath\/Temp.txt\" -Encoding ascii\n echo y | .puttyplink.exe -ssh $RemoteHost -l $DeviceCred.LoginUser -pw $DeviceCred.LoginPass -m \"$DeviceScriptPath\/Exit.txt\"\n .puttyplink.exe -ssh $RemoteHost -l $DeviceCred.LoginUser -pw $DeviceCred.LoginPass -m \"$DeviceScriptPath\/Temp.txt\" | Out-File -FilePath \"$DeviceConfigPath\/$DeviceName\/$ConfigFileName\"\n Remove-Item -Path \"$DeviceScriptPath\/Temp.txt\" -Force\n Checkin-CbDeviceBackup $DeviceName\n}\n\nfunction Backup-CbSftpDevice {\n param(\n [string] $DeviceName,\n [string] $RemoteHost,\n [string] $Fingerprint,\n [string] $Directory,\n [string] $Filename\n )\n\n Checkout-CbDeviceBackup $DeviceName\n $DeviceCred = Get-CbDeviceCred $DeviceName\n Sync-WinScp -Protocol 'Sftp' -HostName $RemoteHost -SshHostKeyFingerprint $Fingerprint -UserName $DeviceCred.LoginUser -Password $DeviceCred.LoginPass -RemoteDirectory $Directory -RemoteFilename $Filename -LocalDirectory \"$DeviceConfigPath\/$DeviceName\"\n Checkin-CbDeviceBackup $DeviceName\n}<\/pre>\n

 <\/p>\n

The meat, this file contains the necessary pieces to backup different types of devices<\/p>\n

ConfigBackup.ps1<\/h3>\n
# \n# Author: Matt Heckman, 2009\n# \n# Credits: \n#\n# Published with permission at www.atumvirt.com\n#\n#\n\nImport-Module \".CbFunctions.ps1\"\nImport-Module \".WinScpFunctions.ps1\"\n\nBackup-CbSshDevice -DeviceName \"BlueCoatProxySG900-30\" -RemoteHost \"proxy.contoso.local\"\nBackup-CbSshDevice -DeviceName \"CiscoASA5520\" -RemoteHost \"asa.contoso.local\"\nBackup-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 \"\"\nBackup-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 \"\"<\/pre>\n

This file defines the jobs you wish to run. \u00a0As you see, you can backup SSH or Sftp devices fairly easily. \u00a0You 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)<\/p>\n

WinSCPFunctions.ps1<\/h3>\n
# \n# Author: Matt Heckman, 2009\n#\n# Credits: http:\/\/winscp.net\/eng\/docs\/library_powershell\n#\n# Published with permission at www.atumvirt.com\n#\n#\n\n\n# Load WinSCP .NET assembly\n[Reflection.Assembly]::LoadFrom(\"C:Program Files (x86)WinSCPWinSCP.dll\") | Out-Null\n\nfunction Sync-WinScp {\n param(\n [string] $Protocol,\n [string] $HostName,\n [string] $SshHostKeyFingerprint,\n [string] $UserName,\n [string] $Password,\n [string] $RemoteDirectory,\n [string] $RemoteFilename,\n [string] $LocalDirectory\n )\n \n if ($Protocol -eq 'Sftp') {\n try\n { \n # Setup session options\n $sessionOptions = New-Object WinSCP.SessionOptions\n $sessionOptions.Protocol = [WinSCP.Protocol]::Sftp\n $sessionOptions.HostName = $HostName\n $sessionOptions.SshHostKeyFingerprint = $SshHostKeyFingerprint\n $sessionOptions.UserName = $UserName\n $sessionOptions.Password = $Password\n \n $session = New-Object WinSCP.Session\n $syncResult = $null\n\n try\n {\n # Connect\n $session.Open($sessionOptions)\n \n $fileName = $RemoteFilename\n $remotePath = $RemoteDirectory\n $localPath = $LocalDirectory\n \n $transferOptions = New-Object WinSCP.TransferOptions\n $transferOptions.FileMask = $fileName\n $syncResult = $session.SynchronizeDirectories([WinSCP.SynchronizationMode]::Local, $LocalDirectory, $RemoteDirectory, $true, $true, [WinSCP.SynchronizationCriteria]::Either, $transferOptions)\n }\n finally\n {\n # Disconnect, clean up\n $session.Dispose()\n }\n\n return $syncResult\n }\n catch [Exception]\n {\n Write-Host $_.Exception.Message\n }\n } else {\n Write-Host \"Protocol $Protocol unsupported\"\n }\n}<\/pre>\n

The bits necessary for SFTP support.<\/p>\n

 <\/p>\n

\/DeviceScripts\/*<\/h3>\n

This directory contains example device scripts of commands to issue for each device type. \u00a0The CSV format is used to issue a command, expected response in the case of unusual console output.<\/p>\n","protected":false},"excerpt":{"rendered":"

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. […]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[40,50,1],"tags":[],"_links":{"self":[{"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/posts\/2691"}],"collection":[{"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/comments?post=2691"}],"version-history":[{"count":0,"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/posts\/2691\/revisions"}],"wp:attachment":[{"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/media?parent=2691"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/categories?post=2691"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/avtempwp.azurewebsites.net\/wp-json\/wp\/v2\/tags?post=2691"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}