Populate data from external source

Hi,

I am in the process of extending scom computer objects with data from an external portal. This portal has inventory, CMDB, finacial data etc and is developed in house.

Typically, all windows computer object should have a property saying if the server has backup responsibility, the customer name etc. I can access all this data from a webAPI with powershell Invoke-RestMethod. URL looks something like this

http://cmdb/api/server?ID=netBiosName

How would you add this in to scom. Should i create a script stick it in an orchestrator runbook and add the data directly, or could i run it as a timedPowershellDiscovery on every agent?


 

Can any of you think of a reason why my script work out of the management pack but not when running in the workflow? I have some logging done so i can see the API status code in Opsmgr event log, but the entry is just blank. If i run the script directly in PowerShell. everything Works.

This is how my XML looks now.

<?xml version="1.0" encoding="utf-8"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="C:\Users\aa98\AppData\Local\Temp\tmp38DA.tmp">
  <IntervalSeconds>600</IntervalSeconds>
  <SyncTime>22:59</SyncTime>
  <ScriptName>SCOM-CMDB-Discovery.ps1</ScriptName>
  <ScriptBody>
    param([string]$sourceId, [string]$managedEntityId, $apiURL, [string]$FQDN)
    #Connect to SCOM locally,
    Import-Module OperationsManager
    New-SCOMManagementGroupConnection -ComputerName 'localhost'

    $apiuser = "domain\user"
    $apipassword = ConvertTo-SecureString "password" -AsPlainText -Force
    $apicred = New-Object System.Management.Automation.PSCredential ($apiuser,$apipassword)

    #Get a list of the computers that are active in SCOM
    $ComputerClass = Get-SCOMClass -Name 'Microsoft.Windows.Computer'
    $SCOMComputerList = Get-SCOMClassInstance -Class $ComputerClass | % {$_.DisplayName}

    #Just to add a statuscode to our log
    $APIStatusCode = (Invoke-WebRequest -Uri $apiURL -Credential ($apicred)).StatusCode
    #Get the Incomming data to Include into SCOM
    $CMDBComputers = Invoke-RestMethod -Uri $apiURL -Credential ($apicred)

    #Create the API Object
    $api = new-object -comObject 'MOM.ScriptAPI'
    $discoveryData = $api.CreateDiscoveryData(0, $SourceId, $ManagedEntityId)

    #Log our Start
    $api.LogScriptEvent("IntilityCMDB.ROTServer", 1001, 4, $("API Connection Status: $APIStatusCode | Begin discovery of ROT CMDB Servers from: $apiURL - # of computers {0}" -f $CMDBComputers.count))

    $ct = 0
    foreach ($c in $CMDBComputers) {

    if ($SCOMComputerList.contains($c.Name+$FQDN)) {
    #This server is both in SCOM and our CMDB, we will add it to the objects
    $ct++
    $instanceCMDB = $discoveryData.CreateClassInstance("$MPElement[Name='CMDB.ROTServer']$")
    $instanceCMDB.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $c.Name+$FQDN)
    $instanceCMDB.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", "ROT Computer")
    $instanceCMDB.AddProperty("$MPElement[Name='CMDB.ROTServer']/ROTRole$", $c.Role)
    $instanceCMDB.AddProperty("$MPElement[Name='CMDB.ROTServer']/ROTEnvironment$", $c.Environment)
    $instanceCMDB.AddProperty("$MPElement[Name='CMDB.ROTServer']/ROTBackup$", $c.BackupResponsibility)
    $instanceCMDB.AddProperty("$MPElement[Name='CMDB.ROTServer']/ROTManaged$",$c.OperatingResponsibility)
    $instanceCMDB.AddProperty("$MPElement[Name='CMDB.ROTServer']/ROTCompany$", $c.Location.Name)
    $instanceCMDB.AddProperty("$MPElement[Name='CMDB.ROTServer']/ROTvCenterHost$", $c.ESX.Name)
    $discoveryData.AddInstance($instanceCMDB)

    }
    }


    $api.LogScriptEvent("CMDB.ROTServer", 1001, 4, $("Completed discovery - Servers matching CMDB: {0}" -f $ct))


    #  Proper way to return discovery data in powershell
    $discoveryData
  </ScriptBody>
  <Parameters>
    <Parameter>
      <Name>sourceID</Name>
      <Value>$MPElement$</Value>
    </Parameter>
    <Parameter>
      <Name>managedEntityID</Name>
      <Value>$Target/Id$</Value>
    </Parameter>
    <Parameter>
      <Name>apiURL</Name>
      <Value>https://cmdb/api/server?orderBy=Name&amp;start=0&amp;top=9999</Value>
    </Parameter>
    <Parameter>
      <Name>FQDN</Name>
      <Value>domain</Value>
    </Parameter>
  </Parameters>
  <TimeoutSeconds>600</TimeoutSeconds>
</Configuration>
1 Like

Hi,

You can create a management pack to discover properties for your servers.

You can find an example for it here:
http://practice2perfect.com/tag/authoring/

To add the data to the registry is not a very good practise, because it is not good manageable.

1 Like

It does work, as plain text is displayed and running it locally on my machine Works fine. A couple of Things that i have managed to sort out.

PS v3 does not handle the amount of information returned from the api as Objects, and everything is as LONG string. Upgrading to PSv5 solved this.

Secondly, since this runs as localSystem i used PSexec to fire PowerShell and from there Internet Explorer just to be sure that worked

Using $cred = (get-credential) seems to be functioning differently from the pscredentialObject thingy.

and now i have added “start-transcript” before my script to see what’s actually going on. URLs have alos ben hard coded to the script and I have added Replace “amp;” as well

Thnak you. That is an option. He is using CSV - which i would like to avoid if possible. Query our api directly would be best :slight_smile:

you can generate an array with the invoke-restmethod in Powershell. The loop through the array with the foreach and fill the Class instance and add the properties

That is actually something we can do. I assume we would have to target the discovery on one of the management servers it self. I will try do download and see how it’s done in the example. Getting data from CSV or from any other source souldn’t be that differently.

I have updated my first question. I got a huge step forward reuising most of the discovery script from practice2perfect, but I am stuck right now trying to figure out why it “fails”

I’m interested if the first log event 1001 is empty completely? or is there some data in it?
I should change the log event to:
$api.LogScriptEvent(“IntilityCMDB.ROTServer”, 1001, 4, $(“API Connection Status: {0} Begin discovery of ROT CMDB Servers from: {1} - # of computers {2}” -f $APIStatusCode,$apiURL, $CMDBComputers.count))

This way the variable is provided by the format (-f)

hehe. i have made a huge mistake. Got the script to work. but suddenly everything was gone. Nothing was changed to the script and i noticed that display name and path for all servers was changed.
$instanceCMDB.AddProperty("$MPElement[Name=‘System!System.Entity’]/DisplayName$", “ROT Computer”)

That line in the script caused everything to be set once, and for the script to break later. Now i have to figure out how to take the PrinicpalName of all computers and add that as a value to displayname - or hopefully. Things will work it out it self. This is why you all have a test environment, right?

Everything Works fine now. So all computer Objects are now populated With extra Properties from Our CMDB which is accessed by a REST api. The Next thing i will implement is to create additional discovery that run frequently to be able to change monitoring settings in Our CMDB/Inventory frontend. IE. Disable CPU monitoring etc