DSCResources/MSFT_AdfsWebApiApplication/MSFT_AdfsWebApiApplication.psm1

<#
    .SYNOPSIS
        DSC module for the Adfs Web API Application resource
 
    .DESCRIPTION
        The AdfsWebApiApplication DSC resource manages Web API Applications within Active Directory Federation
        Services. Web Api Applications are a construct that represents a web API secured by ADFS.
 
        ## Requirements
 
        * Target machine must be running ADFS on Windows Server 2016 or above to use this resource.
 
    .PARAMETER Name
        Key - String
        Specifies a name for the Web API application.
 
    .PARAMETER ApplicationGroupIdentifier
        Required - String
        Specifies the ID of an application group for the Web API application.
 
    .PARAMETER Identifier
        Required - String
        Specifies an identifier for the Web API application.
 
    .PARAMETER Description
        Write - String
        Specifies a description for the Web API application.
 
    .PARAMETER Ensure
        Write - String
        Allowed values: Present, Absent
        Specifies whether the Web API application should be present or absent. Default value is 'Present'.
 
    .PARAMETER AllowedAuthenticationClassReferences
        Write - String
        Specifies an array of allow authentication class references.
 
    .PARAMETER ClaimsProviderName
        Write - String
        Specifies an array of claims provider names that you can configure for a relying party trust for Home Realm
        Discovery (HRD) scenario.
 
    .PARAMETER IssuanceAuthorizationRules
        Write - String
        Specifies the issuance authorization rules.
 
    .PARAMETER DelegationAuthorizationRules
        Write - String
        Specifies delegation authorization rules.
 
    .PARAMETER ImpersonationAuthorizationRules
        Write - String
        Specifies the impersonation authorization rules.
 
    .PARAMETER IssuanceTransformRules
        Write - String
        Specifies the issuance transform rules.
 
    .PARAMETER AdditionalAuthenticationRules
        Write - String
        Specifies additional authentication rules.
 
    .PARAMETER AccessControlPolicyName
        Write - String
        Specifies the name of an access control policy.
 
    .PARAMETER NotBeforeSkew
        Write - Sint32
        Specifies the not before skew value.
 
    .PARAMETER TokenLifetime
        Write - Sint32
        Specifies the token lifetime.
 
    .PARAMETER AlwaysRequireAuthentication
        Write - Boolean
        Indicates that this Web API application role always requires authentication, even if it previously
        authenticated credentials for access. Specify this parameter to require users to always supply credentials to
        access sensitive resources.
 
    .PARAMETER AllowedClientTypes
        Write - String
        Allowed values: None, Public, Confidential
        Specifies allowed client types.
 
    .PARAMETER IssueOAuthRefreshTokensTo
        Write - String
        Allowed values: NoDevice, WorkplaceJoinedDevices, AllDevices
        Specifies the refresh token issuance device types.
 
    .PARAMETER RefreshTokenProtectionEnabled
        Write - Boolean
        Indicates whether refresh token protection is enabled.
 
    .PARAMETER RequestMFAFromClaimsProviders
        Write - Boolean
        Indicates that the request MFA from claims providers option is used.
#>


Set-StrictMode -Version 2.0

$script:dscModuleName = 'AdfsDsc'
$script:psModuleName = 'ADFS'
$script:dscResourceName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)

$script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent
$script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules'

$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath "$($script:DSCModuleName).Common"
Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath "$($script:dscModuleName).Common.psm1")

$script:localizedData = Get-LocalizedData -ResourceName $script:dscResourceName

