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.