SCOM 2016 with Powershell Desired State Configuration Part 1

Hi,

the intension of this three-part series is to get a picture of what you have to change on the xSCOM DSC Module to make it aware of SCOM 2016. I will show you what and where you have to change. Please use this as a modular example of how Powershell DSC modules can be manipulated to fit your needs. In the upcoming Part 4 I will show you how to use this xSCOM Module to deploy a complete SCOM environment.

Download the Powershell Module:

First you need to download the xSCOM module from the PowershellGallery:

 Install-Module -Name xSCOM -verbose

Customize the PS DSC Modules Part 1:

Here will the development begin. You can find the modules at the following path: '$env:CommonProgramFiles\WindowsPowerShell\Modules'
We need to adjust the following DSCResources from the xScom Module:

MSFT_xSCOMConsoleSetup (Part1)
MSFT_xSCOMConsoleUpdate (Part1)
MSFT_xSCOMManagementServerSetup (described in Part 2 of this blog series) MSFT_xSCOMManagementServerUpdate (described in Part 2 of this blog series) MSFT_xSCOMWebConsoleServerSetup (described in Part 3 of this blog series) MSFT_xSCOMWebConsoleServerUpdate (described in Part 3 of this blog series)

Each Module folder contains two files. We need to edit the *.PSM (Powershell Module file). Please create a backup before. Each *.PSM file consists of three major Functions:

get-targetresource (needs to be manipulated)
set-targetresource (needs to be manipulated)
test-targetresource (no changes are required)

Please edit the MSFT_xSCOMConsoleSetup.psm1 from the MSFT_xSCOMConsoleSetup folder and go to the first section (Get-Targetresource)
Add the following condition part to the switch statement:

 "7.2.11719.0"
        {
            $IdentifyingNumber = "{E072D8FC-CD31-4ABE-BD65-606965965426}"
            $InstallRegVersion = "12"
        }

And next add the following switch condition to the switch statement in the Set-Targetresource function:

 "7.2.11719.0"
        {
            $IdentifyingNumber = "{E072D8FC-CD31-4ABE-BD65-606965965426}"
        }

After that the MSFT_xSCOMConsoleSetup.psm file is aware of SCOM 2016 Console installations and DSC can push or pull a SCOM 2016 Console.
Now it should look like:

 function Get-TargetResource
{
 [CmdletBinding()]
   [OutputType([System.Collections.Hashtable])]
    param
   (
       [parameter(Mandatory = $true)]
      [ValidateSet("Present","Absent")]
       [System.String]
     $Ensure = "Present",

        [parameter(Mandatory = $true)]
      [System.String]
     $SourcePath,

        [System.String]
     $SourceFolder = "\SystemCenter2012R2\OperationsManager.en",

     [parameter(Mandatory = $true)]
      [System.Management.Automation.PSCredential]
     $SetupCredential,

       [System.String]
     $InstallPath,

       [System.Byte]
       $UseMicrosoftUpdate,

        [System.Byte]
       $SendCEIPReports,

       [ValidateSet("Never","Queued","Always")]
        [System.String]
     $EnableErrorReporting = "Never",

        [System.Byte]
       $SendODRReports
 )

    Import-Module $PSScriptRoot\..\..\xPDT.psm1
        
    $Path = Join-Path -Path (Join-Path -Path $SourcePath -ChildPath $SourceFolder) -ChildPath "setup.exe"
    $Path = ResolvePath $Path
    $Version = (Get-Item -Path $Path).VersionInfo.ProductVersion

    switch($Version)
    {
        "7.2.11719.0"
        {
            $IdentifyingNumber = "{E072D8FC-CD31-4ABE-BD65-606965965426}"
            $InstallRegVersion = "12"
        }
        "7.1.10226.0"
        {
            $IdentifyingNumber = "{041C3416-87CE-4B02-918E-6FDC95F241D3}"
            $InstallRegVersion = "12"
        }
        "7.2.10015.0"
        {
            $IdentifyingNumber = "{F67729BD-18CF-4283-A6FC-F388A463EC01}"
            $InstallRegVersion = "12"
        }
        Default
        {
            throw "Unknown version of Operations Manager!"
        }
    }

    if(Get-WmiObject -Class Win32_Product | Where-Object {$_.IdentifyingNumber -eq $IdentifyingNumber})
    {
      $InstallPath = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\System Center Operations Manager\$InstallRegVersion\Setup" -Name "InstallDirectory").InstallDirectory

        $returnValue = @{
         Ensure = "Present"
          SourcePath = $SourcePath
            SourceFolder = $SourceFolder
            InstallPath = $InstallPath
      }
    }
    else
    {
     $returnValue = @{
           Ensure = "Absent"
           SourcePath = $SourcePath
            SourceFolder = $SourceFolder
        }
    }
    
  $returnValue
}