function Get-TargetResource
{
    <#
    .SYNOPSIS
        Get-TargetResource
 
    .NOTES
        Used Resource PowerShell Cmdlets:
        - Get-AdfsWebApiApplication - https://docs.microsoft.com/en-us/powershell/module/adfs/get-adfswebApiapplication
    #>


    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

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

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

    # Check of the Resource PowerShell module is installed
    Assert-Module -ModuleName $script:psModuleName

    # Check if the Get-AdfsWebApiApplication command is available
    Assert-Command -Module $script:psModuleName -Command 'Get-AdfsWebApiApplication'

    # Check if the ADFS Service is present and running
    Assert-AdfsService -Verbose

    Write-Verbose -Message ($script:localizedData.GettingResourceMessage -f $Name)

    $targetResource = Get-AdfsWebApiApplication -Name $Name

    if ($targetResource)
    {
        # Resource exists
        Write-Debug "Target resource $Name exists"
        $returnValue = @{
            Name                                 = $targetResource.Name
            ApplicationGroupIdentifier           = $targetResource.ApplicationGroupIdentifier
            Identifier                           = @($targetResource.Identifier)
            Description                          = $targetResource.Description
            AllowedAuthenticationClassReferences = @($targetResource.AllowedAuthenticationClassReferences)
            ClaimsProviderName                   = @($targetResource.ClaimsProviderName)
            IssuanceAuthorizationRules           = $targetResource.IssuanceAuthorizationRules
            DelegationAuthorizationRules         = $targetResource.DelegationAuthorizationRules
            ImpersonationAuthorizationRules      = $targetResource.ImpersonationAuthorizationRules
            IssuanceTransformRules               = @(ConvertFrom-IssuanceTransformRule -Rule $targetResource.IssuanceTransformRules)
            AdditionalAuthenticationRules        = $targetResource.AdditionalAuthenticationRules
            AccessControlPolicyName              = $targetResource.AccessControlPolicyName
            NotBeforeSkew                        = $targetResource.NotBeforeSkew
            TokenLifetime                        = $targetResource.TokenLifetime
            AlwaysRequireAuthentication          = $targetResource.AlwaysRequireAuthentication
            AllowedClientTypes                   = @($targetResource.AllowedClientTypes)
            IssueOAuthRefreshTokensTo            = $targetResource.IssueOAuthRefreshTokensTo
            RefreshTokenProtectionEnabled        = $targetResource.RefreshTokenProtectionEnabled
            RequestMFAFromClaimsProviders        = $targetResource.RequestMFAFromClaimsProviders
            Ensure                               = 'Present'
        }
    }
    else
    {
        # Resource does not exist
        Write-Debug "Target resource $Name does not exist"
        $returnValue = @{
            Name                                 = $Name
            ApplicationGroupIdentifier           = $ApplicationGroupIdentifier
            Identifier                           = @($Identifier)
            Description                          = $null
            AllowedAuthenticationClassReferences = @()
            ClaimsProviderName                   = @()
            IssuanceAuthorizationRules           = $null
            DelegationAuthorizationRules         = $null
            ImpersonationAuthorizationRules      = $null
            IssuanceTransformRules               = $null
            AdditionalAuthenticationRules        = $null
            AccessControlPolicyName              = $null
            NotBeforeSkew                        = 0
            TokenLifetime                        = 0
            AlwaysRequireAuthentication          = $null
            AllowedClientTypes                   = @('None')
            IssueOAuthRefreshTokensTo            = 'NoDevice'
            RefreshTokenProtectionEnabled        = $false
            RequestMFAFromClaimsProviders        = $false
            Ensure                               = 'Absent'
        }
    }

    Write-Debug "Returning Value"
    $returnValue
}


