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


Comments (2)

  1. Bill St says:

    The style and article is really bad and it is not obvious what it will be about.

    Why do you recommend changing to many lines instead of providing the finished code. Or why not doing the changes on the public GitHub repository?

    1. Jan Kratzer says:

      Hi Bill,
      Thank you for your open feedback. I have adapted the article and added an intro with the info What and why I do it. In the next few days, Part 4 follows, which will continue the three articles already created with a usecase. I hope this info will help you further. I'm not going to post anything on GitHub for the time being. The article serves to get a feel for the DSC modules and in this case to make adjustments. Help for self-help. If you have any further questions or suggestions for improvement, please let me know.

Skip to main content