ActiveDirectoryCSDsc.psm1

#Region '.\prefix.ps1' -1

using module .\Modules\DscResource.Base

$script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules/DscResource.Common'
Import-Module -Name $script:dscResourceCommonModulePath

# TODO: The goal would be to remove this, when no classes and public or private functions need it.
$script:activeDirectoryCSDscCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules/ActiveDirectoryCSDsc.Common'
Import-Module -Name $script:activeDirectoryCSDscCommonModulePath

$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US'
#EndRegion '.\prefix.ps1' 11
#Region '.\Classes\001.AdcsReason.ps1' -1

<#
    .SYNOPSIS
        The reason a property of a DSC resource is not in desired state.
 
    .DESCRIPTION
        A DSC resource can have a read-only property `Reasons` that the compliance
        part (audit via Azure Policy) of Azure AutoManage Machine Configuration
        uses. The property Reasons holds an array of AdcsReason. Each AdcsReason
        explains why a property of a DSC resource is not in desired state.
#>


class AdcsReason
{
    [DscProperty()]
    [System.String]
    $Code

    [DscProperty()]
    [System.String]
    $Phrase
}
#EndRegion '.\Classes\001.AdcsReason.ps1' 22
#Region '.\Classes\020.AdcsAuthorityInformationAccess.ps1' -1

<#
    .SYNOPSIS
        The `AdcsAuthorityInformationAccess` DSC resource is used to configure the
        URIs in the Authority Information Access and Online Responder OCSP extensions.
 
    .DESCRIPTION
        This resource can be used to configure the URIs in the Authority Information
        Access and Online Responder OCSP extensions of certificates issued by an
        Active Directory Certificate Authority.
 
    .PARAMETER IsSingleInstance
        Specifies the resource is a single instance, the value must be 'Yes'.
 
    .PARAMETER AiaUri
        Specifies the list of URIs that should be included in the AIA extension of
        the issued certificate.
 
    .PARAMETER OcspUri
        Specifies the list of URIs that should be included in the Online Responder
        OCSP extension of the issued certificate.
 
    .PARAMETER AllowRestartService
        Allows the Certificate Authority service to be restarted if changes are made.
 
    .PARAMETER Reasons
        Returns the reason a property is not in desired state.
 
    .NOTES
        Used Functions:
            Name | Module
            --------------------------------- |--------------------------
            Add-CAAuthorityInformationAccess | AdcsAdministration
            Remove-CAAuthorityInformationAccess | AdcsAdministration
            Assert-Module | DscResource.Common
            Assert-BoundParameter | DscResource.Common
            Get-CaAiaUriList | ActiveDirectoryCSDsc
            Restart-ServiceIfExists | ActiveDirectoryCSDsc.Common
#>


[DscResource()]
class AdcsAuthorityInformationAccess : ResourceBase
{
    [DscProperty(Key)]
    [System.String]
    $IsSingleInstance = 'Yes'

    [DscProperty()]
    [System.String[]]
    $AiaUri

    [DscProperty()]
    [System.String[]]
    $OcspUri

    [DscProperty()]
    [Nullable[System.Boolean]]
    $AllowRestartService

    [DscProperty(NotConfigurable)]
    [AdcsReason[]]
    $Reasons

    AdcsAuthorityInformationAccess () : base ($PSScriptRoot)
    {
        # These properties will not be enforced.
        $this.ExcludeDscProperties = @(
            'IsSingleInstance'
            'AllowRestartService'
        )
    }

    [AdcsAuthorityInformationAccess] Get()
    {
        # Call the base method to return the properties.
        return ([ResourceBase] $this).Get()
    }

    # Base method Get() calls this method to get the current state as a Hashtable.
    [System.Collections.Hashtable] GetCurrentState([System.Collections.Hashtable] $properties)
    {
        $state = @{}

        $AiaList = [System.String[]] (Get-CaAiaUriList -ExtensionType 'AddToCertificateAia')
        $OcspList = [System.String[]] (Get-CaAiaUriList -ExtensionType 'AddToCertificateOcsp')

        if ($AiaList -or $OcspList)
        {
            $state = @{
                IsSingleInstance = $properties.IsSingleInstance
                AiaUri           = $AiaList
                OcspUri          = $OcspList
            }
        }

        return $state
    }

    [void] Set()
    {
        # Call the base method to enforce the properties.
        ([ResourceBase] $this).Set()
    }

    <#
        Base method Set() call this method with the properties that should be
        enforced and that are not in desired state.
    #>

