From eb4d182565b22030f61b7b10b2af044f65558d3a Mon Sep 17 00:00:00 2001 From: Sai Rohit Date: Thu, 17 Oct 2024 10:52:06 +0530 Subject: [PATCH 1/4] added mof and psm1 for Custom authentication extension --- ...MSFT_AADCustomAuthenticationExtension.psm1 | 590 ++++++++++++++++++ ...ADCustomAuthenticationExtension.schema.mof | 37 ++ .../readme.md | 6 + .../settings.json | 32 + 4 files changed, 665 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/settings.json diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 new file mode 100644 index 0000000000..a81c729192 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 @@ -0,0 +1,590 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + [ValidateSet( + '#microsoft.graph.onTokenIssuanceStartCustomExtension', + '#microsoft.graph.onAttributeCollectionStartCustomExtension', + '#microsoft.graph.onAttributeCollectionStartCustomExtension' + )] + $CustomAuthenticationExtensionType, + + [Parameter()] + [System.String] + [ValidateSet( + '#microsoft.graph.azureAdTokenAuthentication', + '#microsoft.graph.azureAdPopTokenAuthentication' + )] + $AuthenticationConfigurationType, + + [Parameter()] + [System.String] + $AuthenticationConfigurationResourceId, + + [Parameter()] + [System.Int32] + $ClientConfigurationTimeoutMillisesonds, + + [Parameter()] + [System.Int32] + $ClientConfigurationMaximumRetries, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EndPointConfiguration, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $ClaimsForTokenConfguration, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + ) + + New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters | Out-Null + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $nullResult = $PSBoundParameters + $nullResult.Ensure = 'Absent' + try + { + # check for export. + if ($null -ne $Script:exportedInstances -and $Script:ExportMode) + { + # check with Id first + if (-not [System.String]::IsNullOrEmpty($Id)) + { + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.Id -eq $Id} + } + + # check with display name next. + if ($null -eq $instance) + { + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.DisplayName -eq $DisplayName} + } + } + else + { + if (-not [System.String]::IsNullOrEmpty($Id)) + { + $instance = Get-MgBetaIdentityCustomAuthenticationExtension --CustomAuthenticationExtensionId $Id ` + -ErrorAction SilentlyContinue + } + if ($null -eq $instance) + { + $instance = Get-MgBetaIdentityCustomAuthenticationExtension -Filter "DisplayName eq '$DisplayName'" ` + -ErrorAction SilentlyContinue + } + } + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + DisplayName = $instance.DisplayName + Id = $instance.Id + Description = $instance.Description + } + + if ($instance.AdditionalProperties -ne $null) + { + $results.Add('CustomAuthenticationExtensionType', $instance.AdditionalProperties["@odata.type"]) + } + + if ($instance.AuthenticationConfiguration -ne $null) + { + $results.Add('AuthenticationConfigurationType', $instance.AuthenticationConfiguration["@odata.type"]) + $results.Add('AuthenticationConfigurationResourceId', $instance.AuthenticationConfiguration["resourceId"]) + } + + if ($instance.ClientConfiguration -ne $null) + { + $results.Add('ClientConfigurationTimeoutMilliseconds', $instance.ClientConfiguration.TimeoutInMilliseconds) + $results.Add('ClientConfigurationMaximumRetries', $instance.ClientConfiguration.MaximumRetries) + } + + $endpointConfiguration = @{} + if ($instance.EndPointConfiguration -ne $null -and $instance.EndPointConfiguration.AdditionalProperties -ne $null) + { + $endpointConfiguration.Add("EndpointType", $instance.EndPointConfiguration.AdditionalProperties["@odata.type"]) + + if ($endpointConfiguration["EndpointType"] -eq '#microsoft.graph.httpRequestEndpoint') + { + $endpointConfiguration.Add("TargetUrl", $instance.EndPointConfiguration.AdditionalProperties["targetUrl"]) + } + + if ($endpointConfiguration["EndpointType"] -eq '#microsoft.graph.logicAppTriggerEndpointConfiguration') + { + $endpointConfiguration.Add("SubscriptionId", $instance.EndPointConfiguration.AdditionalProperties["subscriptionId"]) + $endpointConfiguration.Add("ResourceGroupName", $instance.EndPointConfiguration.AdditionalProperties["resourceGroupName"]) + $endpointConfiguration.Add("LogicAppWorkflowName", $instance.EndPointConfiguration.AdditionalProperties["logicAppWorkflowName"]) + } + } + + $claimsForTokenConfguration = @() + if ($instance.AdditionalProperties -ne $null -and $instance.AdditionalProperties["claimsForTokenConfiguration"] -ne $null) + { + foreach ($claim in $instance.AdditionalProperties["claimsForTokenConfiguration"]) + { + $c = @{ + ClaimIdInApiResponse = $claim.claimIdInApiResponse + } + + $claimsForTokenConfguration += $c + } + } + + $results.Add('EndPointConfiguration', $endpointConfiguration) + $results.Add('ClaimsForTokenConfguration', $claimsForTokenConfguration) + + return [System.Collections.Hashtable] $results + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + [ValidateSet( + '#microsoft.graph.onTokenIssuanceStartCustomExtension', + '#microsoft.graph.onAttributeCollectionStartCustomExtension', + '#microsoft.graph.onAttributeCollectionStartCustomExtension' + )] + $CustomAuthenticationExtensionType, + + [Parameter()] + [System.String] + [ValidateSet( + '#microsoft.graph.azureAdTokenAuthentication', + '#microsoft.graph.azureAdPopTokenAuthentication' + )] + $AuthenticationConfigurationType, + + [Parameter()] + [System.String] + $AuthenticationConfigurationResourceId, + + [Parameter()] + [System.Int32] + $ClientConfigurationTimeoutMillisesonds, + + [Parameter()] + [System.Int32] + $ClientConfigurationMaximumRetries, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EndPointConfiguration, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $ClaimsForTokenConfguration, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present' + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $currentInstance = Get-TargetResource @PSBoundParameters + $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + $params = @{ + "@odata.type" = $setParameters["CustomAuthenticationExtensionType"] + displayName = $setParameters["DisplayName"] + description = $setParameters["Description"] + endpointConfiguration = @{ + "@odata.type" = $setParameters["EndpointType"] + targetUrl = $setParameters["TargetUrl"] + subscriptionId = $setParameters["SubscriptionId"] + resourceGroupName = $setParameters["ResourceGroupName"] + logicAppWorkflowName = $setParameters["LogicAppWorkflowName"] + } + authenticationConfiguration = @{ + "@odata.type" = $setParameters["AuthenticationConfigurationType"] + resourceId = $setParameters["AuthenticationConfigurationResourceId"] + } + clientConfiguration = @{ + timeoutInMilliseconds = $setParameters["ClientConfigurationTimeoutMilliseconds"] + maximumRetries = $setParameters["ClientConfigurationMaximumRetries"] + } + claimsForTokenConfguration = @() + } + + foreach ($claim in $setParameters["ClaimsForTokenConfguration"]) + { + $c = @{ + "claimIdInApiResponse" = $claim["ClaimIdInApiResponse"] + } + + $params["claimsForTokenConfiguration"] += $c + } + + # CREATE + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + $params.Remove('Id') | Out-Null + Write-Verbose -Message "Creating new Custom authentication extension with display name {$DisplayName}" + New-MgBetaIdentityCustomAuthenticationExtension @params + } + + # UPDATE + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Updating custom authentication extension {$DisplayName}" + $params.Add('CustomAuthenticationExtensionId', $currentInstance.Id) + $params.Remove('Id') | Out-Null + Update-MgBetaIdentityCustomAuthenticationExtension @SetParameters + } + # REMOVE + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Removing custom authentication extension {$DisplayName}." + Remove-MgBetaIdentityCustomAuthenticationExtension -CustomAuthenticationExtensionId $currentInstance.Id + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + [ValidateSet( + '#microsoft.graph.onTokenIssuanceStartCustomExtension', + '#microsoft.graph.onAttributeCollectionStartCustomExtension', + '#microsoft.graph.onAttributeCollectionStartCustomExtension' + )] + $CustomAuthenticationExtensionType, + + [Parameter()] + [System.String] + [ValidateSet( + '#microsoft.graph.azureAdTokenAuthentication', + '#microsoft.graph.azureAdPopTokenAuthentication' + )] + $AuthenticationConfigurationType, + + [Parameter()] + [System.String] + $AuthenticationConfigurationResourceId, + + [Parameter()] + [System.Int32] + $ClientConfigurationTimeoutMillisesonds, + + [Parameter()] + [System.Int32] + $ClientConfigurationMaximumRetries, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EndPointConfiguration, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $ClaimsForTokenConfguration, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present' + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + + $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] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + try + { + $Script:ExportMode = $true + [array] $Script:exportedInstances = Get-MgBetaIdentityCustomAuthenticationExtension -ErrorAction Stop + + $i = 1 + $dscContent = '' + if ($Script:exportedInstances.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + foreach ($config in $Script:exportedInstances) + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + + $displayedKey = $config.Id + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + $params = @{ + Id = $config.Id + DisplayNames = $config.DisplayName + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + $dscContent += $currentDSCBlock + Save-M365DSCPartialExport -Content $currentDSCBlock ` + -FileName $Global:PartialExportFileName + $i++ + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + return $dscContent + } + catch + { + Write-Host $Global:M365DSCEmojiRedX + + New-M365DSCLogEntry -Message 'Error during Export:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return '' + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.schema.mof new file mode 100644 index 0000000000..4d34f0d3ef --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.schema.mof @@ -0,0 +1,37 @@ +[ClassVersion("1.0.0.0"), FriendlyName("AADCustomAuthenticationExtension")] +class MSFT_AADCustomAuthenticationExtension : OMI_BaseResource +{ + [Key, Description("Display Name of the custom security attribute. Must be unique within an attribute set. Can be up to 32 characters long and include Unicode characters. Can't contain spaces or special characters. Can't be changed later. Case sensitive.")] String DisplayName; + [Write, Description("Unique identifier of the Attribute Definition.")] String Id; + [Write, Description("Description of the custom security attribute. Can be up to 128 characters long and include Unicode characters. Can't contain spaces or special characters. Can be changed later. ")] String Description; + [Write, Description("Defines the authentication configuration type")] String AuthenticationConfigurationType; + [Write, Description("Defines the authentication configuration resource id")] String AuthenticationConfigurationResourceId; + [Write, Description("Defines the client configuration timeout in milliseconds")] UInt32 ClientConfigurationTimeoutMullisesonds; + [Write, Description("Defines the client configuration max retries")] UInt32 ClientConfigurationMaximumRetries; + [Write, Description("Defines the endpoint configuration"), EmbeddedInstance("MSFT_AADCustomAuthenticationExtensionEndPointConfiguration")] String EndpointConfiguration; + [Write, Description("Defines the list of claims for token configurations"), EmbeddedInstance("MSFT_AADCustomAuthenticationExtensionEndPointConfiguration")] String ClaimsForTokenConfguration[]; + + [Write, Description("Present ensures the instance exists, absent ensures it is removed."), ValueMap{"Absent","Present"}, Values{"Absent","Present"}] string Ensure; + [Write, Description("Credentials of the workload's Admin"), EmbeddedInstance("MSFT_Credential")] string Credential; + [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; + [Write, Description("Id of the Azure Active Directory tenant used for authentication.")] String TenantId; + [Write, Description("Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication.")] String CertificateThumbprint; + [Write, Description("Managed ID being used for authentication.")] Boolean ManagedIdentity; + [Write, Description("Access token used for authentication.")] String AccessTokens[]; +}; + +[ClassVersion("1.0.0.0"), FriendlyName("AADCustomAuthenticationExtensionEndPointConfiguration")] +class MSFT_AADCustomAuthenticationExtensionEndPointConfiguration +{ + [Write, Description("Defines the type of the endpoint configuration")] String EndpointType; + [Write, Description("Defines the workflow name for the logic app")] String LogicAppWorkflowName; + [Write, Description("Defines the resource group name for the logic app")] String ResourceGroupName; + [Write, Description("Defines the subscription id for the logic app")] String SubscriptionId; + [Write, Description("Defines the target url for the http endpoint")] String TargetUrl; +} + +[ClassVersion("1.0.0.0"), FriendlyName("AADCustomAuthenticationExtensionClaimForTokenConfguration")] +class MSFT_AADCustomAuthenticationExtensionClaimForTokenConfguration +{ + [Write, Description("Defines the claim id in api response.")] String ClaimIdInApiResponse; +} diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/readme.md new file mode 100644 index 0000000000..d22077d7e6 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/readme.md @@ -0,0 +1,6 @@ + +# AADCustomAuthenticationExtension + +## Description + +Custom authentication extensions define interactions with external systems during a user authentication session. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/settings.json new file mode 100644 index 0000000000..ae6d5a8707 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/settings.json @@ -0,0 +1,32 @@ +{ + "resourceName": "AADCustomAuthenticationExtension", + "description": "Custom authentication extensions define interactions with external systems during a user authentication session. ", + "roles": { + "read": [ + "Attribute Definition Reader" + ], + "update": [ + "Attribute Definition Administrator" + ] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [ + { + "name": "CustomSecAttributeDefinition.Read.All" + } + ], + "update": [ + { + "name": "CustomSecAttributeDefinition.ReadWrite.All" + } + ] + } + } + } +} From c72dfc484e0cd1b3496ad807104e3679c70304f5 Mon Sep 17 00:00:00 2001 From: Sai Rohit Date: Mon, 21 Oct 2024 16:46:46 +0530 Subject: [PATCH 2/4] Added new resource --- ...MSFT_AADCustomAuthenticationExtension.psm1 | 133 ++++++-- ...ADCustomAuthenticationExtension.schema.mof | 37 +-- .../1-Create.ps1 | 52 +++ .../2-Update.ps1 | 52 +++ .../3-Remove.ps1 | 34 ++ ...AADCustomAuthenticationExtension.Tests.ps1 | 186 +++++++++++ Tests/Unit/Stubs/Microsoft365.psm1 | 299 ++++++++++++++++++ 7 files changed, 742 insertions(+), 51 deletions(-) create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADCustomAuthenticationExtension.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 index a81c729192..dd6ee5f452 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 @@ -4,6 +4,7 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( + [Parameter(Mandatory = $true)] [System.String] $DisplayName, @@ -38,7 +39,7 @@ function Get-TargetResource [Parameter()] [System.Int32] - $ClientConfigurationTimeoutMillisesonds, + $ClientConfigurationTimeoutMilliseconds, [Parameter()] [System.Int32] @@ -50,7 +51,7 @@ function Get-TargetResource [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] - $ClaimsForTokenConfguration, + $ClaimsForTokenConfiguration, [Parameter()] [System.Management.Automation.PSCredential] @@ -79,7 +80,7 @@ function Get-TargetResource [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] - $Ensure = 'Present', + $Ensure = 'Present' ) New-M365DSCConnection -Workload 'MicrosoftGraph' ` @@ -99,6 +100,7 @@ function Get-TargetResource $nullResult = $PSBoundParameters $nullResult.Ensure = 'Absent' + Write-Verbose -Message "Fetching result...." try { # check for export. @@ -120,7 +122,7 @@ function Get-TargetResource { if (-not [System.String]::IsNullOrEmpty($Id)) { - $instance = Get-MgBetaIdentityCustomAuthenticationExtension --CustomAuthenticationExtensionId $Id ` + $instance = Get-MgBetaIdentityCustomAuthenticationExtension -CustomAuthenticationExtensionId $Id ` -ErrorAction SilentlyContinue } if ($null -eq $instance) @@ -134,10 +136,13 @@ function Get-TargetResource return $nullResult } + Write-Verbose "Instance found for the resource. Calculating result...." + $results = @{ DisplayName = $instance.DisplayName Id = $instance.Id Description = $instance.Description + Ensure = 'Present' } if ($instance.AdditionalProperties -ne $null) @@ -157,25 +162,25 @@ function Get-TargetResource $results.Add('ClientConfigurationMaximumRetries', $instance.ClientConfiguration.MaximumRetries) } - $endpointConfiguration = @{} + $endpointConfigurationInstance = @{} if ($instance.EndPointConfiguration -ne $null -and $instance.EndPointConfiguration.AdditionalProperties -ne $null) { - $endpointConfiguration.Add("EndpointType", $instance.EndPointConfiguration.AdditionalProperties["@odata.type"]) + $endpointConfigurationInstance.Add("EndpointType", $instance.EndPointConfiguration.AdditionalProperties["@odata.type"]) - if ($endpointConfiguration["EndpointType"] -eq '#microsoft.graph.httpRequestEndpoint') + if ($endpointConfigurationInstance["EndpointType"] -eq '#microsoft.graph.httpRequestEndpoint') { - $endpointConfiguration.Add("TargetUrl", $instance.EndPointConfiguration.AdditionalProperties["targetUrl"]) + $endpointConfigurationInstance.Add("TargetUrl", $instance.EndPointConfiguration.AdditionalProperties["targetUrl"]) } - if ($endpointConfiguration["EndpointType"] -eq '#microsoft.graph.logicAppTriggerEndpointConfiguration') + if ($endpointConfigurationInstance["EndpointType"] -eq '#microsoft.graph.logicAppTriggerEndpointConfiguration') { - $endpointConfiguration.Add("SubscriptionId", $instance.EndPointConfiguration.AdditionalProperties["subscriptionId"]) - $endpointConfiguration.Add("ResourceGroupName", $instance.EndPointConfiguration.AdditionalProperties["resourceGroupName"]) - $endpointConfiguration.Add("LogicAppWorkflowName", $instance.EndPointConfiguration.AdditionalProperties["logicAppWorkflowName"]) + $endpointConfigurationInstance.Add("SubscriptionId", $instance.EndPointConfiguration.AdditionalProperties["subscriptionId"]) + $endpointConfigurationInstance.Add("ResourceGroupName", $instance.EndPointConfiguration.AdditionalProperties["resourceGroupName"]) + $endpointConfigurationInstance.Add("LogicAppWorkflowName", $instance.EndPointConfiguration.AdditionalProperties["logicAppWorkflowName"]) } } - $claimsForTokenConfguration = @() + $ClaimsForTokenConfigurationInstance = @() if ($instance.AdditionalProperties -ne $null -and $instance.AdditionalProperties["claimsForTokenConfiguration"] -ne $null) { foreach ($claim in $instance.AdditionalProperties["claimsForTokenConfiguration"]) @@ -184,12 +189,12 @@ function Get-TargetResource ClaimIdInApiResponse = $claim.claimIdInApiResponse } - $claimsForTokenConfguration += $c + $ClaimsForTokenConfigurationInstance += $c } } - $results.Add('EndPointConfiguration', $endpointConfiguration) - $results.Add('ClaimsForTokenConfguration', $claimsForTokenConfguration) + $results.Add('EndPointConfiguration', $endpointConfigurationInstance) + $results.Add('ClaimsForTokenConfiguration', $ClaimsForTokenConfigurationInstance) return [System.Collections.Hashtable] $results } @@ -211,6 +216,7 @@ function Set-TargetResource [CmdletBinding()] param ( + [Parameter(Mandatory = $true)] [System.String] $DisplayName, @@ -245,7 +251,7 @@ function Set-TargetResource [Parameter()] [System.Int32] - $ClientConfigurationTimeoutMillisesonds, + $ClientConfigurationTimeoutMilliseconds, [Parameter()] [System.Int32] @@ -257,7 +263,7 @@ function Set-TargetResource [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] - $ClaimsForTokenConfguration, + $ClaimsForTokenConfiguration, [Parameter()] [System.Management.Automation.PSCredential] @@ -323,24 +329,24 @@ function Set-TargetResource timeoutInMilliseconds = $setParameters["ClientConfigurationTimeoutMilliseconds"] maximumRetries = $setParameters["ClientConfigurationMaximumRetries"] } - claimsForTokenConfguration = @() - } - - foreach ($claim in $setParameters["ClaimsForTokenConfguration"]) - { - $c = @{ - "claimIdInApiResponse" = $claim["ClaimIdInApiResponse"] - } - - $params["claimsForTokenConfiguration"] += $c } # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { + $params.Add("ClaimsForTokenConfiguration", @()) + foreach ($claim in $setParameters["ClaimsForTokenConfiguration"]) + { + $c = @{ + "claimIdInApiResponse" = $claim["ClaimIdInApiResponse"] + } + + $params["claimsForTokenConfiguration"] += $c + } + $params.Remove('Id') | Out-Null Write-Verbose -Message "Creating new Custom authentication extension with display name {$DisplayName}" - New-MgBetaIdentityCustomAuthenticationExtension @params + New-MgBetaIdentityCustomAuthenticationExtension -BodyParameter $params } # UPDATE @@ -348,8 +354,22 @@ function Set-TargetResource { Write-Verbose -Message "Updating custom authentication extension {$DisplayName}" $params.Add('CustomAuthenticationExtensionId', $currentInstance.Id) + $params.Remove("@odata.type") | Out-Null $params.Remove('Id') | Out-Null - Update-MgBetaIdentityCustomAuthenticationExtension @SetParameters + + $params.Add("AdditionalProperties", @{}) + $params["AdditionalProperties"].Add("ClaimsForTokenConfiguration", @()) + + foreach ($claim in $setParameters["ClaimsForTokenConfiguration"]) + { + $c = @{ + "claimIdInApiResponse" = $claim["ClaimIdInApiResponse"] + } + + $params["AdditionalProperties"]["claimsForTokenConfiguration"] += $c + } + + Update-MgBetaIdentityCustomAuthenticationExtension @params } # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') @@ -365,6 +385,7 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( + [Parameter(Mandatory = $true)] [System.String] $DisplayName, @@ -399,7 +420,7 @@ function Test-TargetResource [Parameter()] [System.Int32] - $ClientConfigurationTimeoutMillisesonds, + $ClientConfigurationTimeoutMilliseconds, [Parameter()] [System.Int32] @@ -411,7 +432,7 @@ function Test-TargetResource [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] - $ClaimsForTokenConfguration, + $ClaimsForTokenConfiguration, [Parameter()] [System.Management.Automation.PSCredential] @@ -458,6 +479,28 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + #Compare Cim instances + foreach ($key in $PSBoundParameters.Keys) + { + $source = $PSBoundParameters.$key + $target = $CurrentValues.$key + if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') + { + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-not $testResult) + { + Write-Verbose "TestResult returned False for $source" + $testTargetResource = $false + } + else { + $ValuesToCheck.Remove($key) | Out-Null + } + } + } + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" @@ -547,7 +590,7 @@ function Export-TargetResource Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline $params = @{ Id = $config.Id - DisplayNames = $config.DisplayName + DisplayName = $config.DisplayName Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId @@ -557,6 +600,18 @@ function Export-TargetResource } $Results = Get-TargetResource @Params + + $endpointConfigurationCimString = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.EndpointConfiguration ` + -CIMInstanceName 'MSFT_AADCustomAuthenticationExtensionEndPointConfiguration' + + $ClaimsForTokenConfigurationCimString = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.ClaimsForTokenConfiguration ` + -CIMInstanceName 'MSFT_AADCustomAuthenticationExtensionClaimForTokenConfiguration' + + $Results.EndPointConfiguration = $endpointConfigurationCimString + $Results.ClaimsForTokenConfiguration = $ClaimsForTokenConfigurationCimString + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results @@ -565,7 +620,19 @@ function Export-TargetResource -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential + + if ($Results.EndPointConfiguration -ne $null) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName "EndPointConfiguration" + } + + if ($Results.ClaimsForTokenConfiguration -ne $null) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName "ClaimsForTokenConfiguration" -IsCIMArray $true + } + $dscContent += $currentDSCBlock + Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName $i++ diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.schema.mof index 4d34f0d3ef..6382d10037 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.schema.mof @@ -1,15 +1,32 @@ +[ClassVersion("1.0.0.0")] +class MSFT_AADCustomAuthenticationExtensionEndPointConfiguration +{ + [Write, Description("Defines the type of the endpoint configuration")] String EndpointType; + [Write, Description("Defines the workflow name for the logic app")] String LogicAppWorkflowName; + [Write, Description("Defines the resource group name for the logic app")] String ResourceGroupName; + [Write, Description("Defines the subscription id for the logic app")] String SubscriptionId; + [Write, Description("Defines the target url for the http endpoint")] String TargetUrl; +}; + +[ClassVersion("1.0.0.0")] +class MSFT_AADCustomAuthenticationExtensionClaimForTokenConfiguration +{ + [Write, Description("Defines the claim id in api response.")] String ClaimIdInApiResponse; +}; + [ClassVersion("1.0.0.0"), FriendlyName("AADCustomAuthenticationExtension")] class MSFT_AADCustomAuthenticationExtension : OMI_BaseResource { [Key, Description("Display Name of the custom security attribute. Must be unique within an attribute set. Can be up to 32 characters long and include Unicode characters. Can't contain spaces or special characters. Can't be changed later. Case sensitive.")] String DisplayName; [Write, Description("Unique identifier of the Attribute Definition.")] String Id; + [Write, Description("Defines the custom authentication extension type.")] String CustomAuthenticationExtensionType; [Write, Description("Description of the custom security attribute. Can be up to 128 characters long and include Unicode characters. Can't contain spaces or special characters. Can be changed later. ")] String Description; [Write, Description("Defines the authentication configuration type")] String AuthenticationConfigurationType; [Write, Description("Defines the authentication configuration resource id")] String AuthenticationConfigurationResourceId; - [Write, Description("Defines the client configuration timeout in milliseconds")] UInt32 ClientConfigurationTimeoutMullisesonds; + [Write, Description("Defines the client configuration timeout in milliseconds")] UInt32 ClientConfigurationTimeoutMilliseconds; [Write, Description("Defines the client configuration max retries")] UInt32 ClientConfigurationMaximumRetries; [Write, Description("Defines the endpoint configuration"), EmbeddedInstance("MSFT_AADCustomAuthenticationExtensionEndPointConfiguration")] String EndpointConfiguration; - [Write, Description("Defines the list of claims for token configurations"), EmbeddedInstance("MSFT_AADCustomAuthenticationExtensionEndPointConfiguration")] String ClaimsForTokenConfguration[]; + [Write, Description("Defines the list of claims for token configurations"), EmbeddedInstance("MSFT_AADCustomAuthenticationExtensionClaimForTokenConfiguration")] String ClaimsForTokenConfiguration[]; [Write, Description("Present ensures the instance exists, absent ensures it is removed."), ValueMap{"Absent","Present"}, Values{"Absent","Present"}] string Ensure; [Write, Description("Credentials of the workload's Admin"), EmbeddedInstance("MSFT_Credential")] string Credential; @@ -19,19 +36,3 @@ class MSFT_AADCustomAuthenticationExtension : OMI_BaseResource [Write, Description("Managed ID being used for authentication.")] Boolean ManagedIdentity; [Write, Description("Access token used for authentication.")] String AccessTokens[]; }; - -[ClassVersion("1.0.0.0"), FriendlyName("AADCustomAuthenticationExtensionEndPointConfiguration")] -class MSFT_AADCustomAuthenticationExtensionEndPointConfiguration -{ - [Write, Description("Defines the type of the endpoint configuration")] String EndpointType; - [Write, Description("Defines the workflow name for the logic app")] String LogicAppWorkflowName; - [Write, Description("Defines the resource group name for the logic app")] String ResourceGroupName; - [Write, Description("Defines the subscription id for the logic app")] String SubscriptionId; - [Write, Description("Defines the target url for the http endpoint")] String TargetUrl; -} - -[ClassVersion("1.0.0.0"), FriendlyName("AADCustomAuthenticationExtensionClaimForTokenConfguration")] -class MSFT_AADCustomAuthenticationExtensionClaimForTokenConfguration -{ - [Write, Description("Defines the claim id in api response.")] String ClaimIdInApiResponse; -} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/1-Create.ps1 new file mode 100644 index 0000000000..7091be751f --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/1-Create.ps1 @@ -0,0 +1,52 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + AADCustomAuthenticationExtension "AADCustomAuthenticationExtension1" + { + AuthenticationConfigurationResourceId = "api://microsoft365dsc.com/11105949-846e-42a1-a873-f12db8345013" + AuthenticationConfigurationType = "#microsoft.graph.azureAdTokenAuthentication" + ClaimsForTokenConfiguration = @( + MSFT_AADCustomAuthenticationExtensionClaimForTokenConfiguration{ + ClaimIdInApiResponse = 'MyClaim' + } + MSFT_AADCustomAuthenticationExtensionClaimForTokenConfiguration{ + ClaimIdInApiResponse = 'My2ndClaim' + } + ) + ClientConfigurationMaximumRetries = 1 + ClientConfigurationTimeoutMilliseconds = 2000 + CustomAuthenticationExtensionType = "#microsoft.graph.onTokenIssuanceStartCustomExtension" + Description = "DSC Testing 1" + DisplayName = "DSCTestExtension" + EndPointConfiguration = MSFT_AADCustomAuthenticationExtensionEndPointConfiguration{ + EndpointType = '#microsoft.graph.httpRequestEndpoint' + TargetUrl = 'https://Microsoft365DSC.com' + } + Ensure = "Present"; + Id = "11105949-846e-42a1-a873-f12db8345013" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/2-Update.ps1 new file mode 100644 index 0000000000..7091be751f --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/2-Update.ps1 @@ -0,0 +1,52 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + AADCustomAuthenticationExtension "AADCustomAuthenticationExtension1" + { + AuthenticationConfigurationResourceId = "api://microsoft365dsc.com/11105949-846e-42a1-a873-f12db8345013" + AuthenticationConfigurationType = "#microsoft.graph.azureAdTokenAuthentication" + ClaimsForTokenConfiguration = @( + MSFT_AADCustomAuthenticationExtensionClaimForTokenConfiguration{ + ClaimIdInApiResponse = 'MyClaim' + } + MSFT_AADCustomAuthenticationExtensionClaimForTokenConfiguration{ + ClaimIdInApiResponse = 'My2ndClaim' + } + ) + ClientConfigurationMaximumRetries = 1 + ClientConfigurationTimeoutMilliseconds = 2000 + CustomAuthenticationExtensionType = "#microsoft.graph.onTokenIssuanceStartCustomExtension" + Description = "DSC Testing 1" + DisplayName = "DSCTestExtension" + EndPointConfiguration = MSFT_AADCustomAuthenticationExtensionEndPointConfiguration{ + EndpointType = '#microsoft.graph.httpRequestEndpoint' + TargetUrl = 'https://Microsoft365DSC.com' + } + Ensure = "Present"; + Id = "11105949-846e-42a1-a873-f12db8345013" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/3-Remove.ps1 new file mode 100644 index 0000000000..1b48def3ec --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADCustomAuthenticationExtension/3-Remove.ps1 @@ -0,0 +1,34 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + + node localhost + { + AADCustomAuthenticationExtension "AADCustomAuthenticationExtension1" + { + DisplayName = "DSCTestExtension" + Ensure = "Absent" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADCustomAuthenticationExtension.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADCustomAuthenticationExtension.Tests.ps1 new file mode 100644 index 0000000000..828ee261ff --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADCustomAuthenticationExtension.Tests.ps1 @@ -0,0 +1,186 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath '..\..\Unit' ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Microsoft365.psm1' ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Generic.psm1' ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\UnitTestHelper.psm1' ` + -Resolve) + +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource "AADCustomAuthenticationExtension" -GenericStubModule $GenericStubPath +Describe -Name $Global:DscHelper.DescribeHeader -Fixture { + InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { + Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope + BeforeAll { + + $secpasswd = ConvertTo-SecureString (New-Guid | Out-String) -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName Get-PSSession -MockWith { + } + + Mock -CommandName Remove-PSSession -MockWith { + } + + Mock -CommandName Update-MgBetaIdentityCustomAuthenticationExtension -MockWith { + } + + Mock -CommandName New-MgBetaIdentityCustomAuthenticationExtension -MockWith { + } + + Mock -CommandName Remove-MgBetaIdentityCustomAuthenticationExtension -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credentials" + } + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + $Script:exportedInstances =$null + $Script:ExportMode = $False + } + + # Test Contexts + Context -Name "The AADCustomAuthenticationExtension should exist but it DOES NOT" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "testcustomextension" + Description = "test description" + Ensure = "Present" + Credential = $Credential + } + + Mock -CommandName Get-MgBetaIdentityCustomAuthenticationExtension -MockWith { + return $null + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + It 'Should Create the group from the Set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName New-MgBetaIdentityCustomAuthenticationExtension -Exactly 1 + } + } + + Context -Name 'The AADCustomAuthenticationExtension exists but it should not' -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "testcustomextension" + Description = "test description" + Ensure = "Absent" + Credential = $Credential + CustomAuthenticationExtensionType = "#microsoft.graph.onTokenIssuanceStartCustomExtension" + AuthenticationConfigurationType = "#microsoft.graph.azureAdTokenAuthentication" + AuthenticationConfigurationResourceId = "api://microsoft365dsc.com/a5352e69-55c0-4160-b4b5-03d034d842f" + ClientConfigurationTimeoutMilliseconds = 2000 + ClientConfigurationMaximumRetries = 1 + Id = "1f0c894f-d068-4f9c-af71-81d602569ad1" + ClaimsForTokenConfiguration = @() + } + + Mock -CommandName Get-MgBetaIdentityCustomAuthenticationExtension -MockWith { + $customextension = New-Object PSCustomObject + $customextension | Add-Member -MemberType NoteProperty -Name DisplayName -Value "testcustomextension" + $customextension | Add-Member -MemberType NoteProperty -Name Description -Value "test description" + $customextension | Add-Member -MemberType NoteProperty -Name Id -Value "1f0c894f-d068-4f9c-af71-81d602569ad1" + + return $customextension + } + } + + It 'Should return values from the get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + Should -Invoke -CommandName 'Get-MgBetaIdentityCustomAuthenticationExtension' -Exactly 1 + } + + It 'Should return false from the test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should remove the app from the set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName 'Remove-MgBetaIdentityCustomAuthenticationExtension' -Exactly 1 + } + } + + Context -Name 'The AADCustomAuthenticationExtension exists and values are in the desired state' -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "testcustomextension" + Description = "test description" + Ensure = "Present" + Id = "1f0c894f-d068-4f9c-af71-81d602569ad1" + } + + Mock -CommandName Get-MgBetaIdentityCustomAuthenticationExtension -MockWith { + $customextension = New-Object PSCustomObject + $customextension | Add-Member -MemberType NoteProperty -Name DisplayName -Value "testcustomextension" + $customextension | Add-Member -MemberType NoteProperty -Name Description -Value "test description" + $customextension | Add-Member -MemberType NoteProperty -Name Id -Value "1f0c894f-d068-4f9c-af71-81d602569ad1" + + return $customextension + } + } + + It 'Should return values from the get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + Should -Invoke -CommandName 'Get-MgBetaIdentityCustomAuthenticationExtension' -Exactly 1 + } + + It 'Should return false from the test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name 'The AADCustomAuthenticationExtension exists and values are not in the desired state' -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "testcustomextension" + Description = "test description modified" + Ensure = "Present" + Id = "1f0c894f-d068-4f9c-af71-81d602569ad1" + } + + Mock -CommandName Get-MgBetaIdentityCustomAuthenticationExtension -MockWith { + $customextension = New-Object PSCustomObject + $customextension | Add-Member -MemberType NoteProperty -Name DisplayName -Value "testcustomextension" + $customextension | Add-Member -MemberType NoteProperty -Name Description -Value "test description" + $customextension | Add-Member -MemberType NoteProperty -Name Id -Value "1f0c894f-d068-4f9c-af71-81d602569ad1" + + return $customextension + } + } + + It 'Should return values from the get method' { + Get-TargetResource @testParams + Should -Invoke -CommandName 'Get-MgBetaIdentityCustomAuthenticationExtension' -Exactly 1 + } + + It 'Should return false from the test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName 'Update-MgBetaIdentityCustomAuthenticationExtension' -Exactly 1 + } + } + } +} diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 816dc46565..7120b986b2 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -30,6 +30,7 @@ function Get-MgBetaPolicyAdminConsentRequestPolicy param() } + #region Microsoft.Graph.Beta.Applications function Get-MgBetaApplication { @@ -18283,7 +18284,305 @@ function Invoke-MgGraphRequest $Headers ) } + +function New-MgBetaIdentityCustomAuthenticationExtension +{ + [CmdletBinding()] + param( + [Parameter()] + [System.Collections.Hashtable] + $EndpointConfiguration, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.Collections.Hashtable] + $AuthenticationConfiguration, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [PSObject] + $ClientConfiguration, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Collections.Hashtable] + $BodyParameter, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} +function Update-MgBetaIdentityCustomAuthenticationExtension +{ + [CmdletBinding()] + param( + [Parameter()] + [System.Collections.Hashtable] + $EndpointConfiguration, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.Collections.Hashtable] + $AuthenticationConfiguration, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [PSObject] + $ClientConfiguration, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.String] + $CustomAuthenticationExtensionId, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Collections.Hashtable] + $BodyParameter, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} +function Get-MgBetaIdentityCustomAuthenticationExtension +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String[]] + $Property, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Int32] + $PageSize, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Int32] + $Skip, + + [Parameter()] + [System.String] + $CustomAuthenticationExtensionId, + + [Parameter()] + [System.Int32] + $Top, + + [Parameter()] + [System.String] + $CountVariable, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.String[]] + $Sort, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $All, + + [Parameter()] + [System.String] + $Filter, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $Search, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.String[]] + $ExpandProperty, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} +function Remove-MgBetaIdentityCustomAuthenticationExtension +{ + [CmdletBinding()] + param( + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $PassThru, + + [Parameter()] + [System.String] + $IfMatch, + + [Parameter()] + [System.String] + $CustomAuthenticationExtensionId, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [PSObject] + $HttpPipelineAppend, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break + ) +} #endregion + #region Microsoft.Graph.Beta.DeviceManagement function Get-MgBetaDeviceManagement { From d2908a1c4ba6f0e01202e957f25a4768883dd59c Mon Sep 17 00:00:00 2001 From: Sai Rohit Date: Tue, 22 Oct 2024 13:39:37 +0530 Subject: [PATCH 3/4] Fixed Set cases. --- ...MSFT_AADCustomAuthenticationExtension.psm1 | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 index dd6ee5f452..8cb16f56e8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 @@ -311,19 +311,15 @@ function Set-TargetResource $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters $params = @{ - "@odata.type" = $setParameters["CustomAuthenticationExtensionType"] - displayName = $setParameters["DisplayName"] - description = $setParameters["Description"] + "@odata.type" = $setParameters.CustomAuthenticationExtensionType + displayName = $setParameters.DisplayName + description = $setParameters.Description endpointConfiguration = @{ - "@odata.type" = $setParameters["EndpointType"] - targetUrl = $setParameters["TargetUrl"] - subscriptionId = $setParameters["SubscriptionId"] - resourceGroupName = $setParameters["ResourceGroupName"] - logicAppWorkflowName = $setParameters["LogicAppWorkflowName"] + "@odata.type" = $setParameters.EndPointConfiguration.EndpointType } authenticationConfiguration = @{ - "@odata.type" = $setParameters["AuthenticationConfigurationType"] - resourceId = $setParameters["AuthenticationConfigurationResourceId"] + "@odata.type" = $setParameters.AuthenticationConfigurationType + resourceId = $setParameters.AuthenticationConfigurationResourceId } clientConfiguration = @{ timeoutInMilliseconds = $setParameters["ClientConfigurationTimeoutMilliseconds"] @@ -331,21 +327,37 @@ function Set-TargetResource } } + if ($params.endpointConfiguration["@odata.type"] -eq "#microsoft.graph.httpRequestEndpoint") + { + Write-Verbose -Message "{$setParameters.EndPointConfiguration.TargetUrl}" + $params.endpointConfiguration["targetUrl"] = $setParameters.EndPointConfiguration.TargetUrl + } + + if ($params.endpointConfiguration["@odata.type"] -eq "#microsoft.graph.logicAppTriggerEndpointConfiguration") + { + $params.endpointConfiguration["subscriptionId"] = $setParameters.EndPointConfiguration["SubscriptionId"] + $params.endpointConfiguration["resourceGroupName"] = $setParameters.EndPointConfiguration["ResourceGroupName"] + $params.endpointConfiguration["logicAppWorkflowName"] = $setParameters.EndPointConfiguration["LogicAppWorkflowName"] + } + # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { - $params.Add("ClaimsForTokenConfiguration", @()) - foreach ($claim in $setParameters["ClaimsForTokenConfiguration"]) + $params.Add("claimsForTokenConfiguration", @()) + foreach ($claim in $setParameters.claimsForTokenConfiguration) { + $val = $claim.claimIdInApiResponse + Write-Verbose -Message "{$val}" $c = @{ - "claimIdInApiResponse" = $claim["ClaimIdInApiResponse"] + "claimIdInApiResponse" = $claim.claimIdInApiResponse } - $params["claimsForTokenConfiguration"] += $c + $params.claimsForTokenConfiguration += $c } $params.Remove('Id') | Out-Null - Write-Verbose -Message "Creating new Custom authentication extension with display name {$DisplayName}" + $type = $params["@odata.type"] + Write-Verbose -Message "Creating new Custom authentication extension with display name {$DisplayName} and type {$type}" New-MgBetaIdentityCustomAuthenticationExtension -BodyParameter $params } @@ -354,7 +366,6 @@ function Set-TargetResource { Write-Verbose -Message "Updating custom authentication extension {$DisplayName}" $params.Add('CustomAuthenticationExtensionId', $currentInstance.Id) - $params.Remove("@odata.type") | Out-Null $params.Remove('Id') | Out-Null $params.Add("AdditionalProperties", @{}) @@ -369,7 +380,8 @@ function Set-TargetResource $params["AdditionalProperties"]["claimsForTokenConfiguration"] += $c } - Update-MgBetaIdentityCustomAuthenticationExtension @params + Write-Verbose -Message "{$params['@odata.type']}" + Update-MgBetaIdentityCustomAuthenticationExtension -CustomAuthenticationExtensionId $Id -BodyParameter $params } # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') From e597b00376cf47615c6ceb812e243e6e39176e1c Mon Sep 17 00:00:00 2001 From: Sai Rohit Date: Tue, 22 Oct 2024 14:13:37 +0530 Subject: [PATCH 4/4] Added change log --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9879a4382a..cac51ab204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ * Initial release. * AADConnectorGroupApplicationProxy * Initial release. +* AADCustomAuthenticationExtension + * Initial release. * AADCustomSecurityAttributeDefinition * Initial release. * AADDeviceRegistrationPolicy