Sync Configuration Manager Client and Operations Manager Agent State in Service Manager

This post is the 4th in a series focused on making common administrative tasks in System Center and Azure available via the Service Manager Self-Service Portal. The Configuration Manager and Operations Manager Connectors pull a lot of information into Service Manager but not everything necessary to manage clients, agents, and other settings. This solution allows for the synchronizing of Configuration Manager client state and Operations Manager agent state into Service Manager on a schedule and on-demand.

Series

Using the Service Manager Self-Service Portal for Common Tasks in Configuration Manager, Operations Manager, and Azure

Prerequisites

The scenarios were designed using the following

  • System Center Service Manager 2012 R2
    • Self-Service Portal configured and working
    • Active Directory Connector configured and working
    • Configuration Manager Connector configured and working
    • Orchestrator Connector configured and working
  • System Center Configuration Manager 2012 R2
    • Discovery configured and working
  • System Center Orchestrator 2012 R2
    • SC 2012 Configuration Manager Integration Pack configured and working
    • SC 2012 Service Manager Integration Pack configured and working
    • Configuration Manager Console installed on runbook servers (open the console, make sure you can connect to your site server)
    • Operations Manager Console installed on runbook servers
    • Service Manager Console installed on runbook servers
    • Runbook servers configured to allow PowerShell scripts to run

Create a service account or use the one created in the previous blog post

  1. Give the account admin rights to Service Manager
  2. Give the account admin rights to Configuration Manager
  3. Give the account admin rights to Operations Manager

Create a share to store scripts and logs or use the one created in the previous blog post

  1. Create a share that the service account you created and authenticated users will have access to on the Runbook Servers that will be used for this scenario.
  2. In the share, create a folder called "Automation" and give the service account access to it.
  3. Copy DiscoverWindowsComputerExtended.ps1 into the Automation Folder
  4. In the share, create a sub-folder called "Logs" in the Automation Folder and give the applicable administrators access to it. Orchestrator will write logs to this folder and admins can use these logs for troubleshooting.
  5. In the Logs folder, create a sub-folder called "SRLogs" and give authenticated users access to it. Users of the Service Manager Portal will use these to see the status of the Collection Sync task so they will need rights to this folder.
 param
(
  [Parameter(Mandatory=$true)]
  $CMSiteCode,
  [Parameter(Mandatory=$true)]
  $CMSiteServer,
  [Parameter(Mandatory=$true)]
  $OMManagementServer,
  [Parameter(Mandatory=$true)]
  $SMManagementServer,
  [Parameter(Mandatory=$true)]
  $VerboseLogging,
  [Parameter(Mandatory=$false)]
  $ServiceRequest
)

