DSCResources/MSFT_UpdateServicesApprovalRule/MSFT_UpdateServicesApprovalRule.psm1

# DSC resource to manage WSUS Approval Rule.

# Classifications ID reference...
#
# Applications = 5C9376AB-8CE6-464A-B136-22113DD69801
# Connectors = 434DE588-ED14-48F5-8EED-A15E09A991F6
# Critical Updates = E6CF1350-C01B-414D-A61F-263D14D133B4
# Definition Updates = E0789628-CE08-4437-BE74-2495B842F43B
# Driver Sets = 77835C8D-62A7-41F5-82AD-F28D1AF1E3B1
# Drivers = EBFC1FC5-71A4-4F7B-9ACA-3B9A503104A0
# Developer Kits = E140075D-8433-45C3-AD87-E72345B36078
# Feature Packs = B54E7D24-7ADD-428F-8B75-90A396FA584F
# Guidance = 9511D615-35B2-47BB-927F-F73D8E9260BB
# Hotfix = 5EAEF3E6-ABB0-4192-9B26-0FD955381FA9
# Security Updates = 0FA1201D-4330-4FA8-8AE9-B877473B6441
# Service Packs = 68C5B0A3-D1A6-4553-AE49-01D3A7827828
# Third Party = 871A0782-BE12-A5C4-C57F-1BD6D9F7144E
# Tools = B4832BD8-E735-4761-8DAF-37F882276DAB
# Update Rollups = 28BC880E-0592-4CBF-8F95-C79B17911D5F
# Updates = CD5FFD1E-E932-4E3A-BF74-18BF0B1BBD83
# Upgrades = 3689BDC8-B205-4AF4-8D4A-A63924C5E9D5


# Load Common Module
$script:resourceHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common'
Import-Module -Name $script:resourceHelperModulePath

$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US'

<#
    .SYNOPSIS
        Returns the current Approval Rules
 
    .PARAMETER Name
        If provided, returns details of a specific rule
 
#>

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name
    )

    try
    {
        $WsusServer = Get-WsusServer
        $Ensure = 'Absent'
        $Classifications = $null
        $Products = $null
        $ComputerGroups = $null
        $Enabled = $null

        if ($null -ne $WsusServer)
        {
            Write-Verbose -Message ('Identified WSUS server information: {0}' -f $WsusServer.Name)

            $ApprovalRule = $WsusServer.GetInstallApprovalRules() | Where-Object -FilterScript { $_.Name -eq $Name }

            if ($null -ne $ApprovalRule)
            {
                $Ensure = 'Present'

                if ( -Not ($Classifications = @($ApprovalRule.GetUpdateClassifications().ID.Guid)))
                {
                    $Classifications = @('All Classifications')
                }

                if ( -Not ($Products = @($ApprovalRule.GetCategories().Title)))
                {
                    $Products = @('All Products')
                }

                if ( -Not ($ComputerGroups = @($ApprovalRule.GetComputerTargetGroups().Name)))
                {
                    $ComputerGroups = @('All Computers')
                }

                $Enabled = $ApprovalRule.Enabled
            }
        }
        else
        {
            Write-Verbose -Message 'Did not identify an instance of WSUS'
        }
    }
    catch
    {
        New-InvalidOperationException -Message $script:localizedData.WSUSConfigurationFailed -ErrorRecord $_
    }

    $returnValue = @{
        Ensure          = $Ensure
        Name            = $Name
        Classifications = $Classifications
        Products        = $Products
        ComputerGroups  = $ComputerGroups
        Enabled         = $Enabled
    }

    $returnValue
}

<#
    .SYNOPSIS
        Sets approval rules
 
    .PARAMETER Ensure
        Determines if the rule should be created or removed.
        Accepts 'Present'(default) or 'Absent'.
 
    .PARAMETER Name
        Name of the rule to create
 
    .PARAMETER Classifications
        Classification for the rule or All Classifications
 
    .PARAMETER Products
        The name of the product for the rule or All Products
 
    .PARAMETER ComputerGroups
        The name of the computer group to apply the rule to or All Computers
 
    .PARAMETER Enabled
        Boolean to set rule enabled or disabled
 
    .PARAMETER Synchronize
        Boolean, when enabled the rule will synchronize with Windows Update
        This applies because the rule will approve updates as they are sync'd
 
    .PARAMETER RunRuleNow
        Boolean that has the same effect as clicking 'Run Rule Now' when Set occurs
        The impact is updates already sync'd will also be approved
        Otherwise, the rule is not applied to existing content
 
