so, before our cloud team migrates things to GCP we have to flip our nodes to Dynamic IP polling, update DNS, and mute alerts; then after the migration we have to remove the existing (legacy) interfaces, discover and add new ones, and make sure alert suppression is removed.
in the website, this can take something like 5 minutes per node; we have > 8,000 servers.
enter PowerShell, stage left. the cloud team provides us with a csv of server names and verified DNS entries, and the magic of the API does everything else.
The Pre-Migration Script:
#region Top of Script #requires -version 2 <# .SYNOPSIS Transitions nodes from standard to Dynamic IP Polling and Schedule Alert Suppression .DESCRIPTION https://github.com/solarwinds/OrionSDK/wiki/PowerShell .NOTES Version: 1.0 Author: Zack Mutchler Creation Date: August 3, 2018 Purpose/Change: Initial Script development. #> #endregion #####-----------------------------------------------------------------------------------------##### #region Pre-Work # Start the transcript $transcript = Read-Host "Where do you want to save the logfile?" Start-Transcript -Path $transcript -Force -Append # Start the timer $stopWatch = [ System.Diagnostics.Stopwatch ]::StartNew() #endregion Pre-Work #####-----------------------------------------------------------------------------------------##### #region Functions # Create a function to connect to the SolarWinds Information Service (SWIS) Function Set-SwisConnection { Param( [Parameter( Mandatory = $true, HelpMessage = "What SolarWinds server are you connecting to (Hostname or IP)?" ) ] [string ] $SolarWindsServer, [Parameter( Mandatory = $true, HelpMessage = "Do you want to use the credentials from PowerShell (Trusted), or a new login (Explicit)?" ) ] [ ValidateSet( 'Trusted', 'Explicit' ) ] [string ] $ConnectionType ) # Import the SolarWinds PowerShell Module Import-Module SwisPowerShell # Connect to SWIS IF ( $ConnectionType -eq 'Trusted' ) { $swis = Connect-Swis -Trusted -Hostname $SolarWindsServer } ELSE { $creds = Get-Credential -Message "Please provide a Domain or Local Login for SolarWinds" $swis = Connect-Swis -Credential $creds -Hostname $SolarWindsServer } RETURN $swis } #endregion Functions #####-----------------------------------------------------------------------------------------##### #region Variables # Set the target for SWIS $hostname = Read-Host "What is the hostname of the SolarWinds server you want to connect to?" # Create a SwisConnection $swis = Set-SwisConnection -SolarWindsServer $hostname -ConnectionType Trusted # Grab the source data $sourceCSV = Read-Host 'Where is the source CSV file?' $nodes = Import-Csv -Path $sourceCSV # Set the Alert Suppression start time $suppressTime = Read-Host "What is the DateTime to start alert suppression? MM/DD/YY HH:MM" $suppressTime = $suppressTime -as [ DateTime ] If( !( $suppressTime ) ) { Throw "You entered an invalid date. Please start over and use the MM/DD/YY HH:MM format." } #endregion Variables #####-----------------------------------------------------------------------------------------##### #region Execution # Iterate through the source data and match to existing SW Nodes and update/suppress Foreach( $n in $nodes ) { $server = $n.server $dns = $n.dns $query = "SELECT uri FROM Orion.Nodes WHERE Caption = @s" $uri = Get-SwisData -SwisConnection $swis -Query $query -Parameters @{ s = $server } If( !( $uri ) ) { Write-Host "Server Not Found: $( $server )" -ForegroundColor Yellow } Else { Write-Host "$( $server )`n`tDNS: $( $dns )`n`tURI: $( $uri )" Try { Set-SwisObject -SwisConnection $swis -Uri $uri -Properties @{ DNS = $dns; DynamicIP = $true } | Out-Null Invoke-SwisVerb -SwisConnection $swis -EntityName Orion.AlertSuppression -Verb SuppressAlerts -Arguments @( $uri, $suppressTime ) | Out-Null Write-Host "$( $server ) has been updated to DynamicIP polling and alerts will be muted at $( $suppressTime )" -ForegroundColor Green } Catch { $errorMessage = $_.Exception.Message Write-Host "$( $errorMessage )" -ForegroundColor Red } } } # Stop the clock and show us how long this took $stopWatch.Stop() Write-Host "SCRIPT DURATION:`n HOURS: $( $stopWatch.Elapsed.Hours )`n MINUTES: $( $stopWatch.Elapsed.Minutes )`n SECONDS: $( $stopWatch.Elapsed.Seconds )" #Stop the transcript recording Stop-Transcript #endregion Execution
The Post-Migration Script:
#region Top of Script #requires -version 2 <# .SYNOPSIS Removes old interfaces, adds new, and removes alert suppression .DESCRIPTION https://github.com/solarwinds/OrionSDK/wiki/PowerShell .NOTES Version: 1.0 Author: Zack Mutchler Creation Date: August 3, 2018 Purpose/Change: Initial Script development. #> #endregion #####-----------------------------------------------------------------------------------------##### #region Pre-Work # Start the transcript $transcript = Read-Host "Where do you want to save the logfile?" Start-Transcript -Path $transcript -Force -Append # Start the timer $stopWatch = [ System.Diagnostics.Stopwatch ]::StartNew() #endregion Pre-Work #####-----------------------------------------------------------------------------------------##### #region Functions # Create a function to connect to the SolarWinds Information Service (SWIS) Function Set-SwisConnection { Param( [Parameter( Mandatory = $true, HelpMessage = "What SolarWinds server are you connecting to (Hostname or IP)?" ) ] [string ] $SolarWindsServer, [Parameter( Mandatory = $true, HelpMessage = "Do you want to use the credentials from PowerShell (Trusted), or a new login (Explicit)?" ) ] [ ValidateSet( 'Trusted', 'Explicit' ) ] [string ] $ConnectionType ) # Import the SolarWinds PowerShell Module Import-Module SwisPowerShell # Connect to SWIS IF ( $ConnectionType -eq 'Trusted' ) { $swis = Connect-Swis -Trusted -Hostname $SolarWindsServer } ELSE { $creds = Get-Credential -Message "Please provide a Domain or Local Login for SolarWinds" $swis = Connect-Swis -Credential $creds -Hostname $SolarWindsServer } RETURN $swis } #endregion Functions #####-----------------------------------------------------------------------------------------##### #region Variables # Set the target for SWIS $hostname = Read-Host "What is the hostname of the SolarWinds server you want to connect to?" # Create a SwisConnection $swis = Set-SwisConnection -SolarWindsServer $hostname -ConnectionType Trusted # Grab the source data $sourceCSV = Read-Host 'Where is the source CSV file?' $nodes = Import-Csv -Path $sourceCSV # Create a string to use in our nodes query $captions = "'$( $nodes.server -join "','" )'" #endregion Variables #####-----------------------------------------------------------------------------------------##### #region Execution # Create the query to evaluate nodes on $nodesQuery = "SELECT NodeID, Caption, ObjectSubType, Uri FROM Orion.Nodes WHERE Caption IN ( $( $captions ) )" $nodesResults = Get-SwisData -SwisConnection $swis -Query $nodesQuery # List the servers that were not found in SolarWinds $missing = Compare-Object -ReferenceObject $nodes.server -DifferenceObject $nodesResults.Caption Foreach($m in $missing.InputObject ) { Write-Host "$m was not found in SolarWinds" -ForegroundColor Red } # Evaluate the ones found, delete current interfaces, and remove alert suppression Foreach( $n in $nodesResults ) { $nodeID = $n.NodeID $nodeName = $n.Caption $pollingMethod = $n.ObjectSubType $nodeURI = $n.Uri # Remove alert suppression on all nodes Invoke-SwisVerb -SwisConnection $swis -EntityName Orion.AlertSuppression -Verb ResumeAlerts -Arguments @( , [ string [] ] $nodeURI ) -ErrorAction SilentlyContinue | Out-Null Write-Host "Alert Suppression on $( $nodeName ) has been removed" -ForegroundColor Green If( $pollingMethod -ne 'SNMP' ) { Write-Host "$( $nodeName ) uses $( $pollingMethod ) - Manually List Resources in Web Console" -ForegroundColor Magenta } Else { # Query for Interface Details $intQuery = "SELECT Caption, Uri FROM Orion.NPM.Interfaces WHERE NodeID = $( $nodeID )" $intResults = Get-SwisData -SwisConnection $swis -Query $intQuery # Remove the Interfaces Foreach( $i in $intResults ) { $interfaceName = $i.Caption $interfaceURI = $i.Uri Remove-SwisObject -SwisConnection $swis -Uri $interfaceURI -ErrorAction SilentlyContinue | Out-Null Write-Host "Removed $( $interfaceName ) from $( $nodeName )" -ForegroundColor Yellow } # Discover interfaces on the nodes $discInt = Invoke-SwisVerb $swis -EntityName Orion.NPM.Interfaces -Verb DiscoverInterfacesOnNode -Arguments $nodeID -ErrorAction SilentlyContinue # Check if discovery worked If( $discInt.Result -ne "Succeed" ) { Write-Host "Interface Discovery Failed on $( $nodeName ) - $( $discInt.Result )" -ForegroundColor Magenta } Else { $discInt.DiscoveredInterfaces.DiscoveredLiteInterface | ?{ ( $_.Caption.InnerText -like '*Filter*' -or $_.Caption.InnerText -like '*Scheduler*' -or $_.Caption.InnerText -like '*Loopback*' -or $_.Caption.InnerText -like '*Debug*' -or $_.ifOperStatus -ne 4 ) } | %{ $discInt.DiscoveredInterfaces.RemoveChild($_) } | Out-Null # Add the remaining interfaces Invoke-SwisVerb $swis -EntityName Orion.NPM.Interfaces -Verb AddInterfacesOnNode -Arguments @( $nodeID, $discInt.DiscoveredInterfaces, "AddDefaultPollers" ) | Out-Null Write-Host "Interface(s) added on $( $nodeName ): $( $discInt.DiscoveredInterfaces.InnerText )" -ForegroundColor Green } } } #endregion Execution #####-----------------------------------------------------------------------------------------##### #region Cleanup # Stop the clock and show us how long this took $stopWatch.Stop() Write-Host "SCRIPT DURATION:`n HOURS: $( $stopWatch.Elapsed.Hours )`n MINUTES: $( $stopWatch.Elapsed.Minutes )`n SECONDS: $( $stopWatch.Elapsed.Seconds )" #Stop the transcript recording Stop-Transcript #endregion Cleanup
Note that the Interface Discovery only works for SNMP nodes right now. Luckily that's the majority of ours. For WMI and Agent nodes, we will run Sonar Discovery jobs in the website and add the new interfaces.
These scripts work for us in our environment for the tasks we have, but are being shared to ignite new ideas for what you could do in your own worlds. Enjoy.