function Set-TargetResource
{
   [CmdletBinding()]
   param
   (
       [parameter(Mandatory = $true)]
      [ValidateSet("Present","Absent")]
       [System.String]
     $Ensure = "Present",

        [parameter(Mandatory = $true)]
      [System.String]
     $SourcePath,

        [System.String]
     $SourceFolder = "\SystemCenter2012R2\OperationsManager.en",

     [parameter(Mandatory = $true)]
      [System.Management.Automation.PSCredential]
     $SetupCredential,

       [System.String]
     $InstallPath,

       [System.Byte]
       $UseMicrosoftUpdate,

        [System.Byte]
       $SendCEIPReports,

       [ValidateSet("Never","Queued","Always")]
        [System.String]
     $EnableErrorReporting = "Never",

        [System.Byte]
       $SendODRReports
 )

    Import-Module $PSScriptRoot\..\..\xPDT.psm1
        
    $Path = Join-Path -Path (Join-Path -Path $SourcePath -ChildPath $SourceFolder) -ChildPath "setup.exe"
    $Path = ResolvePath $Path
    $Version = (Get-Item -Path $Path).VersionInfo.ProductVersion

    switch($Version)
    {
        "7.2.11719.0"
        {
            $IdentifyingNumber = "{E072D8FC-CD31-4ABE-BD65-606965965426}"
        }
        "7.1.10226.0"
        {
            $IdentifyingNumber = "{041C3416-87CE-4B02-918E-6FDC95F241D3}"
        }
        "7.2.10015.0"
        {
            $IdentifyingNumber = "{F67729BD-18CF-4283-A6FC-F388A463EC01}"
        }
        Default
        {
            throw "Unknown version of Operations Manager!"
        }
    }

    switch($Ensure)
    {
        "Present"
        {
            # Set defaults, if they couldn't be set in param due to null configdata input
            if($UseMicrosoftUpdate -ne 1)
            {
                $UseMicrosoftUpdate = 0
            }
            if($SendCEIPReports -ne 1)
            {
                $SendCEIPReports = 0
            }
            if($SendODRReports -ne 1)
            {
                $SendODRReports = 0
            }

            # Create install arguments
            $Arguments = "/silent /install /AcceptEndUserLicenseAgreement:1 /components:OMConsole"
            $ArgumentVars = @(
                "InstallPath",
                "UseMicrosoftUpdate",
                "SendCEIPReports",
                "EnableErrorReporting",
                "SendODRReports"
            )
            foreach($ArgumentVar in $ArgumentVars)
            {
                if(!([String]::IsNullOrEmpty((Get-Variable -Name $ArgumentVar).Value)))
                {
                    $Arguments += " /$ArgumentVar`:" + [Environment]::ExpandEnvironmentVariables((Get-Variable -Name $ArgumentVar).Value)
                }
            }
        }
        "Absent"
        {
            # Create uninstall arguments
            $Arguments = "/silent /uninstall /components:OMConsole"
        }
    }

    Write-Verbose "Path: $Path"
    Write-Verbose "Arguments: $Arguments"
    
    $Process = StartWin32Process -Path $Path -Arguments $Arguments -Credential $SetupCredential -AsTask
    Write-Verbose $Process
    WaitForWin32ProcessEnd -Path $Path -Arguments $Arguments -Credential $SetupCredential

    if((Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Name 'PendingFileRenameOperations' -ErrorAction SilentlyContinue) -ne $null)
    {
        $global:DSCMachineStatus = 1
    }
    else
    {
        if(!(Test-TargetResource @PSBoundParameters))
        {
            throw "Set-TargetResouce failed"
        }
    }
}