    hidden [void] Modify([System.Collections.Hashtable] $properties)
    {
        $RestartRequired = $false

        if ($properties.ContainsKey('AiaUri'))
        {
            # Get the array number of the entry
            $index = [System.Array]::IndexOf($this.PropertiesNotInDesiredState.Property, 'AiaUri')

            # Add any missing AIA URIs
            foreach ($desiredAiaUri in $this.PropertiesNotInDesiredState[$index].ExpectedValue)
            {
                if ($desiredAiaUri -notin $this.PropertiesNotInDesiredState[$index].ActualValue)
                {
                    Write-Debug -Message ($this.localizedData.AddingAdcsAiaUriMessage -f 'AIA', $desiredAiaUri)

                    Add-CAAuthorityInformationAccess -Uri $desiredAiaUri -AddToCertificateAia -Force -Verbose:$false

                    $RestartRequired = $true
                }
            }

            # Remove any AIA URIs that aren't required
            foreach ($currentAiaUri in $this.PropertiesNotInDesiredState[$index].ActualValue)
            {
                if ($currentAiaUri -notin $this.PropertiesNotInDesiredState[$index].ExpectedValue)
                {
                    Write-Debug -Message ($this.localizedData.RemovingAdcsAiaUriMessage -f 'AIA', $currentAiaUri)

                    Remove-CAAuthorityInformationAccess -Uri $currentAiaUri -AddToCertificateAia -Force -Verbose:$false

                    $RestartRequired = $true
                }
            }
        }

        if ($properties.ContainsKey('OcspUri'))
        {
            # Get the array number of the entry
            $index = [System.Array]::IndexOf($this.PropertiesNotInDesiredState.Property, 'OcspUri')

            # Add any missing OCSP URIs
            foreach ($desiredOcspUri in $this.PropertiesNotInDesiredState[$index].ExpectedValue)
            {
                if ($desiredOcspUri -notin $this.PropertiesNotInDesiredState[$index].ActualValue)
                {
                    Write-Debug -Message ($this.localizedData.AddingAdcsAiaUriMessage -f 'OCSP', $desiredOcspUri)

                    Add-CAAuthorityInformationAccess -Uri $desiredOcspUri -AddToCertificateOcsp -Force -Verbose:$false

                    $RestartRequired = $true
                }
            }

            # Remove any OCSP URIs that aren't required
            foreach ($currentOcspUri in $this.PropertiesNotInDesiredState[$index].ActualValue)
            {
                if ($currentOcspUri -notin $this.PropertiesNotInDesiredState[$index].ExpectedValue)
                {
                    Write-Debug -Message ($this.localizedData.RemovingAdcsAiaUriMessage -f 'OCSP', $currentOcspUri)

                    Remove-CAAuthorityInformationAccess -Uri $currentOcspUri -AddToCertificateOcsp -Force -Verbose:$false

                    $RestartRequired = $true
                }
            }
        }

        if ($RestartRequired -and $this.AllowRestartService)
        {
            Write-Debug -Message $this.localizedData.RestartingCertSvcMessage

            $null = Restart-ServiceIfExists -Name 'CertSvc' -Verbose:$false
        }
    }

    [System.Boolean] Test()
    {
        # Call the base method to test all of the properties that should be enforced.
        return ([ResourceBase] $this).Test()
    }

    <#
        Base method Assert() call this method with the properties that was assigned
        a value.
    #>

    hidden [void] AssertProperties([System.Collections.Hashtable] $properties)
    {
        Assert-Module -ModuleName 'ADCSAdministration'

        $assertBoundParameterParameters = @{
            BoundParameterList = $properties
            RequiredParameter  = @(
                'AiaUri'
                'OcspUri'
            )
            RequiredBehavior   = 'Any'
        }

        Assert-BoundParameter @assertBoundParameterParameters
    }
}
#EndRegion '.\Classes\020.AdcsAuthorityInformationAccess.ps1' 210
#Region '.\Classes\020.AdcsOnlineResponder.ps1' -1