#>

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter()]
        [System.String[]]
        $Classifications = @('All Classifications'),

        [Parameter()]
        [System.String[]]
        $Products = @('All Products'),

        [Parameter()]
        [System.String[]]
        $ComputerGroups = @('All Computers'),

        [Parameter()]
        [System.Boolean]
        $Enabled,

        [Parameter()]
        [System.Boolean]
        $Synchronize,

        [Parameter()]
        [System.Boolean]
        $RunRuleNow
    )

    try
    {
        if ($WsusServer = Get-WsusServer)
        {
            switch ($Ensure)
            {
                'Present'
                {
                    if ($ApprovalRule = $WsusServer.GetInstallApprovalRules() | Where-Object -FilterScript { $_.Name -eq $Name })
                    {
                        Write-Verbose -Message $script:localizedData.UseExistingApprovalRule
                    }
                    else
                    {
                        Write-Verbose -Message $script:localizedData.CreateApprovalRule
                        $ApprovalRule = $WsusServer.CreateInstallApprovalRule($Name)
                    }

                    if ($ApprovalRule)
                    {
                        $ApprovalRule.Enabled = $Enabled
                        $ApprovalRule.Save()

                        $ClassificationCollection = New-Object `
                            -TypeName Microsoft.UpdateServices.Administration.UpdateClassificationCollection

                        foreach ($Classification in $Classifications)
                        {
                            if ($WsusClassification = Get-WsusClassification | Where-Object -FilterScript { $_.Classification.ID.Guid -eq $Classification })
                            {
                                $ClassificationCollection.Add($WsusServer.GetUpdateClassification(`
                                            $WsusClassification.Classification.Id))
                            }
                            else
                            {
                                Write-Verbose -Message ($script:localizedData.ClassificationNotFound -f $Classification)
                            }
                        }

                        $ApprovalRule.SetUpdateClassifications($ClassificationCollection)
                        $ApprovalRule.Save()

                        $ProductCollection = New-Object -TypeName Microsoft.UpdateServices.Administration.UpdateCategoryCollection
                        foreach ($Product in $Products)
                        {
                            if ($WsusProduct = Get-WsusProduct | Where-Object -FilterScript { $_.Product.Title -eq $Product })
                            {
                                $ProductCollection.Add($WsusServer.GetUpdateCategory($WsusProduct.Product.Id))
                            }
                        }

                        $ApprovalRule.SetCategories($ProductCollection)
                        $ApprovalRule.Save()

                        $ComputerGroupCollection = New-Object -TypeName Microsoft.UpdateServices.Administration.ComputerTargetGroupCollection
                        foreach ($ComputerGroup in $ComputerGroups)
                        {
                            if ($WsusComputerGroup = $WsusServer.GetComputerTargetGroups() | Where-Object -FilterScript { $_.Name -eq $ComputerGroup })
                            {
                                $ComputerGroupCollection.Add($WsusComputerGroup)
                            }
                        }

                        $ApprovalRule.SetComputerTargetGroups($ComputerGroupCollection)
                        $ApprovalRule.Save()
                        if ($RunRuleNow)
                        {
                            Write-Verbose -Message ($script:localizedData.RunApprovalRule -f $Name)

                            try
                            {
                                $ApprovalRule.ApplyRule()
                            }
                            catch
                            {
                                New-InvalidOperationException -Message (
                                    $script:localizedData.RuleFailedToApply -f $Name
                                ) -ErrorRecord $_
                            }
                        }
                    }
                    else
                    {
                        New-InvalidOperationException -Message (
                            $script:localizedData.RuleFailedToCreate -f $Name
                        ) -ErrorRecord $_
                    }
                }
                'Absent'
                {
                    if ($ApprovalRule = $WsusServer.GetInstallApprovalRules() | Where-Object -FilterScript { $_.Name -eq $Name })
                    {
                        $WsusServer.DeleteInstallApprovalRule($ApprovalRule.Id)
                    }
                    else
                    {
                        Write-Verbose -Message ($script:localizedData.RuleDoNotExist -f $ApprovalRule.Name)
                    }
                }
            }
        }
        else
        {
            Write-Verbose -Message $script:localizedData.GetWsusServerFailed
        }
    }
    catch
    {
        $errorMessage = $script:localizedData.RuleFailedToCreate -f $Name
        New-InvalidOperationException -Message $errorMessage -ErrorRecord $_
    }

    if ( -Not (Test-TargetResource @PSBoundParameters))
    {
        $errorMessage = $script:localizedData.TestFailedAfterSet
        New-InvalidResultException -Message $errorMessage -ErrorRecord $_
    }
    else
    {
        if ($Synchronize)
        {
            Write-Verbose -Message $script:localizedData.SyncWsus

            try
            {
                $WsusServer.GetSubscription().StartSynchronization()
            }
            catch
            {
                $errorMessage = $script:localizedData.FailedSyncStart
                New-InvalidOperationException -Message $errorMessage -ErrorRecord $_
            }
        }
    }
}