function Set-TargetResource
{
    <#
    .SYNOPSIS
        Set-TargetResource
 
    .NOTES
        Used Resource PowerShell Cmdlets:
        - Add-AdfsWebApiApplication - https://docs.microsoft.com/en-us/powershell/module/adfs/add-adfswebapiapplication
        - Remove-AdfsWebApiApplication - https://docs.microsoft.com/en-us/powershell/module/adfs/remove-adfswebapiapplication
        - Set-AdfsWebApiApplication - https://docs.microsoft.com/en-us/powershell/module/adfs/set-adfswebapiapplication
    #>


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

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

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

        [Parameter()]
        [System.String]
        $Description,

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter()]
        [System.String[]]
        $AllowedAuthenticationClassReferences,

        [Parameter()]
        [System.String[]]
        $ClaimsProviderName,

        [Parameter()]
        [System.String]
        $IssuanceAuthorizationRules,

        [Parameter()]
        [System.String]
        $DelegationAuthorizationRules,

        [Parameter()]
        [System.String]
        $ImpersonationAuthorizationRules,

        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance[]]
        $IssuanceTransformRules,

        [Parameter()]
        [System.String]
        $AdditionalAuthenticationRules,

        [Parameter()]
        [System.String]
        $AccessControlPolicyName,

        [Parameter()]
        [System.Int32]
        $NotBeforeSkew,

        [Parameter()]
        [System.Int32]
        $TokenLifetime,

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

        [Parameter()]
        [ValidateSet('None', 'Public', 'Confidential')]
        [System.String[]]
        $AllowedClientTypes,

        [Parameter()]
        [ValidateSet('NoDevice', 'WorkplaceJoinedDevices', 'AllDevices')]
        [System.String]
        $IssueOAuthRefreshTokensTo,

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

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

    # Remove any parameters not used in Splats
    [HashTable]$parameters = $PSBoundParameters
    $parameters.Remove('Ensure')
    $parameters.Remove('Verbose')

    $GetTargetResourceParms = @{
        ApplicationGroupIdentifier = $ApplicationGroupIdentifier
        Name                       = $Name
        Identifier                 = $Identifier
    }
    $targetResource = Get-TargetResource @GetTargetResourceParms

    if ($targetResource.Ensure -eq 'Present')
    {
        # Resource is Present
        if ($Ensure -eq 'Present')
        {
            # Resource should be Present
            $propertiesNotInDesiredState = @()

            if ($PSBoundParameters.Keys.Contains('IssuanceTransformRules'))
            {
                $propertiesNotInDesiredState += (
                    Compare-IssuanceTransformRule -CurrentValue $targetResource.IssuanceTransformRules `
                        -DesiredValue $IssuanceTransformRules |
                    Where-Object -Property InDesiredState -eq $false)
            }

            $propertiesNotInDesiredState += (
                Compare-ResourcePropertyState -CurrentValues $targetResource -DesiredValues $parameters `
                    -IgnoreProperties 'IssuanceTransformRules' | Where-Object -Property InDesiredState -eq $false)

            if ($propertiesNotInDesiredState |
                    Where-Object -Property ParameterName -eq 'ApplicationGroupIdentifier')
            {
                Write-Verbose -Message ($script:localizedData.RemovingResourceMessage -f
                    $Name, $targetResource.ApplicationGroupIdentifier)
                Remove-AdfsWebApiApplication -TargetName $Name
                Write-Verbose -Message ($script:localizedData.AddingResourceMessage -f
                    $Name, $ApplicationGroupIdentifier)
                Add-AdfsWebApiApplication @parameters -Verbose:$false
                break
            }

            $setParameters = @{ }
            foreach ($property in $propertiesNotInDesiredState)
            {
                Write-Verbose -Message ($script:localizedData.SettingResourceMessage -f
                    $Name, $property.ParameterName, ($property.Expected -join ', '))

                if ($property.ParameterName -eq 'IssuanceTransformRules')
                {
                    # Custom processing for 'IssuanceTransformRules' property
                    $SetParameters.Add($property.ParameterName, ($IssuanceTransformRules | ConvertTo-IssuanceTransformRule))
                }
                else
                {
                    $setParameters.add($property.ParameterName, $property.Expected)
                }
            }

            Set-AdfsWebApiApplication -TargetName $Name @setParameters
        }
        else
        {
            # Resource should be Absent
            Write-Verbose -Message ($script:localizedData.RemovingResourceMessage -f
                $Name, $ApplicationGroupIdentifier)
            Remove-AdfsWebApiApplication -TargetName $Name
        }
    }
    else
    {
        # Resource is Absent
        if ($Ensure -eq 'Present')
        {
            # Resource should be Present
            if ($parameters.ContainsKey('IssuanceTransformRules'))
            {
                # Custom processing for 'IssuanceTransformRules' property
                $parameters.IssuanceTransformRules = $parameters.IssuanceTransformRules | ConvertTo-IssuanceTransformRule
            }

            Write-Verbose -Message ($script:localizedData.AddingResourceMessage -f
                $Name, $ApplicationGroupIdentifier)
            Add-AdfsWebApiApplication @parameters -Verbose:$false
        }
        else
        {
            # Resource should be Absent
            Write-Verbose -Message ($script:localizedData.ResourceInDesiredStateMessage -f $Name)
        }
    }
}

