DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1

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

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

        [Parameter()]
        [System.String]
        [ValidateSet('disabled', 'enabled', 'enabledForReportingButNotEnforced')]
        $State,

        #ConditionalAccessApplicationCondition
        [Parameter()]
        [System.String[]]
        $IncludeApplications,

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

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

        #ConditionalAccessUserCondition
        [Parameter()]
        [System.String[]]
        $IncludeUsers,

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

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

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

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

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

        #ConditionalAccessPlatformCondition
        [Parameter()]
        [System.String[]]
        $IncludePlatforms,

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

        #ConditionalAccessLocationCondition
        [Parameter()]
        [System.String[]]
        $IncludeLocations,

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

        #ConditionalAccessDevicesCondition
        [Parameter()]
        [System.String[]]
        $IncludeDevices,

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

        #Further conditions
        [Parameter()]
        [System.String[]]
        $UserRiskLevels,

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

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

        #ConditionalAccessGrantControls
        [Parameter()]
        [ValidateSet('AND', 'OR')]
        [System.String]
        $GrantControlOperator,

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

        #ConditionalAccessSessionControls
        [Parameter()]
        [System.Boolean]
        $ApplicationEnforcedRestrictionsIsEnabled,

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

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

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

        [Parameter()]
        [ValidateSet('Days', 'Hours', '')]
        [System.String]
        $SignInFrequencyType,

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

        [Parameter()]
        [ValidateSet('Always', 'Never', '')]
        [System.String]
        $PersistentBrowserMode,

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

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

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $GlobalAdminAccount,

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

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

        [Parameter()]
        [System.String]
        $CertificateThumbprint
    )

    Write-Verbose -Message "Getting configuration of AzureAD Conditional Access Policy"
    $ConnectionMode = New-M365DSCConnection -Platform 'AzureAD' -InboundParameters $PSBoundParameters

    #region Telemetry
    $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace("MSFT_", "")
    $data = [System.Collections.Generic.Dictionary[[String], [String]]]::new()
    $data.Add("Resource", $ResourceName)
    $data.Add("Method", $MyInvocation.MyCommand)
    $data.Add("Principal", $GlobalAdminAccount.UserName)
    $data.Add("TenantId", $TenantId)
    $data.Add("ConnectionMode", $ConnectionMode)
    Add-M365DSCTelemetryEvent -Data $data
    #endregion

    if ($PSBoundParameters.ContainsKey("Id"))
    {
        Write-Verbose -Message "PolicyID was specified"
        try
        {
            $Policy = Get-AzureADMSConditionalAccessPolicy -PolicyId $Id
        }
        catch
        {
            $Policy = Get-AzureADMSConditionalAccessPolicy | Where-Object { $_.DisplayName -eq $DisplayName }
            if ($Policy.Length -gt 1)
            {
                throw "Duplicate CA Policies named $DisplayName exist in tenant"
            }
        }
    }
    else
    {
        Write-Verbose -Message "Id was NOT specified"
        ## Can retreive multiple CA Policies since displayname is not unique
        $Policy = Get-AzureADMSConditionalAccessPolicy | Where-Object { $_.DisplayName -eq $DisplayName }
        if ($Policy.Length -gt 1)
        {
            throw "Duplicate CA Policies named $DisplayName exist in tenant"
        }
    }

    if ($null -eq $Policy)
    {
        $currentValues = $PSBoundParameters
        $currentValues.Ensure = "Absent"
        return $currentValues
    }
    else
    {
        Write-Verbose -Message "Get-TargetResource: Found existing Conditional Access policy"
        $PolicyDisplayName = $Policy.DisplayName

        Write-Verbose -Message "Get-TargetResource: Process IncludeUsers"
        #translate IncludeUser GUIDs to UPN, except id value is GuestsOrExternalUsers or All
        $IncludeUsers = @()
        if ($Policy.Conditions.Users.IncludeUsers)
        {
            foreach ($IncludeUserGUID in $Policy.Conditions.Users.IncludeUsers)
            {
                if ($IncludeUserGUID -notin "GuestsOrExternalUsers", "All")
                {
                    $IncludeUser = $null
                    try
                    {
                        $IncludeUser = (Get-AzureADUser -ObjectId $IncludeUserGUID).userprincipalname
                    }
                    catch
                    {
                        try
                        {
                            Write-Verbose -Message $_
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message "Couldn't find user $IncludeUserGUID , that is defined in policy $PolicyDisplayName" -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    if ($IncludeUser)
                    {
                        $IncludeUsers += $IncludeUser
                    }
                }
                else
                {
                    $IncludeUsers += $IncludeUserGUID
                }
            }
        }

        Write-Verbose -Message "Get-TargetResource: Process ExcludeUsers"
        #translate ExcludeUser GUIDs to UPN, except id value is GuestsOrExternalUsers or All
        $ExcludeUsers = @()
        if ($Policy.Conditions.Users.ExcludeUsers)
        {
            foreach ($ExcludeUserGUID in $Policy.Conditions.Users.ExcludeUsers)
            {
                if ($ExcludeUserGUID -notin "GuestsOrExternalUsers", "All")
                {
                    $ExcludeUser = $null
                    try
                    {
                        $ExcludeUser = (Get-AzureADUser -ObjectId $ExcludeUserGUID).userprincipalname
                    }
                    catch
                    {
                        $Message = "Couldn't find user $ExcludeUserGUID , that is defined in policy $PolicyDisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    if ($ExcludeUser)
                    {
                        $ExcludeUsers += $ExcludeUser
                    }
                }
                else
                {
                    $ExcludeUsers += $ExcludeUserGUID
                }
            }
        }

        Write-Verbose -Message "Get-TargetResource: Process IncludeGroups"
        #translate IncludeGroup GUIDs to DisplayName
        $IncludeGroups = @()
        if ($Policy.Conditions.Users.IncludeGroups)
        {
            foreach ($IncludeGroupGUID in $Policy.Conditions.Users.IncludeGroups)
            {
                $IncludeGroup = $null
                try
                {
                    $IncludeGroup = (Get-AzureADGroup -ObjectId $IncludeGroupGUID).displayname
                }
                catch
                {
                    $Message = "Couldn't find Group $IncludeGroupGUID , that is defined in policy $PolicyDisplayName"
                    try
                    {
                        Write-Verbose -Message $Message
                        $tenantIdValue = ""
                        if (-not [System.String]::IsNullOrEmpty($TenantId))
                        {
                            $tenantIdValue = $TenantId
                        }
                        elseif ($null -ne $GlobalAdminAccount)
                        {
                            $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                        }
                        Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                            -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                            -TenantId $tenantIdValue
                    }
                    catch
                    {
                        Write-Verbose -Message $_
                    }
                }
                if ($IncludeGroup)
                {
                    $IncludeGroups += $IncludeGroup
                }
            }
        }

        Write-Verbose -Message "Get-TargetResource: Process ExcludeGroups"
        #translate ExcludeGroup GUIDs to DisplayName
        $ExcludeGroups = @()
        if ($Policy.Conditions.Users.ExcludeGroups)
        {
            foreach ($ExcludeGroupGUID in $Policy.Conditions.Users.ExcludeGroups)
            {
                $ExcludeGroup = $null
                try
                {
                    $ExcludeGroup = (Get-AzureADGroup -ObjectId $ExcludeGroupGUID).displayname
                }
                catch
                {
                    $Message = "Couldn't find Group $ExcludeGroupGUID , that is defined in policy $PolicyDisplayName"
                    try
                    {
                        Write-Verbose -Message $Message
                        $tenantIdValue = ""
                        if (-not [System.String]::IsNullOrEmpty($TenantId))
                        {
                            $tenantIdValue = $TenantId
                        }
                        elseif ($null -ne $GlobalAdminAccount)
                        {
                            $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                        }
                        Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                            -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                            -TenantId $tenantIdValue
                    }
                    catch
                    {
                        Write-Verbose -Message $_
                    }
                }
                if ($ExcludeGroup)
                {
                    $ExcludeGroups += $ExcludeGroup
                }
            }
        }


        $IncludeRoles = @()
        $ExcludeRoles = @()
        #translate role template guids to role name
        if ($Policy.Conditions.Users.IncludeRoles -or $Policy.Conditions.Users.ExcludeRoles)
        {
            Write-Verbose -Message "Get-TargetResource: Role condition defined, processing"
            #build role translation table
            $rolelookup = @{}
            foreach ($role in Get-AzureADDirectoryRoleTemplate)
            {
                $rolelookup[$role.ObjectId] = $role.DisplayName
            }

            Write-Verbose -Message "Get-TargetResource: Processing IncludeRoles"
            if ($Policy.Conditions.Users.IncludeRoles)
            {
                foreach ($IncludeRoleGUID in $Policy.Conditions.Users.IncludeRoles)
                {
                    if ($null -eq $rolelookup[$IncludeRoleGUID])
                    {
                        $Message = "Couldn't find role $IncludeRoleGUID , couldn't add to policy $PolicyDisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $IncludeRoles += $rolelookup[$IncludeRoleGUID]
                    }
                }
            }

            Write-Verbose -Message "Get-TargetResource: Processing ExcludeRoles"
            if ($Policy.Conditions.Users.ExcludeRoles)
            {
                foreach ($ExcludeRoleGUID in $Policy.Conditions.Users.ExcludeRoles)
                {
                    if ($null -eq $rolelookup[$ExcludeRoleGUID])
                    {
                        $Message = "Couldn't find role $ExcludeRoleGUID , couldn't add to policy $PolicyDisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $ExcludeRoles += $rolelookup[$ExcludeRoleGUID]
                    }
                }
            }

        }

        $IncludeLocations = @()
        $ExcludeLocations = @()
        #translate Location template guids to Location name
        if ($Policy.Conditions.Locations)
        {
            Write-Verbose -Message "Get-TargetResource: Location condition defined, processing"
            #build Location translation table
            $Locationlookup = @{}
            foreach ($Location in Get-AzureADMSNamedLocationPolicy)
            {
                $Locationlookup[$Location.Id] = $Location.DisplayName
            }

            Write-Verbose -Message "Get-TargetResource: Processing IncludeLocations"
            if ($Policy.Conditions.Locations.IncludeLocations)
            {
                foreach ($IncludeLocationGUID in $Policy.Conditions.Locations.IncludeLocations)
                {
                    if ($IncludeLocationGUID -in "All", "AllTrusted")
                    {
                        $IncludeLocations += $IncludeLocationGUID
                    }
                    elseif ($null -eq $Locationlookup[$IncludeLocationGUID])
                    {
                        $Message = "Couldn't find Location $IncludeLocationGUID , couldn't add to policy $PolicyDisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $IncludeLocations += $Locationlookup[$IncludeLocationGUID]
                    }
                }
            }

            Write-Verbose -Message "Get-TargetResource: Processing ExcludeLocations"
            if ($Policy.Conditions.Locations.ExcludeLocations)
            {
                foreach ($ExcludeLocationGUID in $Policy.Conditions.Locations.ExcludeLocations)
                {
                    if ($ExcludeLocationGUID -in "All", "AllTrusted")
                    {
                        $ExcludeLocations += $ExcludeLocationGUID
                    }
                    elseif ($null -eq $Locationlookup[$ExcludeLocationGUID])
                    {
                        $Message = "Couldn't find Location $ExcludeLocationGUID , couldn't add to policy $PolicyDisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $ExcludeLocations += $Locationlookup[$ExcludeLocationGUID]
                    }
                }
            }


        }
        if ($Policy.SessionControls.CloudAppSecurity.IsEnabled)
        {
            $CloudAppSecurityType = [System.String]$Policy.SessionControls.CloudAppSecurity.CloudAppSecurityType
        }
        else
        {
            $CloudAppSecurityType = $null
        }
        if ($Policy.SessionControls.SignInFrequency.IsEnabled)
        {
            $SignInFrequencyType = [System.String]$Policy.SessionControls.SignInFrequency.Type
        }
        else
        {
            $SignInFrequencyType = $null
        }
        if ($Policy.SessionControls.PersistentBrowser.IsEnabled)
        {
            $PersistentBrowserMode = [System.String]$Policy.SessionControls.PersistentBrowser.Mode
        }
        else
        {
            $PersistentBrowserMode = $null
        }
        $result = @{
            DisplayName                              = $Policy.DisplayName
            Id                                       = $Policy.Id
            State                                    = $Policy.State
            IncludeApplications                      = [System.String[]](@() + $Policy.Conditions.Applications.IncludeApplications)
            #no translation of Application GUIDs, return empty string array if undefined
            ExcludeApplications                      = [System.String[]](@() + $Policy.Conditions.Applications.ExcludeApplications)
            #no translation of GUIDs, return empty string array if undefined
            IncludeUserActions                       = [System.String[]](@() + $Policy.Conditions.Applications.IncludeUserActions)
            #no translation needed, return empty string array if undefined
            IncludeUsers                             = $IncludeUsers
            ExcludeUsers                             = $ExcludeUsers
            IncludeGroups                            = $IncludeGroups
            ExcludeGroups                            = $ExcludeGroups
            IncludeRoles                             = $IncludeRoles
            ExcludeRoles                             = $ExcludeRoles

            IncludePlatforms                         = [System.String[]](@() + $Policy.Conditions.Platforms.IncludePlatforms)
            #no translation needed, return empty string array if undefined
            ExcludePlatforms                         = [System.String[]](@() + $Policy.Conditions.Platforms.ExcludePlatforms)
            #no translation needed, return empty string array if undefined
            IncludeLocations                         = $IncludeLocations
            ExcludeLocations                         = $ExcludeLocations
            IncludeDevices                           = [System.String[]](@() + $Policy.Conditions.Devices.IncludeDevices)
            #no translation needed, return empty string array if undefined
            ExcludeDevices                           = [System.String[]](@() + $Policy.Conditions.Devices.ExcludeDevices)
            #no translation needed, return empty string array if undefined
            UserRiskLevels                           = [System.String[]](@() + $Policy.Conditions.UserRiskLevels)
            #no translation needed, return empty string array if undefined
            SignInRiskLevels                         = [System.String[]](@() + $Policy.Conditions.SignInRiskLevels)
            #no translation needed, return empty string array if undefined
            ClientAppTypes                           = [System.String[]](@() + $Policy.Conditions.ClientAppTypes)
            #no translation needed, return empty string array if undefined
            GrantControlOperator                     = $Policy.GrantControls._Operator
            #no translation or conversion needed
            BuiltInControls                          = [System.String[]](@() + $Policy.GrantControls.BuiltInControls)
            #no translation needed, return empty string array if undefined
            ApplicationEnforcedRestrictionsIsEnabled = $false -or $Policy.SessionControls.ApplicationEnforcedRestrictions.IsEnabled
            #make false if undefined, true if true
            CloudAppSecurityIsEnabled                = $false -or $Policy.SessionControls.CloudAppSecurity.IsEnabled
            #make false if undefined, true if true
            CloudAppSecurityType                     = [System.String]$Policy.SessionControls.CloudAppSecurity.CloudAppSecurityType
            #no translation needed, return empty string array if undefined
            SignInFrequencyIsEnabled                 = $false -or $Policy.SessionControls.SignInFrequency.IsEnabled
            #make false if undefined, true if true
            SignInFrequencyValue                     = $Policy.SessionControls.SignInFrequency.Value
            #no translation or conversion needed, $null returned if undefined
            SignInFrequencyType                      = [System.String]$Policy.SessionControls.SignInFrequency.Type
            #no translation needed
            PersistentBrowserIsEnabled               = $false -or $Policy.SessionControls.PersistentBrowser.IsEnabled
            #make false if undefined, true if true
            PersistentBrowserMode                    = [System.String]$Policy.SessionControls.PersistentBrowser.Mode
            #no translation needed
            #Standard part
            Ensure                                   = "Present"
            GlobalAdminAccount                       = $GlobalAdminAccount
            ApplicationId                            = $ApplicationId
            TenantId                                 = $TenantId
            CertificateThumbprint                    = $CertificateThumbprint
        }
        Write-Verbose -Message "Get-TargetResource Result: `n $(Convert-M365DscHashtableToString -Hashtable $result)"
        return $result
    }
}

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $DisplayName,

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

        [Parameter()]
        [System.String]
        [ValidateSet('disabled', 'enabled', 'enabledForReportingButNotEnforced')]
        $State,

        #ConditionalAccessApplicationCondition
        [Parameter()]
        [System.String[]]
        $IncludeApplications,

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

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

        #ConditionalAccessUserCondition
        [Parameter()]
        [System.String[]]
        $IncludeUsers,

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

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

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

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

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

        #ConditionalAccessPlatformCondition
        [Parameter()]
        [System.String[]]
        $IncludePlatforms,

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

        #ConditionalAccessLocationCondition
        [Parameter()]
        [System.String[]]
        $IncludeLocations,

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

        #ConditionalAccessDevicesCondition
        [Parameter()]
        [System.String[]]
        $IncludeDevices,

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

        #Further conditions
        [Parameter()]
        [System.String[]]
        $UserRiskLevels,

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

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

        #ConditionalAccessGrantControls
        [Parameter()]
        [ValidateSet('AND', 'OR')]
        [System.String]
        $GrantControlOperator,

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

        #ConditionalAccessSessionControls
        [Parameter()]
        [System.Boolean]
        $ApplicationEnforcedRestrictionsIsEnabled,

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

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

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

        [Parameter()]
        [ValidateSet('Days', 'Hours', '')]
        [System.String]
        $SignInFrequencyType,

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

        [Parameter()]
        [ValidateSet('Always', 'Never', '')]
        [System.String]
        $PersistentBrowserMode,

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

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

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $GlobalAdminAccount,

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

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

        [Parameter()]
        [System.String]
        $CertificateThumbprint
    )

    Write-Verbose -Message "Set-Targetresource: Start processing"
    Write-Verbose -Message "Set-Targetresource: Starting telemetry"
    #region Telemetry
    $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace("MSFT_", "")
    $data = [System.Collections.Generic.Dictionary[[String], [String]]]::new()
    $data.Add("Resource", $ResourceName)
    $data.Add("Method", $MyInvocation.MyCommand)
    $data.Add("Principal", $GlobalAdminAccount.UserName)
    $data.Add("TenantId", $TenantId)
    Add-M365DSCTelemetryEvent -Data $data
    #endregion
    Write-Verbose -Message "Set-Targetresource: Finished telemetry"
    Write-Verbose -Message "Set-Targetresource: Running Get-TargetResource"
    $currentPolicy = Get-TargetResource @PSBoundParameters
    Write-Verbose -Message "Set-Targetresource: Cleaning up parameters"
    $currentParameters = $PSBoundParameters
    $currentParameters.Remove("ApplicationId") | Out-Null
    $currentParameters.Remove("TenantId") | Out-Null
    $currentParameters.Remove("CertificateThumbprint") | Out-Null
    $currentParameters.Remove("GlobalAdminAccount") | Out-Null
    $currentParameters.Remove("Ensure") | Out-Null

    if ($Ensure -eq 'Present')#create policy attribute objects
    {
        Write-Verbose -Message "Set-Targetresource: Policy $Displayname Ensure Present"
        $NewParameters = @{}
        $NewParameters.Add("DisplayName", $DisplayName)
        $NewParameters.Add("State", $State)
        #create Conditions object
        Write-Verbose -Message "Set-Targetresource: create Conditions object"
        $conditions = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessConditionSet
        #create and provision Application Condition object
        Write-Verbose -Message "Set-Targetresource: create Application Condition object"
        $conditions.Applications = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessApplicationCondition
        $conditions.Applications.IncludeApplications = $IncludeApplications
        $conditions.Applications.ExcludeApplications = $ExcludeApplications
        $conditions.Applications.IncludeUserActions = $IncludeUserActions
        #create and provision User Condition object
        Write-Verbose -Message "Set-Targetresource: create and provision User Condition object"
        $conditions.Users = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessUserCondition
        Write-Verbose -Message "Set-Targetresource: process includeusers"
        $conditions.Users.IncludeUsers = @()
        foreach ($includeuser in $IncludeUsers)
        {
            #translate user UPNs to GUID, except id value is GuestsOrExternalUsers or All
            if ($includeuser)
            {
                if ($includeuser -notin "GuestsOrExternalUsers", "All")
                {
                    $userguid = $null
                    try
                    { $userguid = (Get-AzureADUser -ObjectId $includeuser).ObjectId
                    }
                    catch
                    {
                        $Message = $_
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    if ($null -eq $userguid)
                    {
                        $Message = "Couldn't find user $includeuser , couldn't add to policy $DisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $conditions.Users.IncludeUsers += $userguid
                    }
                }
                else
                {
                    $conditions.Users.IncludeUsers += $includeuser
                }
            }
        }
        Write-Verbose -Message "Set-Targetresource: process excludeusers"
        $conditions.Users.ExcludeUsers = @()
        foreach ($excludeuser in $ExcludeUsers)
        {
            #translate user UPNs to GUID, except id value is GuestsOrExternalUsers or All
            if ($excludeuser)
            {
                if ($excludeuser -notin "GuestsOrExternalUsers", "All")
                {
                    $userguid = $null
                    try
                    { $userguid = (Get-AzureADUser -ObjectId $excludeuser).ObjectId
                    }
                    catch
                    {
                        $Message = $_
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    if ($null -eq $userguid)
                    {
                        $Message = "Couldn't find user $excludeuser , couldn't add to policy $DisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $conditions.Users.ExcludeUsers += $userguid
                    }
                }
                else
                {
                    $conditions.Users.ExcludeUsers += $excludeuser
                }
            }
        }
        Write-Verbose -Message "Set-Targetresource: process includegroups"
        $conditions.Users.IncludeGroups = @()
        foreach ($includegroup in $IncludeGroups)
        {
            #translate user Group names to GUID
            if ($includegroup)
            {
                $GroupLookup = $null
                try
                { $GroupLookup = Get-AzureADGroup -Filter "DisplayName eq '$includegroup'"
                }
                catch
                {
                    $Message = $_
                    try
                    {
                        Write-Verbose -Message $Message
                        $tenantIdValue = ""
                        if (-not [System.String]::IsNullOrEmpty($TenantId))
                        {
                            $tenantIdValue = $TenantId
                        }
                        elseif ($null -ne $GlobalAdminAccount)
                        {
                            $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                        }
                        Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                            -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                            -TenantId $tenantIdValue
                    }
                    catch
                    {
                        Write-Verbose -Message $_
                    }

                    Write-Verbose -Message $_
                }
                if ($GroupLookup.Length -gt 1)
                {
                    $Message = "Duplicate group found with displayname $includegroup , couldn't add to policy $DisplayName"
                    try
                    {
                        Write-Verbose -Message $Message
                        $tenantIdValue = ""
                        if (-not [System.String]::IsNullOrEmpty($TenantId))
                        {
                            $tenantIdValue = $TenantId
                        }
                        elseif ($null -ne $GlobalAdminAccount)
                        {
                            $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                        }
                        Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                            -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                            -TenantId $tenantIdValue
                    }
                    catch
                    {
                        Write-Verbose -Message $_
                    }
                }
                elseif ($null -eq $GroupLookup)
                {
                    $Message = "Couldn't find group $includegroup , couldn't add to policy $DisplayName"
                    try
                    {
                        Write-Verbose -Message $Message
                        $tenantIdValue = ""
                        if (-not [System.String]::IsNullOrEmpty($TenantId))
                        {
                            $tenantIdValue = $TenantId
                        }
                        elseif ($null -ne $GlobalAdminAccount)
                        {
                            $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                        }
                        Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                            -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                            -TenantId $tenantIdValue
                    }
                    catch
                    {
                        Write-Verbose -Message $_
                    }

                }
                else
                {
                    Write-Verbose -Message "adding group to includegroups"
                    $conditions.Users.IncludeGroups += $GroupLookup.ObjectId
                }
            }
        }
        $conditions.Users.ExcludeGroups = @()
        Write-Verbose -Message "Set-Targetresource: process excludegroups"
        foreach ($ExcludeGroup in $ExcludeGroups)
        {
            #translate user Group names to GUID
            if ($ExcludeGroup)
            {
                $GroupLookup = $null
                try
                { $GroupLookup = Get-AzureADGroup -Filter "DisplayName eq '$ExcludeGroup'"
                }
                catch
                {
                    $Message = $_
                    try
                    {
                        Write-Verbose -Message $Message
                        $tenantIdValue = ""
                        if (-not [System.String]::IsNullOrEmpty($TenantId))
                        {
                            $tenantIdValue = $TenantId
                        }
                        elseif ($null -ne $GlobalAdminAccount)
                        {
                            $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                        }
                        Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                            -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                            -TenantId $tenantIdValue
                    }
                    catch
                    {
                        Write-Verbose -Message $_
                    }
                    Write-Verbose -Message $_
                }
                if ($GroupLookup.Length -gt 1)
                {
                    $Message = "Duplicate group found with displayname $ExcludeGroup , couldn't add to policy $DisplayName"
                    try
                    {
                        Write-Verbose -Message $Message
                        $tenantIdValue = ""
                        if (-not [System.String]::IsNullOrEmpty($TenantId))
                        {
                            $tenantIdValue = $TenantId
                        }
                        elseif ($null -ne $GlobalAdminAccount)
                        {
                            $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                        }
                        Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                            -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                            -TenantId $tenantIdValue
                    }
                    catch
                    {
                        Write-Verbose -Message $_
                    }

                }
                elseif ($null -eq $GroupLookup)
                {
                    $Message = "Couldn't find group $ExcludeGroup , couldn't add to policy $DisplayName"
                    try
                    {
                        Write-Verbose -Message $Message
                        $tenantIdValue = ""
                        if (-not [System.String]::IsNullOrEmpty($TenantId))
                        {
                            $tenantIdValue = $TenantId
                        }
                        elseif ($null -ne $GlobalAdminAccount)
                        {
                            $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                        }
                        Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                            -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                            -TenantId $tenantIdValue
                    }
                    catch
                    {
                        Write-Verbose -Message $_
                    }

                }
                else
                {
                    Write-Verbose -Message "adding group to ExcludeGroups"
                    $conditions.Users.ExcludeGroups += $GroupLookup.ObjectId
                }
            }
        }
        Write-Verbose -Message "Set-Targetresource: process includeroles"
        $conditions.Users.IncludeRoles = @()
        if ($IncludeRoles)
        {
            #translate role names to template guid if defined
            $rolelookup = @{}
            foreach ($role in Get-AzureADDirectoryRoleTemplate)
            { $rolelookup[$role.DisplayName] = $role.ObjectId
            }
            foreach ($IncludeRole in $IncludeRoles)
            {
                if ($IncludeRole)
                {
                    if ($null -eq $rolelookup[$IncludeRole])
                    {
                        $Message = "Couldn't find role $IncludeRole , couldn't add to policy $DisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $conditions.Users.IncludeRoles += $rolelookup[$IncludeRole]
                    }
                }
            }
        }
        Write-Verbose -Message "Set-Targetresource: process excluderoles"
        $conditions.Users.ExcludeRoles = @()
        if ($ExcludeRoles)
        {
            #translate role names to template guid if defined
            $rolelookup = @{}
            foreach ($role in Get-AzureADDirectoryRoleTemplate)
            { $rolelookup[$role.DisplayName] = $role.ObjectId
            }
            foreach ($ExcludeRole in $ExcludeRoles)
            {
                if ($ExcludeRole)
                {
                    if ($null -eq $rolelookup[$ExcludeRole])
                    {
                        $Message = "Couldn't find role $ExcludeRole , couldn't add to policy $DisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $conditions.Users.ExcludeRoles += $rolelookup[$ExcludeRole]
                    }

                }
            }
        }
        Write-Verbose -Message "Set-Targetresource: process platform condition"
        if ($IncludePlatforms -or $ExcludePlatforms)
        {
            #create and provision Platform condition object if used
            $conditions.Platforms = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessPlatformCondition
            Write-Verbose -Message "Set-Targetresource: IncludePlatforms: $IncludePlatforms"
            $conditions.Platforms.IncludePlatforms = @() + $IncludePlatforms
            #no translation or conversion needed
            Write-Verbose -Message "Set-Targetresource: ExcludePlatforms: $ExcludePlatforms"
            $conditions.Platforms.ExcludePlatforms = @() + $ExcludePlatforms
            #no translation or conversion needed
        }
        else
        {
            Write-Verbose -Message "Set-Targetresource: setting platform condition to null"
            $conditions.Platforms = $null
        }
        Write-Verbose -Message "Set-Targetresource: process include and exclude locations"
        if ($IncludeLocations -or $ExcludeLocations)
        {
            $conditions.Locations = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessLocationCondition
            $conditions.Locations.IncludeLocations = @()
            $conditions.Locations.ExcludeLocations = @()
            Write-Verbose -Message "Set-Targetresource: locations specified"
            #create and provision Location condition object if used, translate Location names to guid
            $LocationLookup = @{}
            foreach ($Location in Get-AzureADMSNamedLocationPolicy)
            {
                $LocationLookup[$Location.DisplayName] = $Location.Id
            }
            foreach ($IncludeLocation in $IncludeLocations)
            {
                if ($IncludeLocation)
                {
                    if ($IncludeLocation -in "All", "AllTrusted")
                    {
                        $conditions.Locations.IncludeLocations += $IncludeLocation
                    }
                    elseif ($null -eq $LocationLookup[$IncludeLocation])
                    {
                        $Message = "Couldn't find Location $IncludeLocation , couldn't add to policy $DisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $conditions.Locations.IncludeLocations += $LocationLookup[$IncludeLocation]
                    }
                }
            }
            foreach ($ExcludeLocation in $ExcludeLocations)
            {
                if ($ExcludeLocation)
                {
                    if ($ExcludeLocation -eq "All" -or $ExcludeLocation -eq "AllTrusted")
                    {
                        $conditions.Locations.ExcludeLocations += $ExcludeLocation
                    }
                    elseif ($null -eq $LocationLookup[$ExcludeLocation])
                    {
                        $Message = "Couldn't find Location $ExcludeLocation , couldn't add to policy $DisplayName"
                        try
                        {
                            Write-Verbose -Message $Message
                            $tenantIdValue = ""
                            if (-not [System.String]::IsNullOrEmpty($TenantId))
                            {
                                $tenantIdValue = $TenantId
                            }
                            elseif ($null -ne $GlobalAdminAccount)
                            {
                                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                            }
                            Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                                -TenantId $tenantIdValue
                        }
                        catch
                        {
                            Write-Verbose -Message $_
                        }
                    }
                    else
                    {
                        $conditions.Locations.ExcludeLocations += $LocationLookup[$ExcludeLocation]
                    }
                }
            }
        }

        Write-Verbose -Message "Set-Targetresource: process device states"
        if ($IncludeDevices -or $ExcludeDevices)
        {
            #create and provision Device condition object if used
            $conditions.Devices = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessDevicesCondition
            $conditions.Devices.IncludeDevices = $IncludeDevices
            #no translation or conversion needed
            $conditions.Devices.ExcludeDevices = $ExcludeDevices
            #no translation or conversion needed
        }
        Write-Verbose -Message "Set-Targetresource: process risk levels and app types"
        Write-Verbose -Message "Set-Targetresource: UserRiskLevels: $UserRiskLevels"
        $Conditions.UserRiskLevels = $UserRiskLevels
        #no translation or conversion needed
        Write-Verbose -Message "Set-Targetresource: SignInRiskLevels: $SignInRiskLevels"
        $Conditions.SignInRiskLevels = $SignInRiskLevels
        #no translation or conversion needed
        Write-Verbose -Message "Set-Targetresource: ClientAppTypes: $ClientAppTypes"
        $Conditions.ClientAppTypes = $ClientAppTypes
        #no translation or conversion needed
        Write-Verbose -Message "Set-Targetresource: Adding processed conditions"
        #add all conditions to the parameter list
        $NewParameters.Add("Conditions", $Conditions)
        #create and provision Grant Control object
        Write-Verbose -Message "Set-Targetresource: create and provision Grant Control object"
        $GrantControls = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessGrantControls
        $GrantControls._Operator = $GrantControlOperator
        $GrantControls.BuiltInControls = $BuiltInControls
        #no translation or conversion needed
        Write-Verbose -Message "Set-Targetresource: Adding processed grant controls"
        $NewParameters.Add("GrantControls", $GrantControls)
        #add GrantControls to the parameter list
        Write-Verbose -Message "Set-Targetresource: process session controls"

        $sessioncontrols = $null
        if ($ApplicationEnforcedRestrictionsIsEnabled -or $CloudAppSecurityIsEnabled -or $SignInFrequencyIsEnabled -or $PersistentBrowserIsEnabled)
        {
            Write-Verbose -Message "Set-Targetresource: create provision Session Control object"
            $sessioncontrols = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessSessionControls

            if ($ApplicationEnforcedRestrictionsIsEnabled)
            {
                #create and provision ApplicationEnforcedRestrictions object if used
                $sessioncontrols.ApplicationEnforcedRestrictions = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessApplicationEnforcedRestrictions
                $sessioncontrols.ApplicationEnforcedRestrictions.IsEnabled = $true
            }
            if ($CloudAppSecurityIsEnabled)
            {
                #create and provision CloudAppSecurity object if used
                $sessioncontrols.CloudAppSecurity = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessCloudAppSecurity
                $sessioncontrols.CloudAppSecurity.IsEnabled = $true
                $sessioncontrols.CloudAppSecurity.CloudAppSecurityType = $CloudAppSecurityType
            }
            if ($SignInFrequencyIsEnabled)
            {
                #create and provision SignInFrequency object if used
                $sessioncontrols.SignInFrequency = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessSignInFrequency
                $sessioncontrols.SignInFrequency.IsEnabled = $true
                $sessioncontrols.SignInFrequency.Type = $SignInFrequencyType
                $sessioncontrols.SignInFrequency.Value = $SignInFrequencyValue
            }
            if ($PersistentBrowserIsEnabled)
            {
                Write-Verbose -Message "Set-Targetresource: Persistent Browser settings defined: PersistentBrowserIsEnabled:$PersistentBrowserIsEnabled, PersistentBrowserMode:$PersistentBrowserMode"
                #create and provision PersistentBrowser object if used
                $sessioncontrols.PersistentBrowser = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessPersistentBrowser
                $sessioncontrols.PersistentBrowser.IsEnabled = $true
                $sessioncontrols.PersistentBrowser.Mode = $PersistentBrowserMode
            }
        }
        $NewParameters.Add("SessionControls", $sessioncontrols)
        #add SessionControls to the parameter list
    }
    if ($Ensure -eq 'Present' -and $currentPolicy.Ensure -eq 'Present')
    {
        Write-Verbose -Message "Set-Targetresource: Change policy $DisplayName"
        $NewParameters.Add("PolicyId", $currentPolicy.Id)
        try
        {
            Set-AzureADMSConditionalAccessPolicy @NewParameters
        }
        catch
        {
            $Message = $_
            try
            {
                Write-Verbose -Message $Message
                $tenantIdValue = ""
                if (-not [System.String]::IsNullOrEmpty($TenantId))
                {
                    $tenantIdValue = $TenantId
                }
                elseif ($null -ne $GlobalAdminAccount)
                {
                    $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                }
                Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                    -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                    -TenantId $tenantIdValue
            }
            catch
            {
                Write-Verbose -Message $_
            }

            Write-Verbose -Message "Set-Targetresource: Failed change policy $DisplayName"
            Write-Verbose -Message $_
        }
    }
    elseif ($Ensure -eq 'Present' -and $currentPolicy.Ensure -eq 'Absent')
    {
        Write-Verbose -Message "Set-Targetresource: create policy $DisplayName"
        try
        {
            New-AzureADMSConditionalAccessPolicy @NewParameters
        }
        catch
        {
            $Message = $_
            try
            {
                Write-Verbose -Message $Message
                $tenantIdValue = ""
                if (-not [System.String]::IsNullOrEmpty($TenantId))
                {
                    $tenantIdValue = $TenantId
                }
                elseif ($null -ne $GlobalAdminAccount)
                {
                    $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                }
                Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                    -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                    -TenantId $tenantIdValue
            }
            catch
            {
                Write-Verbose -Message $_
            }

            Write-Verbose -Message "Set-Targetresource: Failed creating policy"
            Write-Verbose -Message $_
        }
    }
    elseif ($Ensure -eq 'Absent' -and $currentPolicy.Ensure -eq 'Present')
    {
        Write-Verbose -Message "Set-Targetresource: delete policy $DisplayName"
        try
        {
            Remove-AzureADMSConditionalAccessPolicy -PolicyId $currentPolicy.ID
        }
        catch
        {
            $Message = $_
            try
            {
                Write-Verbose -Message $Message
                $tenantIdValue = ""
                if (-not [System.String]::IsNullOrEmpty($TenantId))
                {
                    $tenantIdValue = $TenantId
                }
                elseif ($null -ne $GlobalAdminAccount)
                {
                    $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
                }
                Add-M365DSCEvent -Message $Message -EntryType 'Error' `
                    -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                    -TenantId $tenantIdValue
            }
            catch
            {
                Write-Verbose -Message $_
            }

            Write-Verbose -Message "Set-Targetresource: Failed deleting policy $DisplayName"
            Write-Verbose -Message $_
        }
    }
    Write-Verbose -Message "Set-Targetresource: finished processing Policy $Displayname"
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $DisplayName,

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

        [Parameter()]
        [System.String]
        [ValidateSet('disabled', 'enabled', 'enabledForReportingButNotEnforced')]
        $State,

        #ConditionalAccessApplicationCondition
        [Parameter()]
        [System.String[]]
        $IncludeApplications,

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

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

        #ConditionalAccessUserCondition
        [Parameter()]
        [System.String[]]
        $IncludeUsers,

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

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

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

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

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

        #ConditionalAccessPlatformCondition
        [Parameter()]
        [System.String[]]
        $IncludePlatforms,

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

        #ConditionalAccessLocationCondition
        [Parameter()]
        [System.String[]]
        $IncludeLocations,

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

        #ConditionalAccessDevicesCondition
        [Parameter()]
        [System.String[]]
        $IncludeDevices,

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

        #Further conditions
        [Parameter()]
        [System.String[]]
        $UserRiskLevels,

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

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

        #ConditionalAccessGrantControls
        [Parameter()]
        [ValidateSet('AND', 'OR')]
        [System.String]
        $GrantControlOperator,

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

        #ConditionalAccessSessionControls
        [Parameter()]
        [System.Boolean]
        $ApplicationEnforcedRestrictionsIsEnabled,

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

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

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

        [Parameter()]
        [ValidateSet('Days', 'Hours', '')]
        [System.String]
        $SignInFrequencyType,

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

        [Parameter()]
        [ValidateSet('Always', 'Never', '')]
        [System.String]
        $PersistentBrowserMode,

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

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

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $GlobalAdminAccount,

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

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

        [Parameter()]
        [System.String]
        $CertificateThumbprint
    )

    Write-Verbose -Message "Testing configuration of AzureAD CA Policies"

    $CurrentValues = Get-TargetResource @PSBoundParameters

    Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)"

    $ValuesToCheck = $PSBoundParameters
    $ValuesToCheck.Remove('GlobalAdminAccount') | Out-Null
    $ValuesToCheck.Remove('Id') | Out-Null

    $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues `
        -Source $($MyInvocation.MyCommand.Source) `
        -DesiredValues $PSBoundParameters `
        -ValuesToCheck $ValuesToCheck.Keys

    Write-Verbose -Message "Test-TargetResource returned $TestResult"

    return $TestResult
}

function Export-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter()]
        [System.Management.Automation.PSCredential]
        $GlobalAdminAccount,

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

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

        [Parameter()]
        [System.String]
        $CertificateThumbprint
    )
    #region Telemetry
    $ConnectionMode = New-M365DSCConnection -Platform 'AzureAD' -InboundParameters $PSBoundParameters

    $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace("MSFT_", "")
    $data = [System.Collections.Generic.Dictionary[[String], [String]]]::new()
    $data.Add("Resource", $ResourceName)
    $data.Add("Method", $MyInvocation.MyCommand)
    $data.Add("Principal", $GlobalAdminAccount.UserName)
    $data.Add("TenantId", $TenantId)
    $data.Add("ConnectionMode", $ConnectionMode)
    Add-M365DSCTelemetryEvent -Data $data
    #endregion

    try
    {
        [array] $Policies = Get-AzureADMSConditionalAccessPolicy
        $i = 1
        $dscContent = ''

        if ($Policies.Length -eq 0)
        {
            Write-Host $Global:M365DSCEmojiGreenCheckMark
        }
        else
        {
            Write-Host "`r`n" -NoNewline
            foreach ($Policy in $Policies)
            {
                Write-Host " |---[$i/$($Policies.Count)] $($Policy.DisplayName)" -NoNewline
                $Params = @{
                    GlobalAdminAccount    = $GlobalAdminAccount
                    DisplayName           = $Policy.DisplayName
                    Id                    = $Policy.Id
                    ApplicationId         = $ApplicationId
                    TenantId              = $TenantId
                    CertificateThumbprint = $CertificateThumbprint
                }
                $Results = Get-TargetResource @Params
                $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode `
                    -Results $Results
                $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName `
                    -ConnectionMode $ConnectionMode `
                    -ModulePath $PSScriptRoot `
                    -Results $Results `
                    -GlobalAdminAccount $GlobalAdminAccount

                $dscContent += $currentDSCBlock
                Save-M365DSCPartialExport -Content $currentDSCBlock `
                    -FileName $Global:PartialExportFileName
                Write-Host $Global:M365DSCEmojiGreenCheckMark
                $i++
            }
        }

        return $dscContent
    }
    catch
    {
        try
        {
            Write-Verbose -Message $_
            $tenantIdValue = ""
            if (-not [System.String]::IsNullOrEmpty($TenantId))
            {
                $tenantIdValue = $TenantId
            }
            elseif ($null -ne $GlobalAdminAccount)
            {
                $tenantIdValue = $GlobalAdminAccount.UserName.Split('@')[1]
            }
            Add-M365DSCEvent -Message $_ -EntryType 'Error' `
                -EventID 1 -Source $($MyInvocation.MyCommand.Source) `
                -TenantId $tenantIdValue
        }
        catch
        {
            Write-Verbose -Message $_
        }
        return ""
    }
}

Export-ModuleMember -Function *-TargetResource