DscResources/Wds/Wds.schema.psm1

configuration Wds
{
    param
    (
        [Parameter(Mandatory)]
        [string]
        $RemInstPath,

        [Parameter()]
        [pscredential]
        $RunAsUser,

        [Parameter()]
        [ValidateSet( 'All', 'Known', 'None')]
        [string]
        $AnswerClients,

        [Parameter()]
        [boolean]
        $UseExistingDhcpScope = $false,
        
        [Parameter()]
        [string]
        $ScopeStart,

        [Parameter()]
        [string]
        $ScopeEnd,

        [Parameter()]
        [string]
        $ScopeId,

        [Parameter()]
        [string]
        $SubnetMask,

        [Parameter()]
        [string]
        $DomainName,

        [Parameter()]
        [string]
        $DefaultDeviceOU,

        [Parameter()]
        [hashtable[]]
        $BootImages,

        [Parameter()]
        [hashtable[]]
        $ImageGroups,

        [Parameter()]
        [hashtable[]]
        $InstallImages,

        [Parameter()]
        [hashtable[]]
        $DeviceReservations
    )

    Import-DscResource -ModuleName PSDesiredStateConfiguration
    Import-DscResource -ModuleName WdsDsc
    Import-DscResource -ModuleName xDhcpServer

    $dependsOnClientScope = ''

    if( $UseExistingDhcpScope -eq $false )
    {
        WindowsFeature dhcpFeature
        {
            Name   = 'DHCP'
            IncludeAllSubFeature = $true
            Ensure = 'Present'
        }

        xDhcpServerScope clientScope
        {
            ScopeId      = $ScopeId
            IPStartRange = $ScopeStart
            IPEndRange   = $ScopeEnd
            SubnetMask   = $SubnetMask
            Name         = 'WdsClients'
            Ensure       = 'Present'
            DependsOn    = '[WindowsFeature]dhcpFeature'
        }

        $dependsOnClientScope = '[xDhcpServerScope]clientScope'
    }
    else
    {
        if( -not [string]::IsNullOrWhiteSpace($ScopeStart) -or
            -not [string]::IsNullOrWhiteSpace($ScopeEnd) -or
            -not [string]::IsNullOrWhiteSpace($SubnetMask) )
        {
            throw "ERROR: if 'UseExistingDhcpScope' is set to 'true' the DHCP scope definition shall be empty."
        }
    }

    WindowsFeature wdsFeature
    {
        Name                 = 'WDS'
        IncludeAllSubFeature = $true
        Ensure               = 'Present'
    }

    if( $null -ne $RunAsUser )
    {
        # the RunAs user requires local administrator rights
        Group addRunAsUserToLocalAdminsGroup
        {
            GroupName        = 'Administrators'
            Ensure           = 'Present'
            MembersToInclude = $RunAsUser.UserName
        }        
    }

    WdsInitialize wdsInit
    {
        IsSingleInstance     = 'Yes'
        PsDscRunAsCredential = $RunAsUser
        Path                 = $RemInstPath
        Authorized           = $true
        Standalone           = $false
        Ensure               = 'Present'
        DependsOn            = '[WindowsFeature]wdsFeature'
    }

    Service wdsService
    {
        Name        = 'WDSServer'
        StartupType = 'Automatic'
        State       = 'Running'
        Ensure      = 'Present'
        DependsOn   = '[WindowsFeature]wdsFeature'
    }

    $dependsOnWdsService = '[Service]wdsService'

    if( -not [string]::IsNullOrWhiteSpace($AnswerClients) )
    {
        Script 'wdsCfg_AnswerClients'
        {
            TestScript = {
                $patternAnswerClients = [Regex]::new('Answer clients:\s*(\w+)')
                $patternAnswerKnownClients = [Regex]::new('Answer only known clients:\s*(\w+)')

                $output = wdsutil /Get-Server /Show:Config
                Write-Verbose "Result of wdsutil /Get-Server /Show:Config:`n$output"

                $matchAnswerClients      = $patternAnswerClients.Matches($output)
                $matchAnswerKnownClients = $patternAnswerKnownClients.Matches($output)

                if( $null -eq $matchAnswerClients.Groups  -or 
                    $null -eq $matchAnswerKnownClients.Groups )
                {
                    Write-Warning "Output of wdsutil has not the expected content."
                    return $false
                }

                [boolean]$curAnswerClients      = if( $matchAnswerClients.Groups[1].Value -eq 'Yes' ) { $true } else { $false }
                [boolean]$curAnswerKnownClients = if( $matchAnswerKnownClients.Groups[1].Value -eq 'Yes' ) { $true } else { $false }

                Write-Verbose "Expected AnswerClients: $using:AnswerClients"
                Write-Verbose "Current AnswerClients: $curAnswerClients"
                Write-Verbose "Current AnswerKnownClients: $curAnswerKnownClients"

                if( $using:AnswerClients -eq 'All' )
                {
                    if( $curAnswerClients -ne $false -and $curAnswerKnownClients -eq $false )
                    {
                        return $true
                    }
                }
                elseif( $using:AnswerClients -eq 'Known' )
                {
                    if( $curAswerClients -ne $false -and $curAnswerKnownClients -ne $false )
                    {
                        return $true
                    }
                }
                elseif( $curAnswerClients -eq $false )
                {
                    # 'None'
                    return $true
                }

                Write-Verbose 'Values are different.'
                return $false
            }
            SetScript = {
                Write-Verbose "Running: wdsutil /Set-Server /AnswerClients:$using:AnswerClients"
                $output = wdsutil /Set-Server /AnswerClients:$using:AnswerClients
                Write-Verbose "Result of 'wdsutil /Set-Server /AnswerClients:$using:AnswerClients':`n$output"
            }
            GetScript = { return @{result = 'N/A'}}
            DependsOn = $dependsOnWdsService
            PsDscRunAsCredential = $RunAsUser
        }        
    }

    if( $null -ne $BootImages )
    {
        foreach( $image in $BootImages )
        {
            $image.DependsOn = $dependsOnWdsService

            $executionName = "bootImg_$($image.NewImageName -replace '[().:\s]', '')"

            (Get-DscSplattedResource -ResourceName WdsBootImage -ExecutionName $executionName -Properties $image -NoInvoke).Invoke($image)
        }
    }

    if( $null -ne $ImageGroups )
    {
        foreach( $group in $ImageGroups )
        {
            Script "imgGroup_$($group.Name -replace '[().:\s]', '_')"
            {
                TestScript = {
                    $wdsGroup = Get-WdsInstallImageGroup -Name $using:group.Name -ErrorAction SilentlyContinue
    
                    if( $using:group.Ensure -eq 'Absent' )
                    {
                        if( $null -eq $wdsGroup -or $wdsGroup.Count -eq 0 )
                        {
                            return $true
                        }
                    }
                    else
                    {
                        if( $null -ne $wdsGroup -and $wdsGroup.Name -eq $using:group.Name )
                        {
                            if( [string]::IsNullOrWhiteSpace($using:group.SecurityDescriptor) -or
                                $wdsGroup.Security -eq $using:group.SecurityDescriptor)
                            {
                                return $true                            
                            }
                        }
                    }
                    return $false
                }
                SetScript = {
                    $params = @{
                        Name = $group.Name
                    }

                    if( $using:group.Ensure -eq 'Absent' )
                    {
                        Remove-WdsInstallImageGroup @params
                    }
                    else 
                    {
                        if( -not [string]::IsNullOrWhiteSpace($using:group.SecurityDescriptor) )
                        {
                            $params.SecurityDescriptorSDDL = $using:group.SecurityDescriptor   
                        }

                        $wdsGroup = Get-WdsInstallImageGroup -Name $using:group.Name -ErrorAction SilentlyContinue

                        if( $null -eq $wdsGroup )
                        {
                            New-WdsInstallImageGroup @params                            
                        }
                        else
                        {
                            Set-WdsInstallImageGroup @params                            
                        }
                    }
                }
                GetScript = { return @{result = 'N/A'}}
                DependsOn = $dependsOnWdsService
            }        
        }
    }

    if( $null -ne $InstallImages )
    {
        foreach( $image in $InstallImages )
        {
            $image.DependsOn = $dependsOnWdsService

            $executionName = "instImg_$($image.NewImageName -replace '[().:\s]', '')"

            (Get-DscSplattedResource -ResourceName WdsInstallImage -ExecutionName $executionName -Properties $image -NoInvoke).Invoke($image)
        }
    }

    if( $null -ne $DeviceReservations )
    {
        foreach( $devRes in $DeviceReservations )
        {
            # Remove Case Sensitivity of ordered Dictionary or Hashtables
            $devRes = @{}+$devRes

            if( -not $devRes.ContainsKey('Ensure') )
            {
                $devRes.Ensure = 'Present'
            }

            # make a DHCP reservation
            if( -not [string]::IsNullOrWhiteSpace($devRes.IpAddress) )
            {
                if( [string]::IsNullOrWhiteSpace($ScopeId) )
                {
                    throw "ERROR: if 'IpAddress' is specified the parameter ScopeId is required to make a DHCP reservation."
                }

                xDhcpServerReservation "dhcpRes_$($devRes.DeviceName -replace '[().:\s]', '')"
                {
                    IPAddress        = $devRes.IpAddress
                    ClientMACAddress = $devRes.MacAddress
                    Name             = $devRes.DeviceName
                    ScopeID          = $ScopeId
                    Ensure           = $devRes.Ensure
                    DependsOn        = $dependsOnClientScope
                }                
            }

            # use MacAddress as DeviceID if it's not specified
            if( [string]::IsNullOrWhiteSpace($devRes.DeviceID) )
            {
                $devRes.DeviceID  = $devRes.MacAddress
            }

            # remove DHCP specific attributes
            $devRes.Remove('IpAddress')
            $devRes.Remove('MacAddress')

            $devRes.DependsOn = $dependsOnWdsService

            if( $devRes.JoinDomain -eq $true )
            {
                if( [string]::IsNullOrWhiteSpace($DomainName) )
                {
                    throw "ERROR: $($devRes.DeviceName) - DomainName shall be specified to make a domain join."
                }

                $devRes.Domain = $DomainName

                if( -not [string]::IsNullOrWhiteSpace($DefaultDeviceOU) -and [string]::IsNullOrWhiteSpace($devRes.OU))
                {
                    $devRes.OU = $DefaultDeviceOU
                }
            }
            
            if( [string]::IsNullOrWhiteSpace($devRes.User) )
            {
                # remove JoinRights if no user is specified
                $devRes.Remove( 'JoinRights' )
            }
            else
            {
                if( [string]::IsNullOrWhiteSpace($devRes.JoinRights) )
                {
                    $devRes.JoinRights = 'JoinOnly'
                }
            }

            if( [string]::IsNullOrWhiteSpace($devRes.PxePromptPolicy) )
            {
                $devRes.PxePromptPolicy = 'NoPrompt'
            }

            if( $null -ne $RunAsUser )
            {
                $devRes.PsDscRunAsCredential = $RunAsUser
            }

            $executionName = "wdsRes_$($devRes.DeviceName -replace '[().:\s]', '')"

            (Get-DscSplattedResource -ResourceName WdsDeviceReservation -ExecutionName $executionName -Properties $devRes -NoInvoke).Invoke($devRes)
        }
    }
}