function Test-TargetResource
{
    <#
    .SYNOPSIS
        Test-TargetResource
    #>


    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

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

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

        [Parameter()]
        [System.String]
        $Description,

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter()]
        [System.String[]]
        $AllowedAuthenticationClassReferences,

        [Parameter()]
        [System.String[]]
        $ClaimsProviderName,

        [Parameter()]
        [System.String]
        $IssuanceAuthorizationRules,

        [Parameter()]
        [System.String]
        $DelegationAuthorizationRules,

        [Parameter()]
        [System.String]
        $ImpersonationAuthorizationRules,

        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance[]]
        $IssuanceTransformRules,

        [Parameter()]
        [System.String]
        $AdditionalAuthenticationRules,

        [Parameter()]
        [System.String]
        $AccessControlPolicyName,

        [Parameter()]
        [System.Int32]
        $NotBeforeSkew,

        [Parameter()]
        [System.Int32]
        $TokenLifetime,

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

        [Parameter()]
        [ValidateSet('None', 'Public', 'Confidential')]
        [System.String[]]
        $AllowedClientTypes,

        [Parameter()]
        [ValidateSet('NoDevice', 'WorkplaceJoinedDevices', 'AllDevices')]
        [System.String]
        $IssueOAuthRefreshTokensTo,

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

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

    $getTargetResourceParms = @{
        Name                       = $Name
        ApplicationGroupIdentifier = $ApplicationGroupIdentifier
        Identifier                 = $Identifier
    }
    $targetResource = Get-TargetResource @getTargetResourceParms

    if ($targetResource.Ensure -eq 'Present')
    {
        # Resource is Present
        if ($Ensure -eq 'Present')
        {
            # Resource should be Present
            $propertiesNotInDesiredState = @()

            if ($PSBoundParameters.Keys.Contains('IssuanceTransformRules'))
            {
                $propertiesNotInDesiredState += (
                    Compare-IssuanceTransformRule -CurrentValue $targetResource.IssuanceTransformRules `
                        -DesiredValue $IssuanceTransformRules |
                        Where-Object -Property InDesiredState -eq $false)
            }

            $propertiesNotInDesiredState += (
                Compare-ResourcePropertyState -CurrentValues $targetResource -DesiredValues $PSBoundParameters `
                    -IgnoreProperties IssuanceTransformRules | Where-Object -Property InDesiredState -eq $false)

            if ($propertiesNotInDesiredState)
            {
                # Resource is not in desired state
                foreach ($property in $propertiesNotInDesiredState)
                {
                    Write-Verbose -Message (
                        $script:localizedData.ResourcePropertyNotInDesiredStateMessage -f
                        $targetResource.Name, $property.ParameterName, `
                            $property.Expected, $property.Actual)
                }
                $inDesiredState = $false
            }
            else
            {
                # Resource is in desired state
                Write-Verbose -Message ($script:localizedData.ResourceInDesiredStateMessage -f
                    $targetResource.Name)
                $inDesiredState = $true
            }
        }
        else
        {
            # Resource should be Absent
            Write-Verbose -Message ($script:localizedData.ResourceIsPresentButShouldBeAbsentMessage -f
                $targetResource.Name)
            $inDesiredState = $false
        }
    }
    else
    {
        # Resource is Absent
        if ($Ensure -eq 'Present')
        {
            # Resource should be Present
            Write-Verbose -Message ($script:localizedData.ResourceIsAbsentButShouldBePresentMessage -f
                $targetResource.Name)
            $inDesiredState = $false
        }
        else
        {
            # Resource should be Absent
            Write-Verbose -Message ($script:localizedData.ResourceInDesiredStateMessage -f
                $targetResource.Name)
            $inDesiredState = $true
        }
    }

    $inDesiredState
}

Export-ModuleMember -Function *-TargetResource