This discussion has been locked. The information referenced herein may be inaccurate due to age, software updates, or external references.
You can no longer post new replies to this discussion. If you have a similar question you can start a new discussion in this forum.

TC18: Creating and Updating Orion Custom Properties with PowerShell

1803_thwack_thwackcamp_session-screens_promoplaceholder.png

This script was featured during the There's an API for That: Introduction to the SolarWinds Orion SDK at THWACKcamp 2018

OVERVIEW / REFERENCES:

This is a small script I used as a demo during a wonderful experience that I was able to share with 2 of my favorite people ( KMSigma​ and adatole​ ). While I am not the most gifted scripter (reminder: Scripting != Coding), I was blessed to be invited to join my fellow MVPs in a session devoted to evangelizing the majesty of all things scripting.

While we obviously didn't have enough time to teach our session to granular detail, we did attempt to provide a decent amount of information in-session and all of the scripts are available here in the Orion SDK​ forum. Here are some good resources for the topics covered by my personal script:

  • GitHub: Orion SDK - THE place to start all of your API adventures!
  • Managing Custom Properties - The SDK Wiki entry that directly applies to the information in this post
  • Orion SDK​ (you've obviously already found this, but who can sleep at night without thanking THWACK?)
  • about_Functions - PowerShell documentation. It's a manual sure, but you'd be remiss to pass this one up. I constantly find myself going back there and learning a new thing (or 10)
  • Hey, Scripting Guy! blog - This blog is a goldmine of information for anyone new to PowerShell. While not always the most detailed, I can usually use the knowledge here as a starting point to get into the "Google-Rabbit Hole" of blogs and how-to's that surround the inter-webs.
  • Learn Windows PowerShell in a Month of Lunches - This seminal book has been a foundation of new beginnings into PowerShell, but some of us are a bit more visual learners. Luckily, Don Jones has us covered in video form as well!

THE PROBLEM:

Manually adding all of your custom properties back in after a net-new installation, manipulating thousands of records via automation, or even simply expanding your comfort level into scripting. All are valid reasons to get "hooked" (yeah, I went there) into the SolarWinds Orion API. In this session, I presented a small script that outlines some foundational concepts of API usage via PowerShell, as well as provides what I hope will become a real-world use-case for you!

More specifically, I am presenting a script that uses PowerShell functions to do the following:

  • Connect to the SolarWinds Information Services (SWIS)
  • Create a new Custom Property
  • Update the value of a Custom Property for an object

CHOICE OF LANGUAGE:

While the very essence of a REST API is surrounded by the beauty of being language-agnostic, at some point everyone has to choose a language. (at least per script) For ease of presentation, I decided to use PowerShell as it's Windows-native (just like the Orion platform), and has a LARGE amount of support for both the Orion Module, and modules that allow you to easily interact with a lot of other systems. (SQL Databases, Microsoft Excel, Amazon Web Services, Azure, etc.) The idea being that you may use posts like these to self-start into a journey of further discovery, and starting in a more complex language that has less built-in support and may require compilation can be daunting for your first jump into the pool. Enter PowerShell: No Muss, No Fuss.

AUTOMATE ALL THE THINGS:

I've been known to say time and again that the absolute best admins I have ever worked with in IT are L-A-Z-Y. IM(not so)HO, it is a waste of time/energy/resources to do anything manually that you can do with a script. There's always exceptions to the rule of course, but I cannot stress how much more effective my team is due to automation scripts. We have a team of 3 engineers handling the work of (from jbiggley​'s estimates) 15. That's in no small part directly attributed to our ability to utilize almost any platform's API to handle the mundane "Click Here, Enter Text, Next, Next, Submit" tasks that we all face every day in IT.

I could spend a lifetime posting articles and reasons to adopt scripting into your world, but I instead will challenge you to find one thing. Find one single task that you repeat more than 3 times per week. No matter what it is, try and create a script for it. Even if the end result is that you save yourself 5 minutes per task; that's 15 minutes per week; which is 13 hours annually that you can use to script other things, which then opens up more time to script even more things... Ever heard of compound interest? Let's apply that to life. emoticons_wink.png

SCRIPT SUMMARY:

OK, so what we're doing with this script is basically outlined as such:

  • Create 3 functions for each of our tasks
  • Execute those functions

Sounds simple? Well, it somewhat is, but when I first started it seemed like I was trying to read and write sanskrit, and not even in one of the common dialects! Trust me that with time (and Google) it gets easier. Plus, you a member of the BEST ONLINE COMMUNITY OF IT PROFESSIONALS IN THE WORLD!!! If you can't find the answer on THWACK, it simply doesn't exist.

You may end up asking yourself, "But Zack, why are you making functions to do something so simple? I mean you've even thrown up the Wiki entry that shows precisely how to do all this, no functions necessary!" Well, the answer is twofold. First, I wanted to show you a small sample of how functions can create a great deal of manageability in a script as you can reduce the amount of times you need to enter the same syntax. Second, functions are, in their own way, a process of automating your script writing. #Scriptception

To be fair, I should REALLY find the time to take the 50-something Orion SDK functions I have built in my personal stores and roll them into something pretty like the PowerOrion module. That takes you even deeper into automation-vana by allowing you to call all of your "functions" (cmdlets in a module) with a single line at the top of your script. It's the golden snitch of the PowerShell scripter's world.

THE BACON BITS:

The most important parts of this script are within the functions themselves. You can find the full text of the script below, full of comments and tidbits of help.

  • Set-SwisConnection (Lines 30 and 269)
  • New-CustomProperty (Lines 62 and 272)
    • This creates a new custom property for either NPM or SAM. This could easily be expanded to any other module, I was sticking with these for simplicity.
  • Set-CustomPropertyValue (Lines 119 and 274)
    • This will update/set the value of an existing custom property for a target object (Node, Application Monitor, etc.)

THE SCRIPT:

#region Top of Script

#requires -version 2
<#
.SYNOPSIS
    Examples of Functions to Create and Update Custom Properties via Orion SDK

.DESCRIPTION
    github.com/.../Managing-Custom-Properties

.NOTES
    Version:        1.0
    Author:         Zack Mutchler
    Creation Date:  March 21, 2018
    Purpose/Change: Initial Script development.

    Version:        1.1
    Author:         Zack Mutchler
    Creation Date:  May 28, 2018
    Purpose/Change: Added Set-CustomPropertyValue Function
#>

#endregion

#####-----------------------------------------------------------------------------------------#####

#region Functions

# Create a function to connect to the SolarWinds Information Service (SWIS)
Function Set-SwisConnection {
   
    [ cmdletbinding() ]

    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

}

# Create a function to create a new Custom Property
Function New-CustomProperty {
   
    [ cmdletbinding() ]

    Param(
        [ Parameter( Mandatory = $true, HelpMessage = "Value from the Set-SwisConnection Function" ) ] [ object ] $SwisConnection,
        [ Parameter( Mandatory = $true, HelpMessage = "What Object Type is this custom property for?" ) ] [ ValidateSet( 'Alerts', 'Applications', 'Groups', 'Interfaces', 'Nodes', 'Reports', 'Volumes' ) ] [ string ] $PropertyType,
        [ Parameter( Mandatory = $true, HelpMessage = "What name would you like to give this custom property?" ) ] [ string[] ] $PropertyName,
        [ Parameter( Mandatory = $true, HelpMessage = "What description would you like to give this custom property?" ) ] [ string ] $Description,
        [ Parameter( Mandatory = $true, HelpMessage = "What data type should be used?" ) ] [ ValidateSet( 'string', 'integer', 'datetime', 'single', 'double', 'boolean' ) ] [ string ] $ValueType,
        [ Parameter( Mandatory = $false, HelpMessage = "OPTIONAL: What is the maximum size, if using a 'string' data type? (Max 4000)" ) ] [ ValidateRange( 0,4000 ) ] [ int ] $Size,
        [ Parameter( Mandatory = $false, HelpMessage = "OPTIONAL: What values would you like to set for the drop down?" ) ] [ string[] ] $DropDown
    )

# Example: New-CustomProperty -SwisConnection $swis -PropertyType "string" -PropertyName "array" -Description "string" -ValueType "string" -Size "optional integer" -DropDown "optional array" -Mandatory <adding this argument = $true> -Default "optional string"
   
    # Set the Custom Property Usages to all available options so you're not limited
    # IsForEntityDetail is required to utilize new popover options in Orion Platform 2018.2 (thwack.solarwinds.com/.../DOC-194255)
    $Usages = @{ 'IsForAlerting' = $true; 'IsForAssetInventory' = $true; 'IsForEntityDetail' = $true; 'IsForFiltering' = $true; 'IsForGrouping' = $true; 'IsForReporting' = $true }

    # Set the Entity Object to be used to create your Custom Property
    # Note these options are going to be based on the SolarWinds modules you have installed
    # The below options reflect NPM and SAM
        If( $PropertyType -eq "Alerts" ) {
            $Entity = "Orion.AlertConfigurationsCustomProperties"
        }
        If( $PropertyType -eq "Applications" ) {
            $Entity = "Orion.APM.ApplicationCustomProperties"
        }
        If( $PropertyType -eq "Groups" ) {
            $Entity = "Orion.GroupCustomProperties"
        }
        If( $PropertyType -eq "Interfaces" ) {
            $Entity = "Orion.NPM.InterfacesCustomProperties"
        }
        If( $PropertyType -eq "Nodes" ) {
            $Entity = "Orion.NodesCustomProperties"
        }
        If( $PropertyType -eq "Reports" ) {
            $Entity = "Orion.ReportsCustomProperties"
        }
        If( $PropertyType -eq "Volumes" ) {
            $Entity = "Orion.VolumesCustomProperties"
        }

    # Create the new custom properties
    Foreach( $n in $PropertyName ) {

        Invoke-SwisVerb $SwisConnection $Entity CreateCustomPropertyWithValues @( $n, $Description, $ValueType, $Size, $null, $null, $null, $null, $null, $null, $DropDown, $Usages )

        Write-Host "Creating Custom Property: $( $n ) for Entity Type: $( $Entity )" -ForegroundColor Yellow
    }
}

# Create a function to modify/set object values on existing Custom Property
# github.com/.../CRUD.SettingCustomProperty.ps1
# This could be enhanced in several ways, most notably by using Dynamic Parameters for the ID and Value entries based on the $NodeCP parameter
Function Set-CustomPropertyValue {

    [ cmdletbinding() ]

    Param (
        [ Parameter( Mandatory = $true, HelpMessage = "Value from the Set-SwisConnection Function" ) ] [ object ] $SwisConnection,
        [ Parameter( Mandatory = $true, HelpMessage = "Primary Polling Engine Name" ) ] [ string ] $PrimaryPoller,
        [ Parameter( Mandatory = $true, HelpMessage = "Custom Property Name" ) ] [ string ] $CustomProperty,
        [ Parameter( Mandatory = $true, HelpMessage = "Is this a NODE Custom Property?" ) ] [boolean] $NodeCP,
        [ Parameter( HelpMessage = "NodeID for Custom Property assignment, REQUIRED for Nodes/Applications/Interfaces/Volumes" ) ] [ int ] $NodeID,
        [ Parameter( ParameterSetName = 'AlertID' ) ] [ int ] $AlertID,
        [ Parameter( ParameterSetName = 'ReportID' ) ] [ int ] $ReportID,
        [ Parameter( ParameterSetName = 'ApplicationID' ) ] [ int ] $ApplicationID,
        [ Parameter( ParameterSetName = 'InterfaceID' ) ] [ int ] $InterfaceID,
        [ Parameter( ParameterSetName = 'VolumeID' ) ] [ int ] $VolumeID,
        [ Parameter( ParameterSetName = 'StringValue' ) ] [ string ] $StringValue,
        [ Parameter( ParameterSetName = 'IntegerValue' ) ] [ int ] $IntegerValue,
        [ Parameter( ParameterSetName = 'DateTimeValue' ) ] [ datetime ] $DateTimeValue,
        [ Parameter( ParameterSetName = 'SingleValue' ) ] [ single ] $SingleValue, #Float with 7 digits of precision
        [ Parameter( ParameterSetName = 'DoubleValue' ) ] [ double ] $DoubleValue, #Float with 15-16 digits of precision
        [ Parameter( ParameterSetName = 'BooleanValue' ) ] [ boolean ] $BooleanValue # $true|$false
    )

<# Examples:
    Node CP: Set-CustomPropertyValue -SwisConnection $swis -PrimaryPoller <> -CustomProperty <> -NodeCP $true -NodeID <> [ $StringValue|$IntegerValue|$DateTimeValue|$SingleValue|$DoubleValue|$BooleanValue ]
    Alert CP: Set-CustomPropertyValue -SwisConnection $swis -PrimaryPoller <> -CustomProperty <> -NodeCP $false -AlertID <> [ $StringValue|$IntegerValue|$DateTimeValue|$SingleValue|$DoubleValue|$BooleanValue ]
    Report CP: Set-CustomPropertyValue -SwisConnection $swis -PrimaryPoller <> -CustomProperty <> -NodeCP $false -ReportID <> [ $StringValue|$IntegerValue|$DateTimeValue|$SingleValue|$DoubleValue|$BooleanValue ]
    Application CP: Set-CustomPropertyValue -SwisConnection $swis -PrimaryPoller <> -CustomProperty <> -NodeCP $false -NodeID <> -ApplicationID <> [ $StringValue|$IntegerValue|$DateTimeValue|$SingleValue|$DoubleValue|$BooleanValue ]
    Interface CP: Set-CustomPropertyValue -SwisConnection $swis -PrimaryPoller <> -CustomProperty <> -NodeCP $false -NodeID <> -InterfaceID <> [ $StringValue|$IntegerValue|$DateTimeValue|$SingleValue|$DoubleValue|$BooleanValue ]
    Volume CP: Set-CustomPropertyValue -SwisConnection $swis -PrimaryPoller <> -CustomProperty <> -NodeCP $false -NodeID <> -VolumeID <> [ $StringValue|$IntegerValue|$DateTimeValue|$SingleValue|$DoubleValue|$BooleanValue ]
#>

# Set your object URI
If ( $NodeCP -eq $true ) {
    $uri = 'swis://' + $PrimaryPoller + '/Orion/Orion.Nodes/NodeID=' + $NodeID + '/CustomProperties'
    Write-Host "Setting URI from Node Custom Property" -ForegroundColor Yellow
}
If ( $NodeCP -eq $false -and $AlertID ) {
    $uri = 'swis://' + $PrimaryPoller + 'Orion/Orion.AlertConfigurations/AlertID=' + $AlertID + '/CustomProperties'
    Write-Host "Setting URI from Alert Custom Property" -ForegroundColor Yellow
}
If ( $NodeCP -eq $false -and $ReportID ) {
    $uri = 'swis://' + $PrimaryPoller + 'Orion/Orion.Report/ReportID=' + $ReportID + '/CustomProperties'
    Write-Host "Setting URI from Report Custom Property" -ForegroundColor Yellow
}
If ( $NodeCP -eq $false -and $ApplicationID -and $NodeID ) {
    $uri = 'swis://' + $PrimaryPoller + '/Orion/Orion.Nodes/NodeID=' + $NodeID + '/Applications/ApplicationID=' + $ApplicationID + '/CustomProperties'
    Write-Host "Setting URI from Application Custom Property" -ForegroundColor Yellow
}
If ( $NodeCP -eq $false -and $InterfaceID -and $NodeID ) {
    $uri = 'swis://' + $PrimaryPoller + '/Orion/Orion.Nodes/NodeID=' + $NodeID + '/Interfaces/InterfaceID=' + $InterfaceID + '/CustomProperties'
    Write-Host "Setting URI from Interface Custom Property" -ForegroundColor Yellow
}
If ( $NodeCP -eq $false -and $VolumeID -and $NodeID ) {
    $uri = 'swis://' + $PrimaryPoller + '/Orion/Orion.Nodes/NodeID=' + $NodeID + '/Volumes/VolumeID=' + $VolumeID + '/CustomProperties'
    Write-Host "Setting URI from Volume Custom Property" -ForegroundColor Yellow
}
If ( $NodeCP -eq $false -and (!( $NodeID ) ) -and ( $ApplicationID -or $InterfaceID -or $VolumeID ) ) {
    Write-Warning "Missing NodeID Parameter for Application, Interface, or Volume Custom Property"
}
If ( $NodeCP -eq $true -and (!( $NodeID ) ) ) {
    Write-Warning "Missing NodeID Parameter for Node Custom Property"
}

# Set the Custom Property Value
If ( $StringValue ) {
    $cp = @{
        $CustomProperty=$StringValue
    }
    Write-Host "String Value = $( $cp.Values )" -ForegroundColor Yellow
}
If ( $IntegerValue ) {
    $cp = @{
        $CustomProperty=$IntegerValue
    }
    Write-Host "Integer Value = $( $cp.Values )" -ForegroundColor Yellow
}
If ( $DateTimeValue ) {
    $cp = @{
        $CustomProperty=$DateTimeValue
    }
    Write-Host "DateTime Value = $( $cp.Values )" -ForegroundColor Yellow
}
If ( $SingleValue ) {
    $cp = @{
        $CustomProperty=$SingleValue
    }
    Write-Host "Single Value = $( $cp.Values )" -ForegroundColor Yellow
}
If ( $DoubleValue ) {
    $cp = @{
        $CustomProperty=$DoubleValue
    }
    Write-Host "Double Value = $( $cp.Values )" -ForegroundColor Yellow
}
If ( $BooleanValue ) {
    $cp = @{
        $CustomProperty=$BooleanValue
    }
    Write-Host "Boolean Value = $( $cp.Values )" -ForegroundColor Yellow
}

# Set the Custom Property Value
Write-Host "Setting Custom Property '$( $CustomProperty )':`n    $( $uri )`n    $( $cp.Keys )`n    $( $cp.Values )" -ForegroundColor Yellow

Set-SwisObject -SwisConnection $SwisConnection -Uri $uri -Properties $cp

}

#endregion Functions


#####-----------------------------------------------------------------------------------------#####

#region Variables

# Set the SolarWinds server to connect to
$hostname = Read-Host -Prompt "Which SolarWinds server would you like to connect to?"

# Create a Name for the Custom Properties you want to create
$nodeCP = 'dark_theme_is_life'

# CP Description
$description = 'Should this object be presented in a glorious #DarkTheme?'

# CP Value Type
$valueType = 'string'

# CP String Size (ignored for non-string Value Types)
$size = 4000

# CP Drop Down Values
$dropDown = New-Object string[] 4
    $dropDown[0] = 'Go Sign Up for a UX session!'
    $dropDown[1] = 'thwack.ux-group.sgizmo.com/.../'
    $dropDown[2] = 'Dark Theme All The Things'
    $dropDown[3] = 'Kevin made me use 4 options...'

# Target NodeID
$nodeID = 10

$nodeCPString = 'Dark Theme All The Things'

#endregion Variables

#####-----------------------------------------------------------------------------------------#####

#region Execution

# Connect to SWIS
$swis = Set-SwisConnection -SolarWindsServer $hostname -ConnectionType Explicit

# Example: New-CustomProperty
New-CustomProperty -SwisConnection $swis -PropertyType Nodes -PropertyName $nodeCP -Description $description -ValueType string -Size $size -DropDown $dropDown

#Example: Set-CustomPropertyValue
Set-CustomPropertyValue -SwisConnection $swis -PrimaryPoller $hostname -CustomProperty $nodeCP -NodeCP $true -NodeID $nodeID -StringValue $nodeCPString

#endregion Execution

Enjoy, and have fun challenging yourselves! If you have any questions about this, or any other scripts, please feel free to ask! Happy Thwacking!

ZackM

#DarkThemeIsLife

The SolarWinds trademarks, service marks, and logos are the exclusive property of SolarWinds Worldwide, LLC or its affiliates.  All other trademarks are the property of their respective owners.