function Test-TargetResource
{
  [CmdletBinding()]
   [OutputType([System.Boolean])]
  param
   (
       [parameter(Mandatory = $true)]
      [ValidateSet("Present","Absent")]
       [System.String]
     $Ensure = "Present",

        [parameter(Mandatory = $true)]
      [System.String]
     $SourcePath,

        [System.String]
     $SourceFolder = "\SystemCenter2012R2\OperationsManager.en",

     [parameter(Mandatory = $true)]
      [System.Management.Automation.PSCredential]
     $SetupCredential,

       [System.String]
     $InstallPath,

       [System.Byte]
       $UseMicrosoftUpdate,

        [System.Byte]
       $SendCEIPReports,

       [ValidateSet("Never","Queued","Always")]
        [System.String]
     $EnableErrorReporting = "Never",

        [System.Byte]
       $SendODRReports
 )

   $result = ((Get-TargetResource @PSBoundParameters).Ensure -eq $Ensure)
  
    $result
}


Export-ModuleMember -Function *-TargetResource

Next we add a few lines to the MSFT_xSCOMConsoleUpdate for deploying the newest SCOM UpdateRollup (UR3 in this case)
Please edit the MSFT_xSCOMConsoleUpdate.psm1 from the MSFT_xSCOMConsoleUpdate folder and go to the first section (Get-Targetresource)
Find the following Where clause:

 Where-Object {$_.Name -eq "System Center Operations Manager 2012 Console"}).Version

And change it to:

 Where-Object {$_.Name -eq "System Center Operations Manager 2016 Console"}).Version

And next add the following switch condition to the switch statement in the Get-Targetresource function:

 "7.2.11719.0"
        {
            $ProductCode = "{E072D8FC-CD31-4ABE-BD65-606965965426}"
            $PatchID = "{C643876F-311B-451F-98D9-3FC433CDDFC4}"
            $Update = "Update Rollup 3"
        }

And now add the following lines to the switch statement in the Set-Targetresource function:

 "7.2.11719.0"
        {
            $UpdateFile = "KB4016126-AMD64-ENU-Console.msp"
        }

After that the MSFT_xSCOMConsoleUpdate.psm file is aware of SCOM 2016 UR3 Console updates and DSC can push or pull a SCOM 2016 Console UpdateRollup 3.
Now it should look like:

 function Get-TargetResource
{
 [CmdletBinding()]
   [OutputType([System.Collections.Hashtable])]
    param
   (
       [parameter(Mandatory = $true)]
      [ValidateSet("Present","Absent")]
       [System.String]
     $Ensure = "Present",

        [parameter(Mandatory = $true)]
      [System.String]
     $SourcePath,

        [System.String]
     $SourceFolder = "\SystemCenter2012R2\OperationsManager.en\Updates",

     [parameter(Mandatory = $true)]
      [System.Management.Automation.PSCredential]
     $SetupCredential
    )
   
    $Version = (Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -eq "System Center Operations Manager 2016 Console"}).Version
    
    switch($Version)
    {
        "7.2.11719.0"
        {
            $ProductCode = "{E072D8FC-CD31-4ABE-BD65-606965965426}"
            $PatchID = "{C643876F-311B-451F-98D9-3FC433CDDFC4}"
            $Update = "Update Rollup 3"
        }
        "7.1.10226.0"
        {
            $ProductCode = "{041C3416-87CE-4B02-918E-6FDC95F241D3}"
            $PatchID = "{2BE319B6-DBD6-4F52-9DE1-6EDF1E129F48}"
            $Update = "Update Rollup 4"
        }
        "7.2.10015.0"
        {
            $returnValue = @{
                Ensure = "Present"
              SourcePath = $SourcePath
                SourceFolder = $SourceFolder
                Update = "None"
         }
        }
        $null
        {
            $returnValue = @{
               Ensure = "Absent"
               SourcePath = $SourcePath
                SourceFolder = $SourceFolder
            }
        }
        Default
        {
            throw "Unknown version of Operations Manager!"
        }
    }

    if($ProductCode -and $PatchID -and (Get-WmiObject -Class Win32_PatchPackage | Where-Object {($_.ProductCode -eq $ProductCode) -and ($_.PatchID -eq $PatchID)}))
    {
        $returnValue = @{
         Ensure = "Present"
          SourcePath = $SourcePath
            SourceFolder = $SourceFolder
            Update = $Update
        }
    }
    else
    {
     $returnValue = @{
           Ensure = "Absent"
           SourcePath = $SourcePath
            SourceFolder = $SourceFolder
        }
    }

  $returnValue
}