#Functions
function LogIt
{
  param (
  [Parameter(Mandatory=$true)]
  $message,
  [Parameter(Mandatory=$true)]
  $component,
  [Parameter(Mandatory=$true)]
  $type )

  switch ($type)
  {
    1 { $type = "Info" }
    2 { $type = "Warning" }
    3 { $type = "Error" }
    4 { $type = "Verbose" }
  }

  if (($type -eq "Verbose") -and ($Global:Verbose))
  {
    $toLog = "{0} `$$<{1}><{2} {3}><thread={4}>" -f ($type + ":" + $message), ($Global:ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
    $toLog | Out-File -Append -Encoding UTF8 -FilePath $Global:LogFile
    $Global:LogBuffer = $Global:LogBuffer + $toLog + "`r`n"
    Write-Host $message
  }
  elseif ($type -ne "Verbose")
  {
    $toLog = "{0} `$$<{1}><{2} {3}><thread={4}>" -f ($type + ":" + $message), ($Global:ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
    $toLog | Out-File -Append -Encoding UTF8 -FilePath $Global:LogFile
    $Global:LogBuffer = $Global:LogBuffer + $toLog + "`r`n"
    Write-Host $message
  }
}

function CreateServiceRequestLog
{
  param($serviceRequest, $srLogPath)

  LogIt -message ("Full Log File Path:" + $Global:LogFile) -component "Main()" -type 1
  if ($serviceRequest)
  {
    $srLog = Join-Path $srLogPath ("Logs\SRLogs\" + $serviceRequest + ".log")
    LogIt -message ("Service Request Log File Path:" + $srLog) -component "Main()" -type 1
    $Global:LogBuffer | Out-File -Append -Encoding UTF8 -FilePath $srLog
  }
}

function GetScriptDirectory
{
  $invocation = (Get-Variable MyInvocation -Scope 1).Value
  Split-Path $invocation.MyCommand.Path
}

function GetCMSiteConnection
{
  param ($siteCode, $siteServer)
  try { $CMModulePath = Join-Path -Path (Split-Path -Path "${Env:SMS_ADMIN_UI_PATH}" -ErrorAction Stop) -ChildPath "ConfigurationManager.psd1" }
  catch 
  { 
    LogIt -message ("Cannot get path to CM console, will try default path: " + $_.Exception.Message) -component "GetCMSiteConnection()" -type 4
    LogIt -message ("Trying static path: C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1") -component "GetCMSiteConnection()" -type 4
    $CMModulePath = 'C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
  }
  Import-Module $CMModulePath -ErrorAction Stop
  try { $CMProvider = Get-PSDrive -PSProvider 'CMSite' -Name $siteCode -ErrorAction Stop }
  catch
  {
    LogIt -message ("Cannot connect to CM site by Site Code, will retry: " + $siteCode + " Error: " + $_.Exception.Message) -component "GetCMSiteConnection()" -type 4
    try { $CMProvider = New-PSDrive -PSProvider 'AdminUI.PS.Provider\CMSite' -Name $siteCode -Root $siteServer -Description 'SCCM Site' -ErrorAction Stop}
    catch
    {
      LogIt -message ("Cannot connect to CM site by Server Name, exiting: " + $siteServer + " Error: " + $_.Exception.Message) -component "GetCMSiteConnection()" -type 3
      exit
    }
  }
  LogIt -message ("Connected to CM Site: " + $siteCode) -component 'GetCMSiteConnection()' -type 1 
  CD "$($CMProvider.SiteCode):\"
  return $CMProvider
}

function GetOMManagementGroupConnection
{
 param ($computerName)
  Import-Module OperationsManager
  try { $OM = New-SCManagementGroupConnection -computerName $computerName -ErrorAction Stop }
  catch
  {
    LogIt -message ("Cannot connect to OM management group: " + $computerName + " Error: " + $_.Exception.Message) -component "GetOMManagementGroupConnection()" -type 3
    exit
  }
  LogIt -message ("Connected to OM management group: " + $computerName) -component "GetOMManagementGroupConnection()" -type 1
  return $OM
}

function GetSMManagementGroupConnection
{
  param ($computerName)
  $smDir = (Get-ItemProperty 'hklm:/software/microsoft/System Center/2010/Service Manager/Setup').InstallDirectory
  try { Import-Module ($smDir + "\Powershell\System.Center.Service.Manager.psd1") -ErrorAction Stop }
  catch
  {
    LogIt -message ("Cannot import SM PowerShell module, Error: " + $_.Exception.Message) -component "GetSMManagementGroupConnection()" -type 3
  }
  try { $SM = New-SCManagementGroupConnection -computerName $computerName -ErrorAction Stop }
  catch
  {
    LogIt -message ("Cannot connect to SM management group: " + $computerName + " Error: " + $_.Exception.Message) -component "GetSMManagementGroupConnection()" -type 3
    exit
  }
  LogIt -message ("Connected to SM management group: " + $computerName) -component "GetSMManagementGroupConnection()" -type 1
  return $SM
}

function GetSCClass
{
  param($computer, $name)

  try { $scClass = Get-SCClass -ComputerName $computer -Name $name }
  catch { LogIt -message ("Cannot get SM Class: " + $name  + " Error: " + $_.Exception.Message) -component "GetSCClass()" -type 3 }

  LogIt -message ("Retrieved SM Class: " + $name) -component "GetSCClass()" -type 4
  return $scClass
}

function GetSCClassInstance
{
  param($computer, $class, $filter)

  if ($filter)
  {
    try { $scClassInstance = Get-SCClassInstance -ComputerName $computer -Class $class -Filter $filter }
    catch { LogIt -message ("Cannot get SM Class instance: " + $class + " with filter: " + $filter + " Error: " + $_.Exception.Message) -component "GetSCClassInstance()" -type 2 }
  }
  else
  {
    try { $scClassInstance = Get-SCClassInstance -ComputerName $computer -Class $class }
    catch { LogIt -message ("Cannot get SM Class instance: " + $class  + " Error: " + $_.Exception.Message) -component "GetSCClassInstance()" -type 2 }
  }
  LogIt -message ("Retrieved SM Class instance of type: " + $class.Name) -component "GetSCClassInstance()" -type 4
  return $scClassInstance
}

function GetCMClientObjects
{
  param($computer, $siteCode)

  try { $cmClients = Get-WmiObject "SMS_R_System" -ComputerName $computer -Namespace ("root\sms\site_{0}" -f $siteCode) }
  catch 
  { 
    LogIt -message ("Cannot get CM Client Objects") -component "GetCMClientObjects()" -type 3
    exit
  }

  LogIt -message ("Retrieved CM Client Objects") -component "GetCMClientObjects()" -type 4
  return $cmClients
}

function GetCMClients
{
  param($objects)
  $ht = @{}

  foreach ($object in $objects)
  {
    $ou = $null
    $i = 0
    if ($object.SystemOUName) { $aOU = $object.SystemOUName.Split(",") }
    
    if ($aOU.Count -gt 0)
    {
      $ouRaw = $object.SystemOUName[$aOU.Count - 1]
      $aOUs = $ouRaw.Split("/")
      $iLastElement = $aOUs.Count
      [array]::Reverse($aOUs)
      foreach ($s in $aOUs)
      {
        $i++
        if ($i -ne $iLastElement)
        {
          if ($ou) { $ou = $ou + "," + $s }
          else { $ou = $s }
        }
      }
    }
    if (!($object.Client)) { $client = $false } else { $client = $true }
    $PSObjectStrongName = (New-Object psobject).psobject.GetType().AssemblyQualifiedName
    $o = New-Object "System.Collections.ObjectModel.Collection``1[[$PSObjectStrongName]]"
    $o.Add((New-Object psobject -Property @{
      PrincipalName=$object.ResourceNames[0];
      Client=$client;
      SystemOUName=$ou;
      Agent=$false;
      ProxyEnabled=$false
      }))
    try { $ht.Add($object.ResourceNames[0].ToUpper(), $o) } catch {}
  }
  LogIt -message ("Retrieved CM Clients") -component "GetCMClients()" -type 4
  return $ht
}

function GetOMAgentObjects
{
  param($computer)
  try { $omAgents = Get-SCOMAgent -ComputerName $computer }
  catch 
  { 
    LogIt -message ("Cannot get OM Agent Objects") -component "GetOMAgentObjects()" -type 3
    exit
  }

  LogIt -message ("Retrieved OM Agent Objects") -component "GetOMAgentObjects()" -type 4
  return $omAgents
}

function GetOMAgents
{
  param($objects)
  $ht = @{}

  foreach ($object in $objects)
  {
    try { $ht.Add($object.PrincipalName.ToUpper(), $object.ProxyingEnabled) } catch {}
  }
  LogIt -message ("Retrieved OM Agents") -component "GetOMAgents()" -type 4
  return $ht
}

function GetSMObjects
{
  param($computer)
  $deviceDefinition = GetSCClass -computer $computer -name 'Microsoft.Windows.Computer'
  $deviceInstances = GetSCClassInstance -computer $computer -class $deviceDefinition
  LogIt -message ("Retrieved SM Objects") -component "GetSMObjects()" -type 4
  return $deviceInstances
}

function GetSMDevices
{
  param($objects)
  $ht = @{}

  foreach ($object in $objects)
  {    
    $PSObjectStrongName = (New-Object psobject).psobject.GetType().AssemblyQualifiedName
    $o = New-Object "System.Collections.ObjectModel.Collection``1[[$PSObjectStrongName]]"
    $o.Add((New-Object psobject -Property @{
      PrincipalName=$object.PrincipalName;
      Client=$object.CMClient;
      SystemOUName=$object.OrganizationalUnit;
      Agent=$object.OMAgent;
      ProxyEnabled=$object.OMProxyEnabled
      }))
    try { $ht.Add($object.PrincipalName.ToUpper(), $o) } catch {}
  }
  LogIt -message ("Retrieved SM Devices") -component "GetSMDevices()" -type 4
  return $ht
}

function GetCombinedCMOMData
{
  param($cmClients, $omAgents)

  $ht = $cmClients

  foreach ($omAgent in $omAgents.GetEnumerator())
  {
    $oCM = $ht.Get_Item($omAgent.Key)
    if ($oCM)
    {
      $PSObjectStrongName = (New-Object psobject).psobject.GetType().AssemblyQualifiedName
      $oCombined = New-Object "System.Collections.ObjectModel.Collection``1[[$PSObjectStrongName]]"
      $oCombined.Add((New-Object psobject -Property @{
        PrincipalName=$oCM.PrincipalName;
        Client=$oCM.Client;
        SystemOUName=$oCM.SystemOUName;
        Agent=$true;
        ProxyEnabled=$omAgent.Value
        }))
      try { $ht.Set_Item($omAgent.Key, $oCombined) } catch {}
    }
    else
    {
      $PSObjectStrongName = (New-Object psobject).psobject.GetType().AssemblyQualifiedName
      $oCombined = New-Object "System.Collections.ObjectModel.Collection``1[[$PSObjectStrongName]]"
      $oCombined.Add((New-Object psobject -Property @{
        PrincipalName=$omAgent.Key;
        Client=$false;
        SystemOUName=$null;
        Agent=$true;
        ProxyEnabled=$omAgent.Value
        }))
      try { $ht.Add($omAgent.Key, $oCombined) } catch {}
    }
  }
  LogIt -message ("Retrieved Combined CM/OM Data") -component "GetCombinedCMOMData()" -type 4
  return $ht
}

function IsWindowsComputerCurrent
{
  param($smRecord, $cmomRecord)
  $bCurrent = $true

  if ($cmomRecord.Value.SystemOUName -ne $smRecord.SystemOUName) { $bCurrent = $false }

  return $bCurrent
}

function IsWindowsComputerExtendedCurrent
{
  param($smRecord, $cmomRecord)
  $bCurrent = $true
  
  try 
  {
    if ($cmomRecord.Value.Agent.ToString() -ne $smRecord.Agent.ToString()) { $bCurrent = $false }
    if ($cmomRecord.Value.ProxyEnabled.ToString() -ne $smRecord.ProxyEnabled.ToString()) { $bCurrent = $false }
    if ($cmomRecord.Value.Client.ToString() -ne $smRecord.Client.ToString()) { $bCurrent = $false }
  }
  catch{ $bCurrent = $false }
  return $bCurrent
}

function UpdateWindowsComputerObject
{
  param($computer, $object)
  $classDefinition = GetSCClass -computer $computer -name 'Microsoft.Windows.Computer'
  $filter = 'PrincipalName -eq "{0}"' -f $object.Value.PrincipalName
  $class = GetSCClassInstance -computer $computer -class $classDefinition -filter $filter
  $class.OrganizationalUnit = $object.Value.SystemOUName
  try { Update-SCClassInstance -Instance $class -ErrorAction Stop }
  catch { LogIt -message ("Cannot Update Windows Computer Instance: " + $object) -component "UpdateWindowsComputerObject()" -type 3 }
  LogIt -message ("Retrieved Existing Instance for " + $object.Value.PrincipalName) -component "UpdateWindowsComputerObject()" -type 4
  LogIt -message ("Updated OU for " + $object.Value.PrincipalName + " to " + $object.Value.SystemOUName) -component "UpdateWindowsComputerObject()" -type 1
}

function UpdateWindowsComputerExtendedObject
{
  param($computer, $object)

  $classDefinition = GetSCClass -computer $computer -name 'Microsoft.Windows.Computer'
  $filter = 'PrincipalName -eq "{0}"' -f $object.Value.PrincipalName
  $class = GetSCClassInstance -computer $computer -class $classDefinition -filter $filter
  if ($class)
  {
    try { $class.OMAgent = $object.Value.Agent } catch { $class.OMAgent = $false }
    try { $class.CMClient = $object.Value.Client } catch { $class.CMClient = $false }
    try { $class.OMProxyEnabled = $object.Value.ProxyEnabled.ToString() } catch { $class.OMProxyEnabled = $false }
    try 
    { 
      $class | Update-SCClassInstance -ErrorAction Stop 
      LogIt -message ("Retrieved Existing Extended Instance for " + $object.Value.PrincipalName) -component "UpdateWindowsComputerExtendedObject()" -type 4
      LogIt -message ("Updated OMAgent for " + $object.Value.PrincipalName + " to " + $object.Value.Agent) -component "UpdateWindowsComputerExtendedObject()" -type 1
      LogIt -message ("Updated CMClient for " + $object.Value.PrincipalName + " to " + $object.Value.Client) -component "UpdateWindowsComputerExtendedObject()" -type 1
      LogIt -message ("Updated OMProxyEnabled for " + $object.Value.PrincipalName + " to " + $object.Value.ProxyEnabled) -component "UpdateWindowsComputerExtendedObject()" -type 1
    }
    catch { LogIt -message ("Cannot Update Windows Computer Instance: " + $object.Value.PrincipalName + " Error:" + $_.Exception.Message) -component "UpdateWindowsComputerExtendedObject()" -type 3 }
  }
}

function DeleteWindowsComputerExtendedObject
{
  param($computer, $object)

  $classDefinition = GetSCClass -computer $computer -name 'Microsoft.Windows.Computer'
  $filter = 'PrincipalName -eq "{0}"' -f $object.Value.PrincipalName
  $class = GetSCClassInstance -computer $computer -class $classDefinition -filter $filter
  if ($class)
  {
    if (($class.OMAgent) -or ($class.CMClient) -or ($class.OMProxyEnabled))
    {
      $class.OMAgent = $false
      $class.CMClient = $false
      $class.OMProxyEnabled = $false
      try { Update-SCClassInstance -Instance $class -ErrorAction Stop }
      catch { LogIt -message ("Cannot Update Extended Instance for " + $object.Value.PrincipalName) -component "DeleteWindowsComputerExtendedObject()" -type 3 }
      LogIt -message ("Retrieved Existing Instance for " + $object.Value.PrincipalName) -component "DeleteWindowsComputerExtendedObject()" -type 4
      LogIt -message ("Extended Instance Removed for " + $object.Value.PrincipalName) -component "DeleteWindowsComputerExtendedObject()" -type 1
    }
  }
}

function UpdateOUProperty
{
  param($computer, $object)

  $classDefinition = GetSCClass -computer $computer -name 'Microsoft.Windows.Computer'
  $filter = 'PrincipalName -eq "{0}"' -f $object.Value.PrincipalName
  $class = GetSCClassInstance -computer $computer -class $classDefinition -filter $filter
  if ($class.OrganizationalUnit -ne $null)
  {
    $class.OrganizationalUnit = $null
    try { Update-SCClassInstance -Instance $class -ErrorAction Stop }
    catch { LogIt -message ("Cannot Update Windows Computer Instance: " + $object) -component "UpdateWindowsComputerObject()" -type 3 }
    LogIt -message ("Retrieved Existing Instance for " + $object.Value.PrincipalName) -component "UpdatedOUProperty()" -type 4
    LogIt -message ("Updated OU for " + $object.Value.PrincipalName + " to NULL") -component "UpdateOUProperty()" -type 1
  }
}

function SubmitDiscoveryData
{
  param($computer, $combinedCMOMData, $combinedSMData)

  #Update/Add
  foreach ($cmomRecord in $combinedCMOMData.GetEnumerator())
  {
    $smRecord = $combinedSMData.Get_Item($cmomRecord.Key)
    if($combinedSMData.ContainsKey($cmomRecord.Key))
    {
      #See if Windows Computer Object needs updating
      if (!(IsWindowsComputerCurrent -smRecord $smRecord -cmomRecord $cmomRecord))
      {
        UpdateWindowsComputerObject -computer $computer -object $cmomRecord
      }

      #See if Windows Computer Extended Object needs updating
      if (!(IsWindowsComputerExtendedCurrent -smRecord $smRecord -cmomRecord $cmomRecord))
      {
        UpdateWindowsComputerExtendedObject -computer $computer -object $cmomRecord
      }
    }
    else { LogIt -message ("Could not find record in SM CMDB for device " + $cmomRecord.Key + ", skipping") -component "SubmitDiscoveryData()" -type 2 }
    $combinedSMData.Remove($cmomRecord.Key)
  }

  #Remove
  foreach ($smRecord in $combinedSMData.GetEnumerator())
  {
    DeleteWindowsComputerExtendedObject -computer $computer -object $smRecord
    UpdateOUProperty -computer $computer -object $smRecord
  }
}

#Main
$Version = "1.0"
[bool]$Global:Verbose = [System.Convert]::ToBoolean($VerboseLogging) 
$Global:LogFile = Join-Path (GetScriptDirectory) 'Logs\DiscoverWindowsComputerExtended.log'
$Global:ScriptName = 'DiscoverWindowsComputerExtended.ps1'
$Global:LogBuffer = ''
$Global:ScriptStatus = 'Success'
LogIt -message ("Windows Computer Extended Discovery Script v{0}" -f $Version) -type 1 -component "Main()"

#Connect to CM
$CM = GetCMSiteConnection -siteServer $CMSiteServer -siteCode $CMSiteCode

#Get CM Clients
$CMClientObjects = GetCMClientObjects -computer $CM.Root -siteCode $CM.Name
$CMClients = GetCMClients -objects $CMClientObjects

#Connect to OM
$OM = GetOMManagementGroupConnection -computerName $OMManagementServer

#Get OM Agents
$OMAgentObjects = GetOMAgentObjects -computer $OMManagementServer
$OMAgents = GetOMAgents -objects $OMAgentObjects

#Combine Data to be Discovered
$CombinedCMOMData = GetCombinedCMOMData -cmClients $CMClients -omAgents $OMAgents

#Connect to SM
$SM = GetSMManagementGroupConnection -computerName $SMManagementServer

#Get SM Devices
$SMObjects = GetSMObjects -computer $SMManagementServer
$SMDevices = GetSMDevices -objects $SMObjects

#Submit Data
SubmitDiscoveryData -computer $SMManagementServer -combinedCMOMData $CombinedCMOMData -combinedSMData $SMDevices

#Log Result
$Ret = $Global:ScriptStatus
LogIt -message ("Script Complete, Result: {0}" -f $Ret) -component "Main()" -type 1

#Create SR Log if needed
CreateServiceRequestLog -serviceRequest $ServiceRequest -srLogPath (GetScriptDirectory)

Import the Windows Computer Extended Discovery Management Pack into Service Manager

This Management Pack contains a class that extends Microsoft.Windows.Computer so that it includes properties for the Configuration Manager client, Operations Manager agent, and Operations Manager Agent Proxy.

  1. Open the Service Manager Console
  2. Select Administration
  3. Right-Click Management Packs and select Import
  4. Select the Custom.Example.DataCenter.Automation.WindowsComputerExtended.mp management pack and choose Open, Import, and OK
 <?xml version="1.0" encoding="utf-8"?>
<ManagementPack SchemaVersion="2.0" ContentReadable="true" xmlns:xsd="https://www.w3.org/2001/XMLSchema">
  <Manifest>
    <Identity>
      <ID>Custom.Example.DataCenter.Automation.WindowsComputerExtended</ID>
      <Version>1.0.0.0</Version>
    </Identity>
    <Name>DataCenter Automation: Windows Computer Extended</Name>
    <References>
      <Reference Alias="Windows">
        <ID>Microsoft.Windows.Library</ID>
        <Version>7.5.8501.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="System">
        <ID>System.Library</ID>
        <Version>7.5.8501.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
    </References>
  </Manifest>
  <TypeDefinitions>
    <EntityTypes>
      <ClassTypes>
        <ClassType ID="Custom.Example.DataCenter.Automation.WindowsComputerExtended.MicrosoftWindowsComputerExtended" Base="Windows!Microsoft.Windows.Computer" Extension="true" Accessibility="Public" Abstract="false" Hosted="false" Singleton="false">
          <Property ID="OMAgent" Key="false" Type="bool" />
          <Property ID="CMClient" Key="false" Type="bool" />
          <Property ID="OMProxyEnabled" Key="false" Type="bool" />
        </ClassType>
      </ClassTypes>
    </EntityTypes>
  </TypeDefinitions>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="Custom.Example.DataCenter.Automation.WindowsComputerExtended">
          <Name>DataCenter Automation: Windows Computer Extended</Name>
          <Description>Management Pack for extending Windows Computer in Service Manager</Description>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.DataCenter.Automation.WindowsComputerExtended.MicrosoftWindowsComputerExtended">
          <Name>Windows Computer Extended</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.DataCenter.Automation.WindowsComputerExtended.MicrosoftWindowsComputerExtended" SubElementID="OMAgent">
          <Name>Operations Manager Agent</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.DataCenter.Automation.WindowsComputerExtended.MicrosoftWindowsComputerExtended" SubElementID="CMClient">
          <Name>Configuration Manager Client</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.DataCenter.Automation.WindowsComputerExtended.MicrosoftWindowsComputerExtended" SubElementID="OMProxyEnabled">
          <Name>Operations Manager Proxy Enabled</Name>
        </DisplayString>
      </DisplayStrings>
      <KnowledgeArticles></KnowledgeArticles>
    </LanguagePack>
  </LanguagePacks>
</ManagementPack>

Create the Windows Computer Extended (On-Demand) Runbook

This Runbook will launch a PowerShell script when it is triggered via the Service Manager Self-Service Portal.

  1. Open the Orchestrator Runbook Designer

  2. Create a new runbook

  3. Drag the "Runbook Control\Initialize Data" activity into the new runbook

  4. Configure a "ServiceRequest" parameter of type "String" under the Initialize Data activity

  5. Drag the "System\Run Program" activity into the new runbook

  6. Link the two activities

  7. Configure the Security of "System\Run Program" to use the service account

  8. Configure three settings under the "Details" section of the "Run Program" activity and click finish

    • Program path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
    • Parameters: -File <local sharepath>\Automation\DiscoverWindowsComputerExtended.ps1 -CMSiteCode CAS -CMSiteServer sccm2012r2cas.contoso.com -OMManagementServer scom2012r2.contoso.com -SMManagementServer sm2012r2.contoso.com -VerboseLogging false -ServiceRequest {ServiceRequest from "Initialize Data"}
    • Working Folder: <local sharepath>\Automation
    • Note, the data between the curly braces are Published Data from the Data Bus. This is obtained by right-clicking on the white space and selecting the appropriate variable. You can also use Orchestrator variables configured under Global Settings for items such as CMSiteCode, CMSiteServer, and SMManagementServer.
  9. Check In the Runbook

  10. The Runbook should look like this:

clip_image001

Create the Windows Computer Extended (Full - Scheduled) Runbook

This Runbook will launch a PowerShell script when it is triggered via a schedule. This will catch Collections and Collection modifications that have occurred outside of the Service Manager Self-Service Portal.

  1. Open the Orchestrator Runbook Designer

  2. Create a new runbook

  3. Drag the "Scheduling\Monitor Date/Time" activity into the new runbook

  4. Configure one setting under Details of the Monitor Date/Time activity and click Finish

    • Every: 1 hour
  5. Drag the "System\Run Program" activity into the new runbook

  6. Link the two activities

  7. Configure the Security of "System\Run Program" to use the service account

  8. Configure three settings under the "Details" section of the "Run Program" activity and click finish

    • Program path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
    • Parameters: -File <local sharepath>\Automation\DiscoverWindowsComputerExtended.ps1 -CMSiteCode CAS -CMSiteServer sccm2012r2cas.contoso.com -OMManagementServer scom2012r2.contoso.com -SMManagementServer sm2012r2.contoso.com -VerboseLogging false
    • Working Folder: <sharepath>\Automation
    • You can also use Orchestrator variables configured under Global Settings for items such as CMSiteCode, CMSiteServer, and SMManagementServer.
  9. Check in and Start this runbook.

  10. The Runbook should look like this:

clip_image002

 

Summary

The on-demand runbook will be used in future posts for synchronizing Configuration Manager and Operations Manager client/agent information with Service Manager. The scheduled runbook will start working on the schedule to synchronize Configuration Manager client state, Operations Manager agent state, and Operations Manager agent proxy setting thereby picking up changes made outside of the Service Manager Portal.

 

Continue to the 5th post in this series: Using the Service Manager Self-Service Portal to Manage Configuration Manager Clients

DiscoverWindowsComputerExtended.zip