Ah automation. One of my favorite things in the world! If I had a dating profile, it would probably say that I enjoy long walks on the beach, smelling flowers in the springtime, and automating tasks using PowerShell. Then again, maybe I’d have more luck if I left out that last part…

Anyway, I’ve been doing a lot of it lately, and while SMLets and the out-of-box Service Manager commandlets have been doing what I’ve needed for years now, today I finally hit a wall.

As part of a user onboarding script I’m building, I *needed* to have the AD object that had been newly created, synced into SCSM before moving on. That user object would then be attached to the onboarding request, and that would let me do all kinds of fun things later on in the onboarding process. I was pretty proud of the overall workflow, and in my rush to get it done, I just set a sleep of about 10 minutes to wait for the AD connector to sync up, before I attempted to get the user object from the SCSM DB. I  just hoped the user was there, and since I was waiting long enough in a small AD environment, it always was.

In an effort to create a better script, I tried to make it more intelligent. I noted the last event log entry for the AD connector sync, and used Get-WinEvent to find that last event, having occurred after the start time of my script. This worked better, but still wasn’t great. My test for the event seemed to work about 4 out of every 5 times, and when no event came up, I ended up having to do some pretty interesting trapping for the possibility of Get-WinEvent erroring out. Several times, my onboarding script ended up in an endless loop of death, which was, of course, no good.

Here at Cireson, one of the most amazing things is the caliber of people you work with! I’m quite sure that everyone on the consulting team is smarter than me, and I’m pretty sure they all think I’m smarter than them. That means we’re constantly challenging each other, scrambling to write some cool new code to show off, or help our nearest coworker in goodwill, knowing that we’ll need a favor from them sometime or another down the road. I’ve yet to work for another company that collaborates like we do, and I don’t expect that it’s easily found elsewhere – just ask our customers how awesome it is to have feedback from an entire *team* when you’ve only got one guy on site 🙂

So, I sent an e-mail blast to our consulting mailing list, and one of our finest, Allen Anderson, replied back saying that I could monitor the connector status in PowerShell just fine – if I used the SDK.

Now, I have been trying to learn some C# and SDK code as of late. I’ve started to dabble in Visual Studio the past two weeks or so and I’ve found it, well, insufferable. I’m quite sure my hair has grown thinner and there’s double the number of wrinkles on my brow. So when Allen says the only way to get this is using the SDK, I cringed, shed a few tears, and begged for help. I googled for days to try and find the solution on my own, but since most of the code examples out there using the SDK are using C# as well, I was thoroughly lost. Allen was a patient teacher and tried his best to lead me to the solution rather than hand it over. For that, I am eternally thankful, even if he still had to help me out a *lot* to get to the point where this worked. Hopefully it serves as an excellent example for others wanting to use the SCSM SDK and PowerShell!

Here’s the function code I wrote that gets the status of an SCSM connector!

        function get-connector-status
        {
            param ($scsmServer,
                    $connectorDisplayName)

            $connectorStatus = $null
            #THIS FUNCTION ONLY WORKS IN PowerShell 3 or higher! If you're using this in orchestrator, you'll want to wrap it. 

            #Load SDK bits
            [Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.Core") | Out-Null
            [Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.ServiceManager.dll") | Out-Null

            #connect to scsm
            $SCSMSDK = $scsmServer
            $emg = New-Object Microsoft.EnterpriseManagement.EnterpriseManagementGroup($SCSMSDK)

            #get the ad connector
            $adConnector = $emg.ConnectorFramework.GetConnectors() | ?{$_.displayname -match "$connectorDisplayName"}

            #get the proper relationship to get the status of the connector
            $connectorStatusRelationship = $emg.EntityTypes.GetRelationshipClasses() | ?{$_.name -match 'Microsoft.SystemCenter.LinkingFramework.DataSourceHostSyncStatus'}

            #define the method we're going to use
            [Type[]]$methodType = @([Guid], [Microsoft.EnterpriseManagement.Configuration.ManagementPackRelationship], [Microsoft.EnterpriseManagement.Configuration.DerivedClassTraversalDepth], [Microsoft.EnterpriseManagement.Common.TraversalDepth], [Microsoft.EnterpriseManagement.Common.ObjectQueryOptions])

            #call the method to get the status
            $getRelatedObjectsMethod = $emg.EntityObjects.GetType().GetMethod('GetRelationshipObjectsWhereSource',$methodType)
            $relatedObjectsGenericMethod = $getRelatedObjectsMethod.MakeGenericMethod([Microsoft.EnterpriseManagement.Common.EnterpriseManagementObject])
            $relatedObjectsCollection = $relatedObjectsGenericMethod.Invoke($emg.EntityObjects, ([guid]$adConnector.ConnectorObject.Id, [Microsoft.EnterpriseManagement.Configuration.ManagementPackRelationship]$connectorStatusRelationship, [Microsoft.EnterpriseManagement.Configuration.DerivedClassTraversalDepth]::Recursive, [Microsoft.EnterpriseManagement.Common.TraversalDepth]::Recursive, [Microsoft.EnterpriseManagement.Common.ObjectQueryOptions]::Default))

            #test to make sure only one connector is returned. this function is meant to be singular only
            if (($relatedObjectsCollection | Measure-Object).count -eq 1)
            {
                $connectorStatus = ($relatedObjectsCollection[0].TargetObject[$null, "Status"].Value.DisplayName)
                #$connectorStatus = ($relatedObjectsCollection[0].TargetObject.Values | ?{$_.type -match 'Status'} | select value).value.displayname #this is a different way to do the same thing, just for reference
            }
            else
            {
                $connectorStatus = 'FUNCTION ERROR'
            }

            return $connectorStatus
        }

That should be plenty to get you started! Testing for various connector statuses is as simple as a few ‘if’ statements beyond that. If you want to see the full script that I came up with, just check out the full post with highlighted code over at this site and you can work off of that. Then again, isn’t it better to be taught how to fish? 😉

Enjoy!

-Will

Share Button