function Set-TargetResource
{
   [CmdletBinding()]
   param
   (
       [parameter(Mandatory = $true)]
      [ValidateSet("Present","Absent")]
       [System.String]
     $Ensure = "Present",

        [parameter(Mandatory = $true)]
      [System.String]
     $SourcePath,

        [System.String]
     $SourceFolder = "\SystemCenter2012R2\OperationsManager.en\Updates",

     [parameter(Mandatory = $true)]
      [System.Management.Automation.PSCredential]
     $SetupCredential
    )

    $Version = (Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -eq "System Center Operations Manager 2016 Console"}).Version
       
    switch($Version)
    {
        "7.2.11719.0"
        {
            $UpdateFile = "KB4016126-AMD64-ENU-Console.msp"
        }
        "7.1.10226.0"
        {
            $UpdateFile = "KB2992020-AMD64-ENU-Console.msp"
        }
        "7.2.10015.0"
        {
            Write-Verbose "No update for this version of Operations Manager!"
        }
        $null
        {
            Write-Verbose "Operations Manager Console not installed!"
        }
        Default
        {
            throw "Unknown version of Operations Manager!"
        }
    }

    if($UpdateFile)
    {
        Import-Module $PSScriptRoot\..\..\xPDT.psm1
        $Path = "msiexec.exe"
        $Path = ResolvePath $Path
        Write-Verbose "Path: $Path"
    
        $MSPPath = Join-Path -Path (Join-Path -Path $SourcePath -ChildPath $SourceFolder) -ChildPath $UpdateFile
        $MSPPath = ResolvePath $MSPPath
        $Arguments = "/update $MSPPath /norestart"
        Write-Verbose "Arguments: $Arguments"

        $Process = StartWin32Process -Path $Path -Arguments $Arguments -Credential $SetupCredential
        Write-Verbose $Process
        WaitForWin32ProcessEnd -Path $Path -Arguments $Arguments -Credential $SetupCredential
    }

    if((Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Name 'PendingFileRenameOperations' -ErrorAction SilentlyContinue) -ne $null)
    {
        $global:DSCMachineStatus = 1
    }
    else
    {
        if(!(Test-TargetResource @PSBoundParameters))
        {
            throw "Set-TargetResouce failed"
        }
    }
}


function Test-TargetResource
{
    [CmdletBinding()]
   [OutputType([System.Boolean])]
  param
   (
       [parameter(Mandatory = $true)]
      [ValidateSet("Present","Absent")]
       [System.String]
     $Ensure = "Present",

        [parameter(Mandatory = $true)]
      [System.String]
     $SourcePath,

        [System.String]
     $SourceFolder = "\SystemCenter2012R2\OperationsManager.en\Updates",

     [parameter(Mandatory = $true)]
      [System.Management.Automation.PSCredential]
     $SetupCredential
    )

   $result = ((Get-TargetResource @PSBoundParameters).Ensure -eq $Ensure)
  
    $result
}


Export-ModuleMember -Function *-TargetResource

Link to Part 2 Link to Part 3