<#
    .SYNOPSIS
        Tests approval rules
 
    .PARAMETER Ensure
        Determines if the rule should be created or removed.
        Accepts 'Present'(default) or 'Absent'.
 
    .PARAMETER Name
        Name of the rule to create
 
    .PARAMETER Classifications
        Classification for the rule or All Classifications
 
    .PARAMETER Products
        THe name of the product for the rule or All Products
 
    .PARAMETER ComputerGroups
        The name of the computer group to apply the rule to or All Computers
 
    .PARAMETER Enabled
        Boolean to set rule enabled or disabled
 
    .PARAMETER Synchronize
        Boolean, when enabled the rule will synchronize with Windows Update
        This applies because the rule will approve updates as they are sync'd
 
    .PARAMETER RunRuleNow
        Boolean that has the same effect as clicking 'Run Rule Now' when Set occurs
        The impact is updates already sync'd will also be approved
        Otherwise, the rule is not applied to existing content
 
#>

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter()]
        [System.String[]]
        $Classifications = @('All Classifications'),

        [Parameter()]
        [System.String[]]
        $Products = @('All Products'),

        [Parameter()]
        [System.String[]]
        $ComputerGroups = @('All Computers'),

        [Parameter()]
        [System.Boolean]
        $Enabled,

        [Parameter()]
        [System.Boolean]
        $Synchronize,

        [Parameter()]
        [System.Boolean]
        $RunRuleNow
    )

    $result = $true

    $ApprovalRule = Get-TargetResource -Name $Name

    if ($ApprovalRule.Ensure -ne $Ensure)
    {
        Write-Verbose -Message $script:localizedData.EnsureTestFailed
        $result = $false
    }

    if ($result -and ($ApprovalRule.Ensure -eq 'Present'))
    {
        if ($null -ne (Compare-Object -ReferenceObject ($ApprovalRule.Classifications | Sort-Object -Unique) -DifferenceObject ($Classifications | Sort-Object -Unique) -SyncWindow 0))
        {
            Write-Verbose -Message $script:localizedData.ClassificationTestFailed
            $result = $false
        }

        if ($null -ne (Compare-Object -ReferenceObject ($ApprovalRule.Products | Sort-Object -Unique) -DifferenceObject ($Products | Sort-Object -Unique) -SyncWindow 0))
        {
            Write-Verbose -Message $script:localizedData.ProductsTestFailed
            $result = $false
        }

        if ($null -ne (Compare-Object -ReferenceObject ($ApprovalRule.ComputerGroups | Sort-Object -Unique) -DifferenceObject ($ComputerGroups | Sort-Object -Unique) -SyncWindow 0))
        {
            Write-Verbose -Message $script:localizedData.ComputerGrpTestFailed
            $result = $false
        }

        if ($ApprovalRule.Enabled -ne $Enabled)
        {
            Write-Verbose -Message $script:localizedData.EnabledTestFailed
            $result = $false
        }
    }

    $result
}

Export-ModuleMember -Function *-TargetResource