<#
    .SYNOPSIS
        The `AdcsOnlineResponder` DSC resource is used to configure the
        ADCS Online Responder after the feature has been installed on the server.
 
    .DESCRIPTION
        This resource can be used to install an ADCS Online Responder after the feature
        has been installed on the server.
        Using this DSC Resource to configure an ADCS Online Responder assumes that
        the ```ADCS-Online-Responder``` feature has already been installed.
 
    .PARAMETER IsSingleInstance
        Specifies the resource is a single instance, the value must be 'Yes'.
 
    .PARAMETER Credential
        If the Online Responder service is configured to use Standalone certification authority,
        then an account that is a member of the local Administrators on the CA is required. If
        the Online Responder service is configured to use an Enterprise CA, then an account that
        is a member of Domain Admins is required.
 
    .PARAMETER Ensure
        Specifies whether the Online Responder feature should be installed or uninstalled.
 
    .PARAMETER Reasons
        Returns the reason a property is not in desired state.
#>


[DscResource(RunAsCredential = 'NotSupported')]
class AdcsOnlineResponder : ResourceBase
{
    [DscProperty(Key)]
    [System.String]
    $IsSingleInstance = 'Yes'

    [DscProperty(Mandatory)]
    [System.Management.Automation.PSCredential]
    $Credential

    [DscProperty()]
    [Ensure]
    $Ensure = [Ensure]::Present

    [DscProperty(NotConfigurable)]
    [AdcsReason[]]
    $Reasons

    AdcsOnlineResponder () : base ($PSScriptRoot)
    {
        # These properties will not be enforced.
        $this.ExcludeDscProperties = @(
            'IsSingleInstance'
            'Credential'
        )
    }

    [AdcsOnlineResponder] Get()
    {
        # Call the base method to return the properties.
        return ([ResourceBase] $this).Get()
    }

    # Base method Get() calls this method to get the current state as a Hashtable.
    hidden [System.Collections.Hashtable] GetCurrentState([System.Collections.Hashtable] $properties)
    {
        try
        {
            # Run the install with a WhatIf to check if the resource is installed.
            $null = Install-AdcsOnlineResponder -Credential $this.Credential -WhatIf
        }
        catch
        {
            if ($_.Exception.GetType().FullName -eq 'Microsoft.CertificateServices.Deployment.Common.OCSP.OnlineResponderSetupException')
            {
                return @{
                    IsSingleInstance = $this.IsSingleInstance
                }
            }

            New-InvalidOperationException -Message $this.localizedData.ErrorGetCurrentState -ErrorRecord $_
        }

        # If we reach here, the Online Responder is not installed.
        return @{}
    }

    [void] Set()
    {
        # Call the base method to enforce the properties.
        ([ResourceBase] $this).Set()
    }

    <#
        Base method Set() call this method with the properties that should be
        enforced and that are not in desired state.
    #>

    hidden [void] Modify([System.Collections.Hashtable] $properties)
    {
        if ($properties.ContainsKey('Ensure') -and $properties.Ensure -eq [Ensure]::Absent)
        {
            try
            {
                $null = Uninstall-AdcsOnlineResponder -Force
            }
            catch
            {
                New-InvalidOperationException -Message $this.localizedData.ErrorModifyUninstall -ErrorRecord $_
            }
        }
        else
        {
            try
            {
                $null = Install-AdcsOnlineResponder -Credential $this.Credential -Force
            }
            catch
            {
                New-InvalidOperationException -Message $this.localizedData.ErrorModifyInstall -ErrorRecord $_
            }
        }
    }

    [System.Boolean] Test()
    {
        # Call the base method to test all of the properties that should be enforced.
        return ([ResourceBase] $this).Test()
    }

    <#
        Base method Assert() call this method with the properties that was assigned
        a value.
    #>

    hidden [void] AssertProperties([System.Collections.Hashtable] $properties)
    {
        Assert-Module -ModuleName 'ADCSDeployment'
    }
}
#EndRegion '.\Classes\020.AdcsOnlineResponder.ps1' 137
#Region '.\Private\Get-CaAiaUriList.ps1' -1

<#
    .SYNOPSIS
        Return the current Authority Information Access list set on the
        certificate authority, either for the AIA or Online Responder OCSP
        extensions as an array of strings.
 
    .PARAMETER ExtensionType
        The type of the extension to return the URI list for.
#>

function Get-CaAiaUriList
{
    [CmdletBinding()]
    [OutputType([System.String[]])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateSet('AddToCertificateAia', 'AddToCertificateOcsp')]
        $ExtensionType
    )

    Write-Debug -Message ($script:localizedData.GettingAiaUrisMessage -f $ExtensionType)

    return (Get-CAAuthorityInformationAccess -Verbose:$false | Where-Object -Property $ExtensionType -Eq $true).Uri
}
#EndRegion '.\Private\Get-CaAiaUriList.ps1' 25