From e46dca3c1ed963bdc5b72f633daf200a8649166f Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 29 Jan 2025 12:12:31 -0500 Subject: [PATCH 01/30] CIMInstance Evaluation Refactoring --- CHANGELOG.md | 7 + .../MSFT_AADApplication.psm1 | 54 +------- .../MSFT_AADGroup/MSFT_AADGroup.psm1 | 129 ++++-------------- .../Modules/M365DSCDRGUtil.psm1 | 42 +++++- 4 files changed, 74 insertions(+), 158 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c73c955fb7..c8d72aebbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change log for Microsoft365DSC +# UNRELEASED + +* AADApplication + * Refactored logic for CIM Instance evaluation in Test-TargetResource. +* AADGroup + * Refactored logic for CIM Instance evaluation in Test-TargetResource. + # 1.25.129.1 * AADRoleEligibilityScheduleRequest diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADApplication/MSFT_AADApplication.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADApplication/MSFT_AADApplication.psm1 index f950fd7afb..f7d83a1f78 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADApplication/MSFT_AADApplication.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADApplication/MSFT_AADApplication.psm1 @@ -455,11 +455,15 @@ function Get-TargetResource # singleSignOnSettings $singleSignOnValues = @{ - kerberosSignOnSettings = @{ + singleSignOnMode = $oppInfo.singleSignOnSettings.singleSignOnMode + } + if ($oppInfo.singleSignOnMode.kerberosSignOnSettings) + { + $kerberosSignOnSettings = @{ kerberosServicePrincipalName = $oppInfo.singleSignOnSettings.kerberosSignOnSettings.kerberosServicePrincipalName kerberosSignOnMappingAttributeType = $oppInfo.singleSignOnSettings.kerberosSignOnSettings.kerberosSignOnMappingAttributeType } - singleSignOnMode = $oppInfo.singleSignOnSettings.singleSignOnMode + $singleSignOnValues.Add('kerberosSignOnSettings', $kerberosSignOnSettings) } $onPremisesPublishingValue.Add('singleSignOnSettings', $singleSignOnValues) } @@ -1415,50 +1419,7 @@ function Test-TargetResource Write-Verbose -Message 'Testing configuration of AzureAD Application' $CurrentValues = Get-TargetResource @PSBoundParameters - - if ($CurrentValues.Permissions.Length -gt 0 -and $null -ne $CurrentValues.Permissions.Name -and ` - $null -ne $Permissions) - { - $differenceObject = $Permissions.Name - if ($null -eq $differenceObject) - { - $differenceObject = @() - } - $permissionsDiff = Compare-Object -ReferenceObject ($CurrentValues.Permissions.Name) -DifferenceObject $differenceObject - $driftedParams = @{} - if ($null -ne $permissionsDiff) - { - Write-Verbose -Message "Permissions differ: $($permissionsDiff | Out-String)" - Write-Verbose -Message "Test-TargetResource returned $false" - $EventValue = "$($CurrentValues.Permissions.Name)" - $EventValue += "$($Permissions.Name)" - $driftedParams.Add('Permissions', $EventValue) - } - else - { - Write-Verbose -Message 'Permissions for Azure AD Application are the same' - } - } - else - { - $driftedParams = @{} - if ($Permissions.Length -gt 0) - { - Write-Verbose -Message 'No Permissions exist for the current Azure AD App, but permissions were specified for desired state' - Write-Verbose -Message "Test-TargetResource returned $false" - - $EventValue = "`$null" - $EventValue += "$($Permissions.Name)" - $driftedParams.Add('Permissions', $EventValue) - } - else - { - Write-Verbose -Message 'No Permissions exist for the current Azure AD App and no permissions were specified' - } - } - $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() - $testTargetResource = $true #Compare Cim instances @@ -1466,7 +1427,7 @@ function Test-TargetResource { $source = $PSBoundParameters.$key $target = $CurrentValues.$key - if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*' -and $source -notlike '*Permission*') + if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') { $testResult = Compare-M365DSCComplexObject ` -Source ($source) ` @@ -1489,7 +1450,6 @@ function Test-TargetResource $ValuesToCheck.Remove('ObjectId') | Out-Null $ValuesToCheck.Remove('AppId') | Out-Null - $ValuesToCheck.Remove('Permissions') | Out-Null $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.psm1 index eb80eb01e7..3adde8eeb7 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.psm1 @@ -1065,128 +1065,47 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" - # Check Licenses - if (-not ($null -eq $AssignedLicenses -and $null -eq $CurrentValues.AssignedLicenses)) + $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() + $ValuesToCheck.Remove('Id') | Out-Null + + $testTargetResource = $true + + #Compare Cim instances + foreach ($key in $PSBoundParameters.Keys) { - try + $source = $PSBoundParameters.$key + $target = $CurrentValues.$key + if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') { - if ($null -ne $CurrentValues.AssignedLicenses -and $CurrentValues.AssignedLicenses.Length -gt 0 -and ` - ($PSBoundParameters.ContainsKey('AssignedLicenses') -and $null -eq $AssignedLicenses)) - { - Write-Verbose -Message "The group {$DisplayName} currently has licenses assigned but it shouldn't" - Write-Verbose -Message "Test-TargetResource returned $false" - $EventMessage = "Assigned Licenses for Azure AD Group {$DisplayName} were not in the desired state.`r`nThe group should not have any licenses assigned but instead contained {$($CurrentValues.AssignedLicenses.SkuId -join ',')}" - Add-M365DSCEvent -Message $EventMessage -EntryType 'Warning' ` - -EventID 1 -Source $($MyInvocation.MyCommand.Source) + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) - return $false - } - elseif ($null -eq $CurrentValues.AssignedLicenses -and $null -ne $AssignedLicenses -and ` - $AssignedLicenses.Length -gt 0) + if (-not $testResult) { - Write-Verbose -Message "The group {$DisplayName} currently doesn't have licenses assigned but it should" - Write-Verbose -Message "Test-TargetResource returned $false" - $EventMessage = "Assigned Licenses for Azure AD Group {$DisplayName} were not in the desired state.`r`nThe group doesn't not have any licenses assigned but should have {$($CurrentValues.AssignedLicenses.SkuId -join ',')}" - Add-M365DSCEvent -Message $EventMessage -EntryType 'Warning' ` - -EventID 1 -Source $($MyInvocation.MyCommand.Source) - - return $false - } - elseif ($CurrentValues.AssignedLicenses.Length -gt 0 -and $AssignedLicenses.Length -gt 0) - { - Write-Verbose -Message "Current assigned licenses and desired assigned licenses for group {$DisplayName} are not null and will be compared" - $licensesDiff = Compare-Object -ReferenceObject ($CurrentValues.AssignedLicenses.SkuId) -DifferenceObject ($AssignedLicenses.SkuId) - if ($null -ne $licensesDiff) - { - Write-Verbose -Message "AssignedLicenses differ for group {$DisplayName}: $($licensesDiff | Out-String)" - Write-Verbose -Message "Test-TargetResource returned $false" - $EventMessage = "Assigned Licenses for Azure AD Group {$DisplayName} were not in the desired state.`r`nThey should contain {$($AssignedLicenses.SkuId -join ',')} but instead contained {$($CurrentValues.AssignedLicenses.SkuId -join ',')}" - Add-M365DSCEvent -Message $EventMessage -EntryType 'Warning' ` - -EventID 1 -Source $($MyInvocation.MyCommand.Source) - - return $false - } - else - { - Write-Verbose -Message "AssignedLicenses for Azure AD Group {$DisplayName} are the same, checking DisabledPlans" - } - - # Disabled Plans - #Compare DisabledPlans for each SkuId - all SkuId's are processed regardless of result - $result = $true - foreach ($assignedLicense in $AssignedLicenses) - { - Write-Verbose "Compare DisabledPlans for SkuId $($assignedLicense.SkuId) in group {$DisplayName}" - $currentLicense = $CurrentValues.AssignedLicenses | Where-Object -FilterScript { $_.SkuId -eq $assignedLicense.SkuId } - if ($assignedLicense.DisabledPlans.Count -ne 0 -or $currentLicense.DisabledPlans.Count -ne 0) - { - try - { - $licensesDiff = Compare-Object -ReferenceObject $assignedLicense.DisabledPlans -DifferenceObject $currentLicense.DisabledPlans - if ($null -ne $licensesDiff) - { - Write-Verbose -Message "DisabledPlans for SkuId $($assignedLicense.SkuId) differ: $($licensesDiff | Out-String)" - Write-Verbose -Message "Test-TargetResource returned $false" - $EventMessage = "Disabled Plans for Azure AD Group Licenses {$DisplayName} SkuId $($assignedLicense.SkuId) were not in the desired state.`r`n" + ` - "They should contain {$($assignedLicense.DisabledPlans -join ',')} but instead contained {$($currentLicense.DisabledPlans -join ',')}" - Add-M365DSCEvent -Message $EventMessage -EntryType 'Warning' ` - -EventID 1 -Source $($MyInvocation.MyCommand.Source) - - $result = $false - } - else - { - Write-Verbose -Message "DisabledPlans for SkuId $($assignedLicense.SkuId) are the same" - } - } - catch - { - Write-Verbose -Message "Test-TargetResource returned `$false (DisabledPlans: $($_.Exception.Message))" - $result = $false - } - } - } - if ($true -ne $result) - { - return $result - } - } - elseif ($PSBoundParameters.ContainsKey('AssignedLicenses')) - { - Write-Verbose -Message "The group {$DisplayName} currently has licenses assigned but it shouldn't have" - Write-Verbose -Message "Test-TargetResource returned $false" - $EventMessage = "Assigned Licenses for Azure AD Group {$DisplayName} were not in the desired state.`r`nThe group has licenses assigned but shouldn't have {$($CurrentValues.AssignedLicenses.SkuId)}" - Add-M365DSCEvent -Message $EventMessage -EntryType 'Warning' ` - -EventID 1 -Source $($MyInvocation.MyCommand.Source) - - return $false + Write-Verbose "TestResult returned False for $source" + $testTargetResource = $false } else { - Write-Verbose -Message "Both the current and desired assigned licenses lists for group {$DisplayName} are empty or not specified." + $ValuesToCheck.Remove($key) | Out-Null } } - catch - { - Write-Verbose -Message "Error evaluating the AssignedLicenses for group {$DisplayName}: $_" - Write-Verbose -Message "Test-TargetResource returned $false" - return $false - } } - $ValuesToCheck = $PSBoundParameters - $ValuesToCheck.Remove('Id') | Out-Null - $ValuesToCheck.Remove('GroupTypes') | Out-Null - $ValuesToCheck.Remove('AssignedLicenses') | Out-Null - $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` -DesiredValues $PSBoundParameters ` -ValuesToCheck $ValuesToCheck.Keys - Write-Verbose -Message "Test-TargetResource returned $TestResult" + if (-not $TestResult) + { + $testTargetResource = $false + } + + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" - return $TestResult + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 index 4f82942a31..8922832fb0 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 @@ -659,19 +659,49 @@ function Compare-M365DSCComplexObject foreach ($item in $Source) { + $foundMatch = $false foreach ($targetItem in $Target) { - $compareResult = Compare-M365DSCComplexObject ` - -Source $item ` - -Target $targetItem + if (-not $foundMatch) + { + $compareResult = Compare-M365DSCComplexObject ` + -Source $item ` + -Target $targetItem + + if ($compareResult) + { + $foundMatch = $true + } + } + } + + if (-not $foundMatch) + { + Write-Verbose -Message 'Configuration drift - The complex array items are not identical' + return $false + } + } - if ($compareResult) + # Do the opposite check + foreach ($item in $target) + { + $foundMatch = $false + foreach ($targetItem in $Source) + { + if (-not $foundMatch) { - break + $compareResult = Compare-M365DSCComplexObject ` + -Source $item ` + -Target $targetItem + + if ($compareResult) + { + $foundMatch = $true + } } } - if (-not $compareResult) + if (-not $foundMatch) { Write-Verbose -Message 'Configuration drift - The complex array items are not identical' return $false From b6bc3ca783b8b7ad67c588babf953d8dde17fe1c Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 29 Jan 2025 15:33:01 -0500 Subject: [PATCH 02/30] Initial --- .../MSFT_AADAdminConsentRequestPolicy.psm1 | 52 ++++++++------ .../MSFT_AADAuthenticationMethodPolicy.psm1 | 21 +++--- ...thenticationMethodPolicyAuthenticator.psm1 | 20 +++--- ...FT_AADAuthenticationMethodPolicyEmail.psm1 | 20 +++--- ...AADAuthenticationMethodPolicyExternal.psm1 | 20 +++--- ...FT_AADAuthenticationMethodPolicyFido2.psm1 | 20 +++--- ...AADAuthenticationMethodPolicyHardware.psm1 | 21 +++--- ...MSFT_AADAuthenticationMethodPolicySms.psm1 | 21 +++--- ...AADAuthenticationMethodPolicySoftware.psm1 | 21 +++--- ...ADAuthenticationMethodPolicyTemporary.psm1 | 21 +++--- ...FT_AADAuthenticationMethodPolicyVoice.psm1 | 21 +++--- ...SFT_AADAuthenticationMethodPolicyX509.psm1 | 21 +++--- .../MSFT_AADAuthenticationRequirement.psm1 | 13 ++-- .../MSFT_AADAuthenticationStrengthPolicy.psm1 | 12 ++-- .../MSFT_AADClaimsMappingPolicy.psm1 | 20 +++--- .../MSFT_AADConditionalAccessPolicy.psm1 | 72 ++++++++++++------- ...SFT_AADConnectorGroupApplicationProxy.psm1 | 11 ++- ...enantAccessPolicyConfigurationDefault.psm1 | 21 +++--- ...enantAccessPolicyConfigurationPartner.psm1 | 21 +++--- ...MSFT_AADCustomAuthenticationExtension.psm1 | 9 ++- .../MSFT_SCPolicyConfig.psm1 | 11 ++- .../Modules/M365DSCDRGUtil.psm1 | 2 +- 22 files changed, 259 insertions(+), 212 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAdminConsentRequestPolicy/MSFT_AADAdminConsentRequestPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAdminConsentRequestPolicy/MSFT_AADAdminConsentRequestPolicy.psm1 index 77ebe8e3ec..8662b3533a 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAdminConsentRequestPolicy/MSFT_AADAdminConsentRequestPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAdminConsentRequestPolicy/MSFT_AADAdminConsentRequestPolicy.psm1 @@ -361,29 +361,41 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - $testResult = $true - foreach ($reviewer in $Reviewers) - { - $currentEquivalent = $CurrentValues.Reviewers | Where-Object -FilterScript { $_.ReviewerId -eq $reviewer.ReviewerId -and $_.ReviewerType -eq $reviewer.ReviewerType } - if ($null -eq $currentEquivalent) - { - $testResult = $false - Write-Verbose -Message "Couldn't find current reviewer {$($reviewer.ReviewerId)}" - } - } - - if ($testResult) + #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 + } + } + } + + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $ValuesToCheck.Remove('Reviewers') | Out-Null - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicy/MSFT_AADAuthenticationMethodPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicy/MSFT_AADAuthenticationMethodPolicy.psm1 index f7e9ea30e0..5fe37db211 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicy/MSFT_AADAuthenticationMethodPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicy/MSFT_AADAuthenticationMethodPolicy.psm1 @@ -508,6 +508,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -518,7 +519,7 @@ function Test-TargetResource { $source = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $source - $testResult = Compare-M365DSCComplexObject ` + $testTargetResource = Compare-M365DSCComplexObject ` -Source ($source) ` -Target ($target) @@ -537,17 +538,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyAuthenticator/MSFT_AADAuthenticationMethodPolicyAuthenticator.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyAuthenticator/MSFT_AADAuthenticationMethodPolicyAuthenticator.psm1 index 22a9b92b6b..718c596575 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyAuthenticator/MSFT_AADAuthenticationMethodPolicyAuthenticator.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyAuthenticator/MSFT_AADAuthenticationMethodPolicyAuthenticator.psm1 @@ -789,6 +789,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -805,7 +806,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -818,17 +819,18 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyEmail/MSFT_AADAuthenticationMethodPolicyEmail.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyEmail/MSFT_AADAuthenticationMethodPolicyEmail.psm1 index 446b1aeda4..8a36cdb0d9 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyEmail/MSFT_AADAuthenticationMethodPolicyEmail.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyEmail/MSFT_AADAuthenticationMethodPolicyEmail.psm1 @@ -453,6 +453,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -469,7 +470,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -482,17 +483,18 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyExternal/MSFT_AADAuthenticationMethodPolicyExternal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyExternal/MSFT_AADAuthenticationMethodPolicyExternal.psm1 index 3d165e0f60..23ce0be271 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyExternal/MSFT_AADAuthenticationMethodPolicyExternal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyExternal/MSFT_AADAuthenticationMethodPolicyExternal.psm1 @@ -439,6 +439,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -455,7 +456,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -466,17 +467,18 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyFido2/MSFT_AADAuthenticationMethodPolicyFido2.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyFido2/MSFT_AADAuthenticationMethodPolicyFido2.psm1 index f7582a366b..00d2df7dfc 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyFido2/MSFT_AADAuthenticationMethodPolicyFido2.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyFido2/MSFT_AADAuthenticationMethodPolicyFido2.psm1 @@ -483,6 +483,7 @@ function Test-TargetResource $testResult = $true #Compare Cim instances + $testTargetResource = $true foreach ($key in $PSBoundParameters.Keys) { $source = $PSBoundParameters.$key @@ -497,7 +498,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -510,17 +511,18 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyHardware/MSFT_AADAuthenticationMethodPolicyHardware.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyHardware/MSFT_AADAuthenticationMethodPolicyHardware.psm1 index 723b4f0a83..1d85e1d98c 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyHardware/MSFT_AADAuthenticationMethodPolicyHardware.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyHardware/MSFT_AADAuthenticationMethodPolicyHardware.psm1 @@ -425,6 +425,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -441,7 +442,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -454,17 +455,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicySms/MSFT_AADAuthenticationMethodPolicySms.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicySms/MSFT_AADAuthenticationMethodPolicySms.psm1 index 188fdcc9b9..c1d4ed5197 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicySms/MSFT_AADAuthenticationMethodPolicySms.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicySms/MSFT_AADAuthenticationMethodPolicySms.psm1 @@ -429,6 +429,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -445,7 +446,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -458,17 +459,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicySoftware/MSFT_AADAuthenticationMethodPolicySoftware.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicySoftware/MSFT_AADAuthenticationMethodPolicySoftware.psm1 index 7d8bd7934c..07a0658607 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicySoftware/MSFT_AADAuthenticationMethodPolicySoftware.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicySoftware/MSFT_AADAuthenticationMethodPolicySoftware.psm1 @@ -428,6 +428,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -444,7 +445,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -457,17 +458,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyTemporary/MSFT_AADAuthenticationMethodPolicyTemporary.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyTemporary/MSFT_AADAuthenticationMethodPolicyTemporary.psm1 index 1ad824f844..72881fe434 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyTemporary/MSFT_AADAuthenticationMethodPolicyTemporary.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyTemporary/MSFT_AADAuthenticationMethodPolicyTemporary.psm1 @@ -497,6 +497,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances Write-Verbose -Message 'Evaluating keys' @@ -514,7 +515,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -527,17 +528,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyVoice/MSFT_AADAuthenticationMethodPolicyVoice.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyVoice/MSFT_AADAuthenticationMethodPolicyVoice.psm1 index bde2d43b71..d77839ccfb 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyVoice/MSFT_AADAuthenticationMethodPolicyVoice.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyVoice/MSFT_AADAuthenticationMethodPolicyVoice.psm1 @@ -441,6 +441,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -457,7 +458,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -470,17 +471,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyX509/MSFT_AADAuthenticationMethodPolicyX509.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyX509/MSFT_AADAuthenticationMethodPolicyX509.psm1 index 46edf9edb9..c3b23dc7d0 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyX509/MSFT_AADAuthenticationMethodPolicyX509.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationMethodPolicyX509/MSFT_AADAuthenticationMethodPolicyX509.psm1 @@ -505,6 +505,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -521,7 +522,7 @@ function Test-TargetResource if (-Not $testResult) { - $testResult = $false + $testTargetResource = $false break } @@ -534,17 +535,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 index bcb1ce332d..8ab33aa514 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 @@ -243,21 +243,16 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() - $testResult = $true - $CurrentValues.remove('Id') | Out-Null $ValuesToCheck.remove('Id') | Out-Null Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) - { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys - } + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys Write-Verbose -Message "Test-TargetResource returned $testResult" diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationStrengthPolicy/MSFT_AADAuthenticationStrengthPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationStrengthPolicy/MSFT_AADAuthenticationStrengthPolicy.psm1 index 1f31b32475..b6574a5ca9 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationStrengthPolicy/MSFT_AADAuthenticationStrengthPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationStrengthPolicy/MSFT_AADAuthenticationStrengthPolicy.psm1 @@ -299,18 +299,14 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $ValuesToCheck.Remove('Id') | Out-Null - $testResult = $true Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) - { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys - } + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys Write-Verbose -Message "Test-TargetResource returned $testResult" diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADClaimsMappingPolicy/MSFT_AADClaimsMappingPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADClaimsMappingPolicy/MSFT_AADClaimsMappingPolicy.psm1 index 0e69584348..2929e98968 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADClaimsMappingPolicy/MSFT_AADClaimsMappingPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADClaimsMappingPolicy/MSFT_AADClaimsMappingPolicy.psm1 @@ -416,6 +416,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -430,6 +431,7 @@ function Test-TargetResource if (-not $testResult) { + $testTargetResource = $false break } @@ -443,17 +445,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 index 331a56ecc3..358188956f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 @@ -2202,43 +2202,63 @@ function Test-TargetResource [System.String[]] $AccessTokens ) + #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 Write-Verbose -Message 'Testing configuration of AzureAD CA Policies' $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() + $testResult = $true + $testTargetResource = $true - Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" - Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" + #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) - $ValuesToCheck = $PSBoundParameters - $ValuesToCheck.Remove('Id') | Out-Null + if (-not $testResult) + { + $testTargetResource = $false + break + } - # If no TransferMethod is specified, ignore it - # If a TransferMethod is specified, check if it is equal to the current value - # while ignoring the order of the values - if (-not $PSBoundParameters.ContainsKey('TransferMethods') -or - $null -eq (Compare-Object -ReferenceObject $TransferMethods.Split(',') -DifferenceObject $CurrentValues.TransferMethods.Split(','))) - { - $ValuesToCheck.Remove('TransferMethods') | Out-Null - $TestResult = $true - } - else - { - Write-Verbose -Message "TransferMethods are not equal: [$TransferMethods] - [$($CurrentValues.TransferMethods)]" - $TestResult = $false + $ValuesToCheck.Remove($key) | Out-Null + } } - if ($TestResult) - { - $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys - } + $ValuesToCheck.Remove('Id') | Out-Null + $ValuesToCheck = Remove-M365DSCAuthenticationParameter -BoundParameters $ValuesToCheck - Write-Verbose -Message "Test-TargetResource returned $TestResult" + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - return $TestResult + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) + { + $testTargetResource = $false + } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/MSFT_AADConnectorGroupApplicationProxy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/MSFT_AADConnectorGroupApplicationProxy.psm1 index ccbbbd5ffc..cf9bfcb2d4 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/MSFT_AADConnectorGroupApplicationProxy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/MSFT_AADConnectorGroupApplicationProxy.psm1 @@ -326,13 +326,10 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) - { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys - } + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys Write-Verbose -Message "Test-TargetResource returned $testResult" diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCrossTenantAccessPolicyConfigurationDefault/MSFT_AADCrossTenantAccessPolicyConfigurationDefault.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCrossTenantAccessPolicyConfigurationDefault/MSFT_AADCrossTenantAccessPolicyConfigurationDefault.psm1 index e4983b150a..5851e16ace 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCrossTenantAccessPolicyConfigurationDefault/MSFT_AADCrossTenantAccessPolicyConfigurationDefault.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCrossTenantAccessPolicyConfigurationDefault/MSFT_AADCrossTenantAccessPolicyConfigurationDefault.psm1 @@ -350,6 +350,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -367,7 +368,7 @@ function Test-TargetResource if (-Not $testResult) { Write-Verbose -Message "Difference found for $key" - $testResult = $false + $testTargetResource = $false break } @@ -378,17 +379,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCrossTenantAccessPolicyConfigurationPartner/MSFT_AADCrossTenantAccessPolicyConfigurationPartner.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCrossTenantAccessPolicyConfigurationPartner/MSFT_AADCrossTenantAccessPolicyConfigurationPartner.psm1 index 73af68b8ec..1c6ac48434 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCrossTenantAccessPolicyConfigurationPartner/MSFT_AADCrossTenantAccessPolicyConfigurationPartner.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCrossTenantAccessPolicyConfigurationPartner/MSFT_AADCrossTenantAccessPolicyConfigurationPartner.psm1 @@ -385,6 +385,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() $testResult = $true + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) @@ -402,7 +403,7 @@ function Test-TargetResource if (-Not $testResult) { Write-Verbose -Message "Difference found for $key" - $testResult = $false + $testTargetResource = $false break } @@ -413,17 +414,17 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - if ($testResult) + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + if (-not $TestResult) { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + $testTargetResource = $false } - - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 index 368dc4b401..5346c2e532 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 @@ -530,9 +530,12 @@ function Test-TargetResource -DesiredValues $PSBoundParameters ` -ValuesToCheck $ValuesToCheck.Keys - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + if (-not $TestResult) + { + $testTargetResource = $false + } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCPolicyConfig/MSFT_SCPolicyConfig.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCPolicyConfig/MSFT_SCPolicyConfig.psm1 index fca3493151..de0d666149 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCPolicyConfig/MSFT_SCPolicyConfig.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCPolicyConfig/MSFT_SCPolicyConfig.psm1 @@ -1185,6 +1185,7 @@ function Test-TargetResource #Compare Cim instances $testResult = $true + $testTargetResource = $true foreach ($key in $PSBoundParameters.Keys) { $source = $PSBoundParameters.$key @@ -1197,6 +1198,7 @@ function Test-TargetResource if (-not $testResult) { + $testTargetResource = $false break } @@ -1209,9 +1211,12 @@ function Test-TargetResource -DesiredValues $PSBoundParameters ` -ValuesToCheck $ValuesToCheck.Keys - Write-Verbose -Message "Test-TargetResource returned $testResult" - - return $testResult + if (-not $testResult) + { + $testTargetResource = $false + } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 index 8922832fb0..ade8a6832c 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 @@ -858,7 +858,7 @@ function Compare-M365DSCComplexObject -DifferenceObject ($differenceObject) } - if ($null -ne $compareResult) + if ($null -ne $compareResult -and -not $compareResult) { Write-Verbose -Message "Configuration drift - simple object key: $key" Write-Verbose -Message "Source {$sourceValue}" From dc2b461b631120265921b756830d1ac1dd909674 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 31 Jan 2025 21:59:38 +0000 Subject: [PATCH 03/30] Updated Resources and Cmdlet documentation pages --- docs/docs/resources/exchange/EXOMigrationEndpoint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/resources/exchange/EXOMigrationEndpoint.md b/docs/docs/resources/exchange/EXOMigrationEndpoint.md index e8a3134503..f5f867aba2 100644 --- a/docs/docs/resources/exchange/EXOMigrationEndpoint.md +++ b/docs/docs/resources/exchange/EXOMigrationEndpoint.md @@ -9,7 +9,7 @@ | **AppID** | Write | String | The Application ID used for authentication. | | | **AppSecretKeyVaultUrl** | Write | String | The URL of the Key Vault that stores the application secret. | | | **Authentication** | Write | String | The authentication method for the migration endpoint. | | -| **EndpointType** | Write | String | The type of migration endpoint. | `IMAP` | +| **EndpointType** | Write | String | The type of migration endpoint. | `IMAP`, `ExchangeRemoteMove` | | **ExchangeServer** | Write | String | The Exchange Server address for the migration endpoint. | | | **MailboxPermission** | Write | String | The mailbox permission for the migration endpoint. | | | **MaxConcurrentIncrementalSyncs** | Write | String | The maximum number of concurrent incremental syncs. | | From c8219c0e36be9f8c00707dc31f0b06e46828bf47 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 3 Feb 2025 08:10:17 -0500 Subject: [PATCH 04/30] Various Fixes --- CHANGELOG.md | 5 ++ .../MSFT_AADConditionalAccessPolicy.psm1 | 10 ++-- ...MSFT_AADConditionalAccessPolicy.schema.mof | 2 +- ...FT_IntuneDeviceConfigurationPolicyiOS.psm1 | 54 +++++++++++++++---- 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccb904dc54..d27d2f8385 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change log for Microsoft365DSC +# UNRELEASED + +* AADConditionalAccessPolicy + * Changed the InsiderRiskTypes property to a string array. + # 1.25.129.3 * EXOMigrationEndpoint diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 index 331a56ecc3..e5addeac3f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 @@ -225,7 +225,7 @@ function Get-TargetResource [Parameter()] [ValidateSet('minor', 'moderate', 'elevated', 'unknownFutureValue')] - [System.String] + [System.String[]] $InsiderRiskLevels, #generic @@ -727,7 +727,7 @@ function Get-TargetResource TransferMethods = [System.String]$Policy.Conditions.AuthenticationFlows.TransferMethods #Standard part TermsOfUse = $termOfUseName - InsiderRiskLevels = $Policy.Conditions.InsiderRiskLevels + InsiderRiskLevels = $Policy.Conditions.InsiderRiskLevels.Split(',') Ensure = 'Present' Credential = $Credential ApplicationSecret = $ApplicationSecret @@ -968,7 +968,7 @@ function Set-TargetResource [Parameter()] [ValidateSet('minor', 'moderate', 'elevated', 'unknownFutureValue')] - [System.String] + [System.String[]] $InsiderRiskLevels, #generic @@ -1687,7 +1687,7 @@ function Set-TargetResource if ([String]::IsNullOrEmpty($InsiderRiskLevels) -eq $false) { - $conditions.Add('insiderRiskLevels', $InsiderRiskLevels) + $conditions.Add('insiderRiskLevels', $($InsiderRiskLevels -join ',')) } Write-Verbose -Message 'Set-Targetresource: process risk levels and app types' @@ -2165,7 +2165,7 @@ function Test-TargetResource [Parameter()] [ValidateSet('minor', 'moderate', 'elevated', 'unknownFutureValue')] - [System.String] + [System.String[]] $InsiderRiskLevels, #generic diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.schema.mof index 556ec8f895..339c98e9b3 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.schema.mof @@ -51,7 +51,7 @@ class MSFT_AADConditionalAccessPolicy : OMI_BaseResource [Write, Description("Name of the associated authentication strength policy.")] String AuthenticationStrength; [Write, Description("Names of the associated authentication flow transfer methods. Possible values are '', 'deviceCodeFlow', 'authenticationTransfer', or 'deviceCodeFlow,authenticationTransfer'.")] String TransferMethods; [Write, Description("Authentication context class references.")] String AuthenticationContexts[]; - [Write, Description("Insider risk levels conditions."), ValueMap{"minor", "moderate", "elevated", "unknownFutureValue"}, Values{"minor", "moderate", "elevated", "unknownFutureValue"}] String InsiderRiskLevels; + [Write, Description("Insider risk levels conditions."), ValueMap{"minor", "moderate", "elevated", "unknownFutureValue"}, Values{"minor", "moderate", "elevated", "unknownFutureValue"}] String InsiderRiskLevels[]; [Write, Description("Specify if the Azure AD CA Policy should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write, Description("Credentials for the Microsoft Graph delegated permissions."), EmbeddedInstance("MSFT_Credential")] string Credential; [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationPolicyiOS/MSFT_IntuneDeviceConfigurationPolicyiOS.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationPolicyiOS/MSFT_IntuneDeviceConfigurationPolicyiOS.psm1 index 49e066acc4..7dd7765973 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationPolicyiOS/MSFT_IntuneDeviceConfigurationPolicyiOS.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationPolicyiOS/MSFT_IntuneDeviceConfigurationPolicyiOS.psm1 @@ -3126,7 +3126,7 @@ function Export-TargetResource $Results.Remove('CompliantAppsList') | Out-Null } } - if ($Results.MediaContentRatingAustralia) + if ($Results.MediaContentRatingAustralia.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingAustralia -CIMInstanceName MicrosoftGraphmediacontentratingaustralia if ($complexTypeStringResult) @@ -3138,7 +3138,11 @@ function Export-TargetResource $Results.Remove('MediaContentRatingAustralia') | Out-Null } } - if ($Results.MediaContentRatingCanada) + else + { + $Results.Remove('MediaContentRatingAustralia') | Out-Null + } + if ($Results.MediaContentRatingCanada.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingCanada -CIMInstanceName MicrosoftGraphmediacontentratingcanada if ($complexTypeStringResult) @@ -3150,7 +3154,11 @@ function Export-TargetResource $Results.Remove('MediaContentRatingCanada') | Out-Null } } - if ($Results.MediaContentRatingFrance) + else + { + $Results.Remove('MediaContentRatingCanada') | Out-Null + } + if ($Results.MediaContentRatingFrance.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingFrance -CIMInstanceName MicrosoftGraphmediacontentratingfrance if ($complexTypeStringResult) @@ -3162,7 +3170,11 @@ function Export-TargetResource $Results.Remove('MediaContentRatingFrance') | Out-Null } } - if ($Results.MediaContentRatingGermany) + else + { + $Results.Remove('MediaContentRatingFrance') | Out-Null + } + if ($Results.MediaContentRatingGermany.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingGermany -CIMInstanceName MicrosoftGraphmediacontentratinggermany if ($complexTypeStringResult) @@ -3174,7 +3186,11 @@ function Export-TargetResource $Results.Remove('MediaContentRatingGermany') | Out-Null } } - if ($Results.MediaContentRatingIreland) + else + { + $Results.Remove('MediaContentRatingGermany') | Out-Null + } + if ($Results.MediaContentRatingIreland.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingIreland -CIMInstanceName MicrosoftGraphmediacontentratingireland if ($complexTypeStringResult) @@ -3186,7 +3202,11 @@ function Export-TargetResource $Results.Remove('MediaContentRatingIreland') | Out-Null } } - if ($Results.MediaContentRatingJapan) + else + { + $Results.Remove('MediaContentRatingIreland') | Out-Null + } + if ($Results.MediaContentRatingJapan.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingJapan -CIMInstanceName MicrosoftGraphmediacontentratingjapan if ($complexTypeStringResult) @@ -3198,7 +3218,11 @@ function Export-TargetResource $Results.Remove('MediaContentRatingJapan') | Out-Null } } - if ($Results.MediaContentRatingNewZealand) + else + { + $Results.Remove('MediaContentRatingJapan') | Out-Null + } + if ($Results.MediaContentRatingNewZealand.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingNewZealand -CIMInstanceName MicrosoftGraphmediacontentratingnewzealand if ($complexTypeStringResult) @@ -3210,7 +3234,11 @@ function Export-TargetResource $Results.Remove('MediaContentRatingNewZealand') | Out-Null } } - if ($Results.MediaContentRatingUnitedKingdom) + else + { + $Results.Remove('MediaContentRatingNewZealand') | Out-Null + } + if ($Results.MediaContentRatingUnitedKingdom.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingUnitedKingdom -CIMInstanceName MicrosoftGraphmediacontentratingunitedkingdom if ($complexTypeStringResult) @@ -3222,7 +3250,11 @@ function Export-TargetResource $Results.Remove('MediaContentRatingUnitedKingdom') | Out-Null } } - if ($Results.MediaContentRatingUnitedStates) + else + { + $Results.Remove('MediaContentRatingUnitedKingdom') | Out-Null + } + if ($Results.MediaContentRatingUnitedStates.Count -gt 0) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.MediaContentRatingUnitedStates -CIMInstanceName MicrosoftGraphmediacontentratingunitedstates if ($complexTypeStringResult) @@ -3234,6 +3266,10 @@ function Export-TargetResource $Results.Remove('MediaContentRatingUnitedStates') | Out-Null } } + else + { + $Results.Remove('MediaContentRatingUnitedStates') | Out-Null + } if ($Results.NetworkUsageRules) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.NetworkUsageRules -CIMInstanceName MicrosoftGraphiosnetworkusagerule From 78df7043d202c38083ca60a50103c4b217c80b7b Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 3 Feb 2025 08:54:26 -0500 Subject: [PATCH 05/30] Update MSFT_AADConditionalAccessPolicy.psm1 --- .../MSFT_AADConditionalAccessPolicy.psm1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 index e5addeac3f..ca02585337 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 @@ -649,6 +649,12 @@ function Get-TargetResource } } + $InsiderRiskLevelsValue = $null + if (-not [System.String]::IsNullOrEmpty($Policy.Conditions.InsiderRiskLevels)) + { + $InsiderRiskLevelsValue = $Policy.Conditions.InsiderRiskLevels.Split(',') + } + $result = @{ DisplayName = $Policy.DisplayName Id = $Policy.Id @@ -727,7 +733,7 @@ function Get-TargetResource TransferMethods = [System.String]$Policy.Conditions.AuthenticationFlows.TransferMethods #Standard part TermsOfUse = $termOfUseName - InsiderRiskLevels = $Policy.Conditions.InsiderRiskLevels.Split(',') + InsiderRiskLevels = $InsiderRiskLevelsValue Ensure = 'Present' Credential = $Credential ApplicationSecret = $ApplicationSecret From 8d927162cf9f783c39ba1042ef7924d3902222c4 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Mon, 3 Feb 2025 14:18:43 +0000 Subject: [PATCH 06/30] Updated Resources and Cmdlet documentation pages --- docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md b/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md index 489253d06c..4767b232df 100644 --- a/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md +++ b/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md @@ -54,7 +54,7 @@ | **AuthenticationStrength** | Write | String | Name of the associated authentication strength policy. | | | **TransferMethods** | Write | String | Names of the associated authentication flow transfer methods. Possible values are '', 'deviceCodeFlow', 'authenticationTransfer', or 'deviceCodeFlow,authenticationTransfer'. | | | **AuthenticationContexts** | Write | StringArray[] | Authentication context class references. | | -| **InsiderRiskLevels** | Write | String | Insider risk levels conditions. | `minor`, `moderate`, `elevated`, `unknownFutureValue` | +| **InsiderRiskLevels** | Write | StringArray[] | Insider risk levels conditions. | `minor`, `moderate`, `elevated`, `unknownFutureValue` | | **Ensure** | Write | String | Specify if the Azure AD CA Policy should exist or not. | `Present`, `Absent` | | **Credential** | Write | PSCredential | Credentials for the Microsoft Graph delegated permissions. | | | **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | From 6515c898fd8d79991af43c7887fa7287a5967b37 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Mon, 3 Feb 2025 14:21:55 +0000 Subject: [PATCH 07/30] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 5f6af0a306..4b01e1ce40 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -3543,7 +3543,7 @@ "Option": "Write" }, { - "CIMType": "String", + "CIMType": "String[]", "Name": "InsiderRiskLevels", "Option": "Write" }, From 6f4167970a8f6c2c0f2a38e09ce5f25f8786b6ea Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 3 Feb 2025 11:40:54 -0500 Subject: [PATCH 08/30] Updated Export logic --- CHANGELOG.md | 2 + .../MSFT_AADApplication.psm1 | 29 +++++- ...IntuneDeviceCompliancePolicyWindows10.psm1 | 23 ++++- .../MSFT_PPTenantIsolationSettings.psm1 | 26 ++++- .../MSFT_SCLabelPolicy.psm1 | 25 ++++- .../MSFT_SCSensitivityLabel.psm1 | 98 +++++++++++++++++-- .../MSFT_SPOTheme/MSFT_SPOTheme.psm1 | 24 ++++- .../MSFT_SPOUserProfileProperty.psm1 | 29 +++++- 8 files changed, 235 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d27d2f8385..2ae55c46d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * AADConditionalAccessPolicy * Changed the InsiderRiskTypes property to a string array. +* MISC + * Changed the CIMInstance logic of various resources to us common logic. # 1.25.129.3 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADApplication/MSFT_AADApplication.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADApplication/MSFT_AADApplication.psm1 index f950fd7afb..193e7ac6d0 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADApplication/MSFT_AADApplication.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADApplication/MSFT_AADApplication.psm1 @@ -1598,9 +1598,29 @@ function Export-TargetResource { $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results + if ($Results.Permissions.Count -gt 0) { - $Results.Permissions = Get-M365DSCAzureADAppPermissionsAsString $Results.Permissions + $complexMapping = @( + @{ + Name = 'Permissions' + CimInstanceName = 'MSFT_AADApplicationPermission' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.Permissions ` + -CIMInstanceName 'MSFT_AADApplicationPermission' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.Permissions = $complexTypeStringResult + } + else + { + $Results.Remove('Permissions') | Out-Null + } } if ($null -ne $Results.Api) @@ -1733,7 +1753,6 @@ function Export-TargetResource } } - if ($null -ne $Results.KeyCredentials) { $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` @@ -1793,16 +1812,20 @@ function Export-TargetResource if ($null -ne $Results.Permissions) { $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` - -ParameterName 'Permissions' + -ParameterName 'Permissions' ` + -IsCIMArray:$True } + if ($Results.OptionalClaims) { $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'OptionalClaims' -IsCIMArray:$False } + if ($Results.OnPremisesPublishing) { $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'OnPremisesPublishing' -IsCIMArray:$False } + if ($Results.AuthenticationBehaviors) { $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'AuthenticationBehaviors' -IsCIMArray:$False diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCompliancePolicyWindows10/MSFT_IntuneDeviceCompliancePolicyWindows10.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCompliancePolicyWindows10/MSFT_IntuneDeviceCompliancePolicyWindows10.psm1 index 4724190c7a..50be26fa9f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCompliancePolicyWindows10/MSFT_IntuneDeviceCompliancePolicyWindows10.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCompliancePolicyWindows10/MSFT_IntuneDeviceCompliancePolicyWindows10.psm1 @@ -954,10 +954,21 @@ function Export-TargetResource $Results.Remove('ValidOperatingSystemBuildRanges') | Out-Null } } - if ($Results.Assignments) + if ($null -ne $Results.Assignments) { - $complexTypeStringResult = Get-M365DSCAssignmentsAsString -Params $Results.Assignments - if ($complexTypeStringResult) + $complexMapping = @( + @{ + Name = 'Assignments' + CimInstanceName = 'MSFT_DeviceManagementConfigurationPolicyAssignments' + sRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.Assignments ` + -CIMInstanceName 'MSFT_DeviceManagementConfigurationPolicyAssignments' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) { $Results.Assignments = $complexTypeStringResult } @@ -971,10 +982,12 @@ function Export-TargetResource -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential + if ($Results.ValidOperatingSystemBuildRanges) { $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'ValidOperatingSystemBuildRanges' } + if ($Results.Assignments) { $isCIMArray = $false @@ -982,7 +995,9 @@ function Export-TargetResource { $isCIMArray = $true } - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Assignments' -IsCIMArray:$isCIMArray + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'Assignments' ` + -IsCIMArray:$isCIMArray } $dscContent += $currentDSCBlock diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_PPTenantIsolationSettings/MSFT_PPTenantIsolationSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_PPTenantIsolationSettings/MSFT_PPTenantIsolationSettings.psm1 index baab2973c4..9c814828a6 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_PPTenantIsolationSettings/MSFT_PPTenantIsolationSettings.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_PPTenantIsolationSettings/MSFT_PPTenantIsolationSettings.psm1 @@ -673,9 +673,28 @@ function Export-TargetResource if ($Results -is [System.Collections.Hashtable] -and $Results.Count -gt 1) { - if ($Results.Rules.Count -gt 0) + if ($null -ne $Results.Rules) { - $Results.Rules = Get-M365DSCTenantIsolationRule $Results.Rules + $complexMapping = @( + @{ + Name = 'Rules' + CimInstanceName = 'MSFT_PPTenantRule' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.Rules ` + -CIMInstanceName 'MSFT_PPTenantRule' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.Rules = $complexTypeStringResult + } + else + { + $Results.Remove('Rules') | Out-Null + } } $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results @@ -688,7 +707,8 @@ function Export-TargetResource if ($null -ne $Results.Rules) { $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` - -ParameterName 'Rules' + -ParameterName 'Rules' ` + -IsCIMArray:$True } $dscContent += $currentDSCBlock diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCLabelPolicy/MSFT_SCLabelPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCLabelPolicy/MSFT_SCLabelPolicy.psm1 index 8251861bbc..cfa1b4179d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCLabelPolicy/MSFT_SCLabelPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCLabelPolicy/MSFT_SCLabelPolicy.psm1 @@ -856,7 +856,26 @@ function Export-TargetResource if ($null -ne $Results.AdvancedSettings) { - $Results.AdvancedSettings = ConvertTo-AdvancedSettingsString -AdvancedSettings $Results.AdvancedSettings + $complexMapping = @( + @{ + Name = 'AdvancedSettings' + CimInstanceName = 'MSFT_SCLabelSetting' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.AdvancedSettings ` + -CIMInstanceName 'MSFT_SCLabelSetting' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.AdvancedSettings = $complexTypeStringResult + } + else + { + $Results.Remove('AdvancedSettings') | Out-Null + } } $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` @@ -868,7 +887,9 @@ function Export-TargetResource -Credential $Credential if ($null -ne $Results.AdvancedSettings) { - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'AdvancedSettings' + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'AdvancedSettings' ` + -IsCIMArray:$True } Write-Host $Global:M365DSCEmojiGreenCheckMark diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCSensitivityLabel/MSFT_SCSensitivityLabel.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCSensitivityLabel/MSFT_SCSensitivityLabel.psm1 index 7a1f174e2c..f612a1a7ac 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCSensitivityLabel/MSFT_SCSensitivityLabel.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCSensitivityLabel/MSFT_SCSensitivityLabel.psm1 @@ -1598,17 +1598,96 @@ function Export-TargetResource if ($null -ne $Results.AdvancedSettings) { - $Results.AdvancedSettings = ConvertTo-AdvancedSettingsString -AdvancedSettings $Results.AdvancedSettings + $complexMapping = @( + @{ + Name = 'AdvancedSettings' + CimInstanceName = 'MSFT_SCLabelSetting' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.AdvancedSettings ` + -CIMInstanceName 'MSFT_SCLabelSetting' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.AdvancedSettings = $complexTypeStringResult + } + else + { + $Results.Remove('AdvancedSettings') | Out-Null + } } if ($null -ne $Results.LocaleSettings) { - $Results.LocaleSettings = ConvertTo-LocaleSettingsString -LocaleSettings $Results.LocaleSettings + $complexMapping = @( + @{ + Name = 'LocaleSettings' + CimInstanceName = 'MSFT_SCLabelLocaleSettings' + IsRequired = $False + } + @{ + Name = 'LabelSettings' + CimInstanceName = 'MSFT_SCLabelSetting' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.LocaleSettings ` + -CIMInstanceName 'MSFT_SCLabelLocaleSettings' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.LocaleSettings = $complexTypeStringResult + } + else + { + $Results.Remove('LocaleSettings') | Out-Null + } } + if ($null -ne $Results.AutoLabelingSettings) { - $Results.AutoLabelingSettings = ConvertTo-AutoLabelingSettingsString -AutoLabelingSettings $Results.AutoLabelingSettings + $complexMapping = @( + @{ + Name = 'AutoLabelingSettings' + CimInstanceName = 'MSFT_SCSLAutoLabelingSettings' + IsRequired = $False + } + @{ + Name = 'Groups' + CimInstanceName = 'MSFT_SCSLSensitiveInformationGroup' + IsRequired = $False + } + @{ + Name = 'SensitiveInformationType' + CimInstanceName = 'MSFT_SCSLSensitiveInformationType' + IsRequired = $False + } + @{ + Name = 'TrainableClassifier' + CimInstanceName = 'MSFT_SCSLTrainableClassifiers' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.AutoLabelingSettings ` + -CIMInstanceName 'MSFT_SCSLAutoLabelingSettings' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.AutoLabelingSettings = $complexTypeStringResult + } + else + { + $Results.Remove('AutoLabelingSettings') | Out-Null + } } + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` @@ -1616,17 +1695,24 @@ function Export-TargetResource -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential + if ($null -ne $Results.AdvancedSettings) { - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'AdvancedSettings' + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'AdvancedSettings' ` + -IsCIMArray:$True } if ($null -ne $Results.LocaleSettings) { - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'LocaleSettings' + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'LocaleSettings' ` + -IsCIMArray:$True } if ($null -ne $Results.AutoLabelingSettings) { - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'AutoLabelingSettings' + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'AutoLabelingSettings' ` + -IsCIMArray:$True } Write-Host $Global:M365DSCEmojiGreenCheckMark diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SPOTheme/MSFT_SPOTheme.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SPOTheme/MSFT_SPOTheme.psm1 index 5e4066479f..41683bf973 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SPOTheme/MSFT_SPOTheme.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SPOTheme/MSFT_SPOTheme.psm1 @@ -432,7 +432,29 @@ function Export-TargetResource $Results = Get-TargetResource @Params $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results - $Results.Palette = ConvertTo-SPOThemePalettePropertyString $Results.Palette + if ($null -ne $Results.Palette) + { + $complexMapping = @( + @{ + Name = 'OptionalClaims' + CimInstanceName = 'MSFT_SPOThemePaletteProperty' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.Palette ` + -CIMInstanceName 'MSFT_SPOThemePaletteProperty' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.Palette = $complexTypeStringResult + } + else + { + $Results.Remove('Palette') | Out-Null + } + } $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` -ConnectionMode $ConnectionMode ` -ModulePath $PSScriptRoot ` diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SPOUserProfileProperty/MSFT_SPOUserProfileProperty.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SPOUserProfileProperty/MSFT_SPOUserProfileProperty.psm1 index 06c347aa47..d491711ba1 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SPOUserProfileProperty/MSFT_SPOUserProfileProperty.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SPOUserProfileProperty/MSFT_SPOUserProfileProperty.psm1 @@ -353,7 +353,30 @@ function Export-TargetResource $Global:M365DSCExportResourceInstancesCount++ } - $Results.Properties = ConvertTo-M365DSCSPOUserProfilePropertyInstanceString -Properties $Results.Properties + if ($null -ne $Results.Properties) + { + $complexMapping = @( + @{ + Name = 'Properties' + CimInstanceName = 'MSFT_SPOUserProfilePropertyInstance' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.Properties ` + -CIMInstanceName 'MSFT_SPOUserProfilePropertyInstance' ` + -ComplexTypeMapping $complexMapping + + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.Properties = $complexTypeStringResult + } + else + { + $Results.Remove('Properties') | Out-Null + } + } + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` @@ -363,7 +386,9 @@ function Export-TargetResource -Credential $Credential if ($null -ne $Results.Properties) { - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Properties' + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'Properties' ` + -IsCIMArray:$True } $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` From dccc6be716e3a5ccb60dc93a1f03b8fb0cdcee54 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 3 Feb 2025 16:05:22 -0500 Subject: [PATCH 09/30] Initial --- .../MSFT_CommerceSelfServicePurchase.psm1 | 380 ++++++++++++++++++ ...SFT_CommerceSelfServicePurchase.schema.mof | 15 + .../readme.md | 6 + .../settings.json | 25 ++ .../CommerceSelfServicePurchase/1-Create.ps1 | 26 ++ .../CommerceSelfServicePurchase/2-Update.ps1 | 26 ++ .../CommerceSelfServicePurchase/3-Remove.ps1 | 26 ++ Modules/Microsoft365DSC/Microsoft365DSC.psd1 | 1 + .../Microsoft365DSC/Modules/M365DSCUtil.psm1 | 4 +- .../M365DSCLicensingHelper.psm1 | 30 ++ .../Microsoft365DSC.ResourceName.Tests.ps1 | 178 ++++++++ 11 files changed, 715 insertions(+), 2 deletions(-) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/3-Remove.ps1 create mode 100644 Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 new file mode 100644 index 0000000000..0cee58fa55 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 @@ -0,0 +1,380 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $ProductId, + + [Parameter(Mandatory = $true)] + [System.String] + $ProductName, + + [Parameter()] + [ValidateSet('Enabled', 'Disabled')] + [System.String] + $PolicyValue, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [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 + ) + + New-M365DSCConnection -Workload 'Licensing' ` + -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 + { + if ($null -ne $Script:exportedInstances -and $Script:ExportMode) + { + $instance = $Script:exportedInstances.items | Where-Object -FilterScript {$_.ProductId -eq $ProductId} + } + else + { + $uri = (Get-MSCloudLoginConnectionProfile -Workload 'Licensing').HostUrl + "/v1/policies/AllowSelfServicePurchase/products/$($ProductId)" + $instance = Invoke-M365DSCLicensingWebRequest -Uri $uri -Method 'GET' + } + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + ProductId = $instance.ProductId + ProductName = $instance.ProductName + PolicyValue = $instance.PolicyValue + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + 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 + ( + [Parameter(Mandatory = $true)] + [System.String] + $ProductId, + + [Parameter(Mandatory = $true)] + [System.String] + $ProductName, + + [Parameter()] + [System.String] + [ValidateSet('Enabled', 'Disabled')] + $PolicyValue, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [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 + ) + + #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 + + if ($Ensure -eq 'Absent') + { + throw "Ensure cannot be absent. This resource can only update existing Self Service Purchase Policies." + } + + $uri = (Get-MSCloudLoginConnectionProfile -Workload 'Licensing').HostUrl + "/v1/policies/AllowSelfServicePurchase/products/$($ProductId)" + $body = @{ + policyValue = $PolicyValue + } + Write-Verbose -Message "Updating Policy for {$ProductName} to value {$PolicyValue}" + Invoke-M365DSCLicensingWebRequest -Uri $uri -Method 'PUT' -Body $body | Out-Null +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $ProductId, + + [Parameter(Mandatory = $true)] + [System.String] + $ProductName, + + [Parameter()] + [System.String] + [ValidateSet('Enabled', 'Disabled')] + $PolicyValue, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [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 + ) + + #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 'Licensing' ` + -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 + $uri = (Get-MSCloudLoginConnectionProfile -Workload 'Licensing').HostUrl + '/v1/policies/AllowSelfServicePurchase/products' + [array] $Script:exportedInstances = Invoke-M365DSCLicensingWebRequest -Uri $uri -Method 'GET' + + $i = 1 + $dscContent = '' + if ($Script:exportedInstances.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + foreach ($config in $Script:exportedInstances.items) + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + + $displayedKey = $config.ProductName + Write-Host " |---[$i/$($Script:exportedInstances.items.Count)] $displayedKey" -NoNewline + $params = @{ + ProductId = $config.productId + ProductName = $config.productName + 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_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.schema.mof new file mode 100644 index 0000000000..56e6bf5d1f --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.schema.mof @@ -0,0 +1,15 @@ +[ClassVersion("1.0.0.0"), FriendlyName("CommerceSelfServicePurchase")] +class MSFT_CommerceSelfServicePurchase : OMI_BaseResource +{ + [Key, Description("Unique ID of the product.")] String ProductId; + [Write, Description("Name of the product")] String ProductName; + [Write, Description("Can be Enabled or Disabled."), ValueMap{"Enabled","Disabled"}, Values{"Enabled","Disabled"}] String PolicyValue; + + [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[]; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/readme.md new file mode 100644 index 0000000000..d5244037e0 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/readme.md @@ -0,0 +1,6 @@ + +# CommerceSelfServicePurchase + +## Description + +Manages the Self Purchase policies in commerce. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/settings.json new file mode 100644 index 0000000000..e79b4e3f34 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/settings.json @@ -0,0 +1,25 @@ +{ + "resourceName": "CommerceSelfServicePurchase", + "description": "Manages the Self Purchase policies in commerce.", + "roles": {}, + "permissions": { + "aeb86249-8ea3-49e2-900b-54cc8e308f85": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [ + { + "name": "Policy.Read.AllowSelfServicePurchase" + } + ], + "update": [ + { + "name": "Policy.ReadWrite.AllowSelfServicePurchase" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/1-Create.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/1-Create.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/2-Update.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/2-Update.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/3-Remove.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/3-Remove.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 index 2ca6216f5a..e6cf6d519d 100644 --- a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 +++ b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 @@ -84,6 +84,7 @@ 'Modules/WorkloadHelpers/M365DSCAzureDevOPSHelper.psm1', 'Modules/WorkloadHelpers/M365DSCDefenderHelper.psm1', 'Modules/WorkloadHelpers/M365DSCFabricHelper.psm1', + 'Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1', 'Modules/M365DSCConfigurationHelper.psm1' ) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index 5d201c08be..d5e67ebe4f 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -667,7 +667,7 @@ function Test-M365DSCParameterState $propertyName = $existingDrift.Keys[0] $value = $existingDrift."$propertyName" $start = $value.IndexOf('') - $currentValue = $value.Substring(0, $start).Replace('', '') + $currentValue = $value.Substring(0, $start).Replace('', '') $desiredValue = $value.Substring($start+15, ($value.Length)-($start+15)).Replace('', '').Replace('', '') $DriftObject.DriftInfo.Add($propertyName, @{ PropertyName = $propertyName @@ -1881,7 +1881,7 @@ function New-M365DSCConnection param ( [Parameter(Mandatory = $true)] - [ValidateSet('AdminAPI', 'Azure', 'AzureDevOPS', 'DefenderForEndPoint', 'ExchangeOnline', 'Fabric', 'Intune', ` + [ValidateSet('AdminAPI', 'Azure', 'AzureDevOPS', 'DefenderForEndPoint', 'ExchangeOnline', 'Fabric', 'Intune', 'Licensing', ` 'SecurityComplianceCenter', 'PnP', 'PowerPlatforms', ` 'MicrosoftTeams', 'MicrosoftGraph', 'SharePointOnlineREST', 'Tasks', 'AdminAPI')] [System.String] diff --git a/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 b/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 new file mode 100644 index 0000000000..cddc8e7de3 --- /dev/null +++ b/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 @@ -0,0 +1,30 @@ +function Invoke-M365DSCLicensingWebRequest +{ + [OutputType([PSCustomObject])] + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [System.String] + $Uri, + + [Parameter()] + [System.String] + $Method = 'GET', + + [Parameter()] + [System.Collections.Hashtable] + $Body + ) + + $headers = @{ + Authorization = (Get-MSCloudLoginConnectionProfile -Workload 'Licensing').AccessToken + } + + $response = Invoke-WebRequest -Method $Method ` + -Uri $Uri ` + -Headers $headers ` + -Body $Body ` + -UseBasicParsing + $result = ConvertFrom-Json $response.Content + return $result +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 new file mode 100644 index 0000000000..780e0f343d --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 @@ -0,0 +1,178 @@ +[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) + +$CurrentScriptPath = $PSCommandPath.Split('\') +$CurrentScriptName = $CurrentScriptPath[$CurrentScriptPath.Length -1] +$ResourceName = $CurrentScriptName.Split('.')[1] +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource $ResourceName -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 New-M365DSCConnection -MockWith { + return "Credentials" + } + + ##TODO - Mock any Remove/Set/New cmdlets + + # 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 instance should exist but it DOES NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return $null + Mock -CommandName Get-Cmdlet -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 a new instance from the Set method' { + ##TODO - Replace the New-Cmdlet by the appropriate one + Set-TargetResource @testParams + Should -Invoke -CommandName New-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Absent' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should remove the instance from the Set method' { + Set-TargetResource @testParams + ##TODO - Replace the Remove-Cmdlet by the appropriate one + Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return the desired values + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "The instance exists and values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return a drift + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Set method' { + Set-TargetResource @testParams + ##TODO - Replace the Update-Cmdlet by the appropriate one + Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope From 68479b2bf6f9f00dad15d0a256c911d6b23b00b7 Mon Sep 17 00:00:00 2001 From: Pranay Kumar Karvi Date: Tue, 4 Feb 2025 11:12:43 +0530 Subject: [PATCH 10/30] Fix null reference error in AppRoleAssignedTo comparison --- .../MSFT_AADServicePrincipal.psm1 | 147 ++++++++++-------- 1 file changed, 84 insertions(+), 63 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 index e4d0baf0a7..8b22208ae2 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 @@ -604,43 +604,48 @@ function Set-TargetResource $appInstance = Get-MgApplication -Filter "AppId eq '$AppId'" Update-MgApplication -ApplicationId $appInstance.Id -IdentifierUris $IdentifierUris } - if ($AppRoleAssignedTo) - { - [Array]$currentPrincipals = $currentAADServicePrincipal.AppRoleAssignedTo.Identity - [Array]$desiredPrincipals = $AppRoleAssignedTo.Identity - - [Array]$differences = Compare-Object -ReferenceObject $currentPrincipals -DifferenceObject $desiredPrincipals - [Array]$membersToAdd = $differences | Where-Object -FilterScript { $_.SideIndicator -eq '=>' } - [Array]$membersToRemove = $differences | Where-Object -FilterScript { $_.SideIndicator -eq '<=' } + if ($AppRoleAssignedTo -and $AppRoleAssignedTo.Count -gt 0) { + [Array]$currentPrincipals = $currentAADServicePrincipal.AppRoleAssignedTo.Identity + [Array]$desiredPrincipals = $AppRoleAssignedTo.Identity + + # Ensure both arrays are initialized before using Compare-Object + if ($currentPrincipals -and $desiredPrincipals -and $currentPrincipals.Count -gt 0 -and $desiredPrincipals.Count -gt 0) { + [Array]$differences = Compare-Object -ReferenceObject $currentPrincipals -DifferenceObject $desiredPrincipals + [Array]$membersToAdd = $differences | Where-Object { $_.SideIndicator -eq '=>' } + [Array]$membersToRemove = $differences | Where-Object { $_.SideIndicator -eq '<=' } + } else { + Write-Verbose "Either currentPrincipals or desiredPrincipals is empty. Skipping comparison." + [Array]$membersToAdd = @() + [Array]$membersToRemove = @() + } - if ($differences.Count -gt 0) - { - if ($membersToAdd.Count -gt 0) - { - $AppRoleAssignedToValues = @() - foreach ($assignment in $AppRoleAssignedTo) - { - $AppRoleAssignedToValues += @{ - PrincipalType = $assignment.PrincipalType - Identity = $assignment.Identity - } + if ($differences.Count -gt 0) { + if ($membersToAdd.Count -gt 0) { + $AppRoleAssignedToValues = @() + foreach ($assignment in $AppRoleAssignedTo) { + $AppRoleAssignedToValues += @{ + PrincipalType = $assignment.PrincipalType + Identity = $assignment.Identity + } + } + foreach ($member in $membersToAdd) { + $assignment = $AppRoleAssignedToValues | Where-Object { $_.Identity -eq $member.InputObject } + + if ($assignment) { + if ($assignment.PrincipalType -eq 'User') { + Write-Verbose -Message "Retrieving user {$($assignment.Identity)}" + $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($assignment.Identity)')" + $PrincipalIdValue = $user.Id + } elseif ($assignment.PrincipalType -eq 'Group') { + Write-Verbose -Message "Retrieving group {$($assignment.Identity)}" + $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.Identity)'" + $PrincipalIdValue = $group.Id + } else { + Write-Verbose "Unknown PrincipalType: $($assignment.PrincipalType). Skipping." + continue } - foreach ($member in $membersToAdd) - { - $assignment = $AppRoleAssignedToValues | Where-Object -FilterScript { $_.Identity -eq $member.InputObject } - if ($assignment.PrincipalType -eq 'User') - { - Write-Verbose -Message "Retrieving user {$($assignment.Identity)}" - $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($assignment.Identity)')" - $PrincipalIdValue = $user.Id - } - else - { - Write-Verbose -Message "Retrieving group {$($assignment.Identity)}" - $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.Identity)'" - $PrincipalIdValue = $group.Id - } + if ($PrincipalIdValue) { $bodyParam = @{ principalId = $PrincipalIdValue resourceId = $currentAADServicePrincipal.ObjectID @@ -649,47 +654,63 @@ function Set-TargetResource Write-Verbose -Message "Adding member {$($member.InputObject.ToString())}" New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $currentAADServicePrincipal.ObjectID ` -BodyParameter $bodyParam | Out-Null + } else { + Write-Verbose "Failed to retrieve PrincipalId for {$($assignment.Identity)}. Skipping." } } + } + } - if ($membersToRemove.Count -gt 0) - { - $AppRoleAssignedToValues = @() - foreach ($assignment in $currentAADServicePrincipal.AppRoleAssignedTo) - { - $AppRoleAssignedToValues += @{ - PrincipalType = $assignment.PrincipalType - Identity = $assignment.Identity - } + if ($membersToRemove.Count -gt 0) { + $AppRoleAssignedToValues = @() + foreach ($assignment in $currentAADServicePrincipal.AppRoleAssignedTo) { + $AppRoleAssignedToValues += @{ + PrincipalType = $assignment.PrincipalType + Identity = $assignment.Identity + } + } + foreach ($member in $membersToRemove) { + $assignment = $AppRoleAssignedToValues | Where-Object { $_.Identity -eq $member.InputObject } + + if ($assignment) { + if ($assignment.PrincipalType -eq 'User') { + Write-Verbose -Message "Retrieving user {$($assignment.Identity)}" + $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($assignment.Identity)')" + $PrincipalIdValue = $user.Id + } elseif ($assignment.PrincipalType -eq 'Group') { + Write-Verbose -Message "Retrieving group {$($assignment.Identity)}" + $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.Identity)'" + $PrincipalIdValue = $group.Id + } else { + Write-Verbose "Unknown PrincipalType: $($assignment.PrincipalType). Skipping." + continue } - foreach ($member in $membersToRemove) - { - $assignment = $AppRoleAssignedToValues | Where-Object -FilterScript { $_.Identity -eq $member.InputObject } - if ($assignment.PrincipalType -eq 'User') - { - Write-Verbose -Message "Retrieving user {$($assignment.Identity)}" - $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($assignment.Identity)')" - $PrincipalIdValue = $user.Id - } - else - { - Write-Verbose -Message "Retrieving group {$($assignment.Identity)}" - $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.Identity)'" - $PrincipalIdValue = $group.Id - } + + if ($PrincipalIdValue) { Write-Verbose -Message "PrincipalID Value = '$PrincipalIdValue'" Write-Verbose -Message "ServicePrincipalId = '$($currentAADServicePrincipal.ObjectID)'" + $allAssignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $currentAADServicePrincipal.ObjectID - $assignmentToRemove = $allAssignments | Where-Object -FilterScript { $_.PrincipalId -eq $PrincipalIdValue } - Write-Verbose -Message "Removing member {$($member.InputObject.ToString())}" - Remove-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $currentAADServicePrincipal.ObjectID ` - -AppRoleAssignmentId $assignmentToRemove.Id | Out-Null + $assignmentToRemove = $allAssignments | Where-Object { $_.PrincipalId -eq $PrincipalIdValue } + + if ($assignmentToRemove) { + Write-Verbose -Message "Removing member {$($member.InputObject.ToString())}" + Remove-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $currentAADServicePrincipal.ObjectID ` + -AppRoleAssignmentId $assignmentToRemove.Id | Out-Null + } else { + Write-Verbose "No matching assignment found for PrincipalId $PrincipalIdValue. Skipping removal." + } + } else { + Write-Verbose "Failed to retrieve PrincipalId for {$($assignment.Identity)}. Skipping removal." } } } } + } +} + +Write-Verbose -Message 'Checking if owners need to be updated...' - Write-Verbose -Message 'Checking if owners need to be updated...' if ($null -ne $Owners) { From 82c56671b9b62509506b01c9838a48ecd9060aad Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 09:02:03 -0500 Subject: [PATCH 11/30] Update M365DSCUtil.psm1 --- .../Microsoft365DSC/Modules/M365DSCUtil.psm1 | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index 5d201c08be..c1bb6c1add 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -667,7 +667,7 @@ function Test-M365DSCParameterState $propertyName = $existingDrift.Keys[0] $value = $existingDrift."$propertyName" $start = $value.IndexOf('') - $currentValue = $value.Substring(0, $start).Replace('', '') + $currentValue = $value.Substring(0, $start).Replace('', '') $desiredValue = $value.Substring($start+15, ($value.Length)-($start+15)).Replace('', '').Replace('', '') $DriftObject.DriftInfo.Add($propertyName, @{ PropertyName = $propertyName @@ -3256,9 +3256,14 @@ function Update-M365DSCDependencies [Parameter()] [Switch] $ValidateOnly, + [Parameter()] [ValidateSet("CurrentUser", "AllUsers")] - $Scope = "AllUsers" + $Scope = "AllUsers", + + [Parameter()] + [System.String] + $Proxy ) try @@ -3273,6 +3278,12 @@ function Update-M365DSCDependencies $returnValue = @() + $params = @{} + if (-not [System.String]::IsNullOrEmpty($Proxy)) + { + $params.Add('Proxy', $Proxy) + } + foreach ($dependency in $dependencies) { Write-Progress -Activity 'Scanning dependencies' -PercentComplete ($i / $dependencies.Count * 100) @@ -3328,7 +3339,7 @@ function Update-M365DSCDependencies Remove-Module 'Microsoft.Graph.Authentication' -Force -ErrorAction SilentlyContinue } Remove-Module $dependency.ModuleName -Force -ErrorAction SilentlyContinue - Install-Module $dependency.ModuleName -RequiredVersion $dependency.RequiredVersion -AllowClobber -Force -Scope "$Scope" + Install-Module $dependency.ModuleName -RequiredVersion $dependency.RequiredVersion -AllowClobber -Force -Scope "$Scope" @Params } } @@ -4788,11 +4799,22 @@ function Update-M365DSCModule param( [Parameter()] [ValidateSet("CurrentUser", "AllUsers")] - $Scope = "AllUsers" + $Scope = "AllUsers", + + [Parameter()] + [System.String] + $Proxy ) + + $params = @{} + + if (-not [System.String]::IsNullOrEmpty($proxy)) + { + $params.Add('Proxy', $Proxy) + } try { - Update-Module -Name 'Microsoft365DSC' -ErrorAction Stop + Update-Module -Name 'Microsoft365DSC' @Params -ErrorAction Stop } catch { @@ -4821,7 +4843,7 @@ function Update-M365DSCModule -Source $($MyInvocation.MyCommand.Source) throw $_ } - Update-M365DSCDependencies -Scope $Scope + Update-M365DSCDependencies -Scope $Scope -Proxy $Proxy Uninstall-M365DSCOutdatedDependencies } From bfc15442f5b588bad39333b0cc0bd08154215f8d Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 09:02:28 -0500 Subject: [PATCH 12/30] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ae55c46d2..8c0c30228c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Changed the InsiderRiskTypes property to a string array. * MISC * Changed the CIMInstance logic of various resources to us common logic. + * Added support for specifying a proxy in Update-M365DSCModule. # 1.25.129.3 From d0dfdadf81d9f6c16cd58dd2001d1c88aed9b7e3 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Tue, 4 Feb 2025 14:38:20 +0000 Subject: [PATCH 13/30] Updated Resources and Cmdlet documentation pages --- docs/docs/user-guide/cmdlets/Update-M365DSCDependencies.md | 1 + docs/docs/user-guide/cmdlets/Update-M365DSCModule.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/docs/user-guide/cmdlets/Update-M365DSCDependencies.md b/docs/docs/user-guide/cmdlets/Update-M365DSCDependencies.md index 131a830e7c..478294c44e 100644 --- a/docs/docs/user-guide/cmdlets/Update-M365DSCDependencies.md +++ b/docs/docs/user-guide/cmdlets/Update-M365DSCDependencies.md @@ -15,6 +15,7 @@ This function does not generate any output. | Force | False | SwitchParameter | | | Specifies that all dependencies should be forcefully imported again. | | ValidateOnly | False | SwitchParameter | | | Specifies that the function should only return the dependencies that are not installed. | | Scope | False | Object | AllUsers | CurrentUser, AllUsers | Specifies the scope of the update of the module. The default value is AllUsers(needs to run as elevated user). | +| Proxy | False | String | | | | ## Examples diff --git a/docs/docs/user-guide/cmdlets/Update-M365DSCModule.md b/docs/docs/user-guide/cmdlets/Update-M365DSCModule.md index eeb0918741..ccedba9b41 100644 --- a/docs/docs/user-guide/cmdlets/Update-M365DSCModule.md +++ b/docs/docs/user-guide/cmdlets/Update-M365DSCModule.md @@ -13,6 +13,7 @@ This function does not generate any output. | Parameter | Required | DataType | Default Value | Allowed Values | Description | | --- | --- | --- | --- | --- | --- | | Scope | False | Object | AllUsers | CurrentUser, AllUsers | Specifies the scope of the update of the module. The default value is AllUsers(needs to run as elevated user). | +| Proxy | False | String | | | | ## Examples From 94f3af32d7af97dccff06c660943e2f952cd0b39 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 12:52:52 -0500 Subject: [PATCH 14/30] Revert "Fix null reference error in AppRoleAssignedTo comparison" --- .../MSFT_AADServicePrincipal.psm1 | 147 ++++++++---------- 1 file changed, 63 insertions(+), 84 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 index 8b22208ae2..e4d0baf0a7 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 @@ -604,48 +604,43 @@ function Set-TargetResource $appInstance = Get-MgApplication -Filter "AppId eq '$AppId'" Update-MgApplication -ApplicationId $appInstance.Id -IdentifierUris $IdentifierUris } - if ($AppRoleAssignedTo -and $AppRoleAssignedTo.Count -gt 0) { - [Array]$currentPrincipals = $currentAADServicePrincipal.AppRoleAssignedTo.Identity - [Array]$desiredPrincipals = $AppRoleAssignedTo.Identity - - # Ensure both arrays are initialized before using Compare-Object - if ($currentPrincipals -and $desiredPrincipals -and $currentPrincipals.Count -gt 0 -and $desiredPrincipals.Count -gt 0) { - [Array]$differences = Compare-Object -ReferenceObject $currentPrincipals -DifferenceObject $desiredPrincipals - [Array]$membersToAdd = $differences | Where-Object { $_.SideIndicator -eq '=>' } - [Array]$membersToRemove = $differences | Where-Object { $_.SideIndicator -eq '<=' } - } else { - Write-Verbose "Either currentPrincipals or desiredPrincipals is empty. Skipping comparison." - [Array]$membersToAdd = @() - [Array]$membersToRemove = @() - } + if ($AppRoleAssignedTo) + { + [Array]$currentPrincipals = $currentAADServicePrincipal.AppRoleAssignedTo.Identity + [Array]$desiredPrincipals = $AppRoleAssignedTo.Identity - if ($differences.Count -gt 0) { - if ($membersToAdd.Count -gt 0) { - $AppRoleAssignedToValues = @() - foreach ($assignment in $AppRoleAssignedTo) { - $AppRoleAssignedToValues += @{ - PrincipalType = $assignment.PrincipalType - Identity = $assignment.Identity - } - } - foreach ($member in $membersToAdd) { - $assignment = $AppRoleAssignedToValues | Where-Object { $_.Identity -eq $member.InputObject } - - if ($assignment) { - if ($assignment.PrincipalType -eq 'User') { - Write-Verbose -Message "Retrieving user {$($assignment.Identity)}" - $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($assignment.Identity)')" - $PrincipalIdValue = $user.Id - } elseif ($assignment.PrincipalType -eq 'Group') { - Write-Verbose -Message "Retrieving group {$($assignment.Identity)}" - $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.Identity)'" - $PrincipalIdValue = $group.Id - } else { - Write-Verbose "Unknown PrincipalType: $($assignment.PrincipalType). Skipping." - continue + [Array]$differences = Compare-Object -ReferenceObject $currentPrincipals -DifferenceObject $desiredPrincipals + [Array]$membersToAdd = $differences | Where-Object -FilterScript { $_.SideIndicator -eq '=>' } + [Array]$membersToRemove = $differences | Where-Object -FilterScript { $_.SideIndicator -eq '<=' } + + if ($differences.Count -gt 0) + { + if ($membersToAdd.Count -gt 0) + { + $AppRoleAssignedToValues = @() + foreach ($assignment in $AppRoleAssignedTo) + { + $AppRoleAssignedToValues += @{ + PrincipalType = $assignment.PrincipalType + Identity = $assignment.Identity + } } + foreach ($member in $membersToAdd) + { + $assignment = $AppRoleAssignedToValues | Where-Object -FilterScript { $_.Identity -eq $member.InputObject } + if ($assignment.PrincipalType -eq 'User') + { + Write-Verbose -Message "Retrieving user {$($assignment.Identity)}" + $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($assignment.Identity)')" + $PrincipalIdValue = $user.Id + } + else + { + Write-Verbose -Message "Retrieving group {$($assignment.Identity)}" + $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.Identity)'" + $PrincipalIdValue = $group.Id + } - if ($PrincipalIdValue) { $bodyParam = @{ principalId = $PrincipalIdValue resourceId = $currentAADServicePrincipal.ObjectID @@ -654,63 +649,47 @@ function Set-TargetResource Write-Verbose -Message "Adding member {$($member.InputObject.ToString())}" New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $currentAADServicePrincipal.ObjectID ` -BodyParameter $bodyParam | Out-Null - } else { - Write-Verbose "Failed to retrieve PrincipalId for {$($assignment.Identity)}. Skipping." } } - } - } - if ($membersToRemove.Count -gt 0) { - $AppRoleAssignedToValues = @() - foreach ($assignment in $currentAADServicePrincipal.AppRoleAssignedTo) { - $AppRoleAssignedToValues += @{ - PrincipalType = $assignment.PrincipalType - Identity = $assignment.Identity - } - } - foreach ($member in $membersToRemove) { - $assignment = $AppRoleAssignedToValues | Where-Object { $_.Identity -eq $member.InputObject } - - if ($assignment) { - if ($assignment.PrincipalType -eq 'User') { - Write-Verbose -Message "Retrieving user {$($assignment.Identity)}" - $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($assignment.Identity)')" - $PrincipalIdValue = $user.Id - } elseif ($assignment.PrincipalType -eq 'Group') { - Write-Verbose -Message "Retrieving group {$($assignment.Identity)}" - $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.Identity)'" - $PrincipalIdValue = $group.Id - } else { - Write-Verbose "Unknown PrincipalType: $($assignment.PrincipalType). Skipping." - continue + if ($membersToRemove.Count -gt 0) + { + $AppRoleAssignedToValues = @() + foreach ($assignment in $currentAADServicePrincipal.AppRoleAssignedTo) + { + $AppRoleAssignedToValues += @{ + PrincipalType = $assignment.PrincipalType + Identity = $assignment.Identity + } } - - if ($PrincipalIdValue) { + foreach ($member in $membersToRemove) + { + $assignment = $AppRoleAssignedToValues | Where-Object -FilterScript { $_.Identity -eq $member.InputObject } + if ($assignment.PrincipalType -eq 'User') + { + Write-Verbose -Message "Retrieving user {$($assignment.Identity)}" + $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($assignment.Identity)')" + $PrincipalIdValue = $user.Id + } + else + { + Write-Verbose -Message "Retrieving group {$($assignment.Identity)}" + $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.Identity)'" + $PrincipalIdValue = $group.Id + } Write-Verbose -Message "PrincipalID Value = '$PrincipalIdValue'" Write-Verbose -Message "ServicePrincipalId = '$($currentAADServicePrincipal.ObjectID)'" - $allAssignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $currentAADServicePrincipal.ObjectID - $assignmentToRemove = $allAssignments | Where-Object { $_.PrincipalId -eq $PrincipalIdValue } - - if ($assignmentToRemove) { - Write-Verbose -Message "Removing member {$($member.InputObject.ToString())}" - Remove-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $currentAADServicePrincipal.ObjectID ` - -AppRoleAssignmentId $assignmentToRemove.Id | Out-Null - } else { - Write-Verbose "No matching assignment found for PrincipalId $PrincipalIdValue. Skipping removal." - } - } else { - Write-Verbose "Failed to retrieve PrincipalId for {$($assignment.Identity)}. Skipping removal." + $assignmentToRemove = $allAssignments | Where-Object -FilterScript { $_.PrincipalId -eq $PrincipalIdValue } + Write-Verbose -Message "Removing member {$($member.InputObject.ToString())}" + Remove-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $currentAADServicePrincipal.ObjectID ` + -AppRoleAssignmentId $assignmentToRemove.Id | Out-Null } } } } - } -} - -Write-Verbose -Message 'Checking if owners need to be updated...' + Write-Verbose -Message 'Checking if owners need to be updated...' if ($null -ne $Owners) { From d2a24112fdff9eb7b2eea1094f0ad798efe09c57 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 13:51:37 -0500 Subject: [PATCH 15/30] AADServicePrincipal - Fix Null Values --- CHANGELOG.md | 2 ++ .../MSFT_AADServicePrincipal.psm1 | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c0c30228c..175dc4065f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * AADConditionalAccessPolicy * Changed the InsiderRiskTypes property to a string array. +* AADServicePrincipal + * Fixes comparison of assigned roles for null values. * MISC * Changed the CIMInstance logic of various resources to us common logic. * Added support for specifying a proxy in Update-M365DSCModule. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 index e4d0baf0a7..56b00a3eee 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 @@ -609,6 +609,15 @@ function Set-TargetResource [Array]$currentPrincipals = $currentAADServicePrincipal.AppRoleAssignedTo.Identity [Array]$desiredPrincipals = $AppRoleAssignedTo.Identity + if ($null -eq $currentPrincipals) + { + $currentPrincipals = @() + } + if ($null -eq $desiredPrincipals) + { + $desiredPrincipals = @() + } + [Array]$differences = Compare-Object -ReferenceObject $currentPrincipals -DifferenceObject $desiredPrincipals [Array]$membersToAdd = $differences | Where-Object -FilterScript { $_.SideIndicator -eq '=>' } [Array]$membersToRemove = $differences | Where-Object -FilterScript { $_.SideIndicator -eq '<=' } From 584d620394f37415a6013ab672e953863705bb7c Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 13:53:21 -0500 Subject: [PATCH 16/30] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 175dc4065f..65f9d6bbc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Changed the InsiderRiskTypes property to a string array. * AADServicePrincipal * Fixes comparison of assigned roles for null values. + FIXES [#5717](https://github.com/microsoft/Microsoft365DSC/issues/5717) * MISC * Changed the CIMInstance logic of various resources to us common logic. * Added support for specifying a proxy in Update-M365DSCModule. From 6134f4494ee1316094db464ec9d8e66957fefc00 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 16:44:59 -0500 Subject: [PATCH 17/30] CommerceSelfServicePurchase --- CHANGELOG.md | 2 + .../MSFT_CommerceSelfServicePurchase.psm1 | 13 ++- ...SFT_CommerceSelfServicePurchase.schema.mof | 2 +- .../CommerceSelfServicePurchase/1-Create.ps1 | 26 ----- .../CommerceSelfServicePurchase/2-Update.ps1 | 11 +- .../CommerceSelfServicePurchase/3-Remove.ps1 | 26 ----- .../M365DSCLicensingHelper.psm1 | 9 +- ...DSC.CommerceSelfServicePurchase.Tests.ps1} | 100 +++++++----------- 8 files changed, 71 insertions(+), 118 deletions(-) delete mode 100644 Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/1-Create.ps1 delete mode 100644 Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/3-Remove.ps1 rename Tests/Unit/Microsoft365DSC/{Microsoft365DSC.ResourceName.Tests.ps1 => Microsoft365DSC.CommerceSelfServicePurchase.Tests.ps1} (61%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65f9d6bbc6..05fca66753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * AADServicePrincipal * Fixes comparison of assigned roles for null values. FIXES [#5717](https://github.com/microsoft/Microsoft365DSC/issues/5717) +* CommerceSelfServicePurchase + * Initial release. * MISC * Changed the CIMInstance logic of various resources to us common logic. * Added support for specifying a proxy in Update-M365DSCModule. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 index 0cee58fa55..3e70bbc24a 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 @@ -13,7 +13,7 @@ function Get-TargetResource $ProductName, [Parameter()] - [ValidateSet('Enabled', 'Disabled')] + [ValidateSet('Enabled', 'Disabled', 'OnlyTrialsWithoutPaymentMethod')] [System.String] $PolicyValue, @@ -75,10 +75,15 @@ function Get-TargetResource $uri = (Get-MSCloudLoginConnectionProfile -Workload 'Licensing').HostUrl + "/v1/policies/AllowSelfServicePurchase/products/$($ProductId)" $instance = Invoke-M365DSCLicensingWebRequest -Uri $uri -Method 'GET' } - if ($null -eq $instance) + if ($null -eq $instance.items) { return $nullResult } + elseif ($instance.items.Length -gt 1) + { + throw "Multiple instances with ProductId {$($ProductId)} were found." + } + $instance = $instance.items[0] $results = @{ ProductId = $instance.ProductId @@ -122,7 +127,7 @@ function Set-TargetResource [Parameter()] [System.String] - [ValidateSet('Enabled', 'Disabled')] + [ValidateSet('Enabled', 'Disabled', 'OnlyTrialsWithoutPaymentMethod')] $PolicyValue, [Parameter()] @@ -199,7 +204,7 @@ function Test-TargetResource [Parameter()] [System.String] - [ValidateSet('Enabled', 'Disabled')] + [ValidateSet('Enabled', 'Disabled', 'OnlyTrialsWithoutPaymentMethod')] $PolicyValue, [Parameter()] diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.schema.mof index 56e6bf5d1f..ff3351516e 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.schema.mof @@ -3,7 +3,7 @@ class MSFT_CommerceSelfServicePurchase : OMI_BaseResource { [Key, Description("Unique ID of the product.")] String ProductId; [Write, Description("Name of the product")] String ProductName; - [Write, Description("Can be Enabled or Disabled."), ValueMap{"Enabled","Disabled"}, Values{"Enabled","Disabled"}] String PolicyValue; + [Write, Description("Can be Enabled or Disabled."), ValueMap{"Enabled","Disabled", "OnlyTrialsWithoutPaymentMethod"}, Values{"Enabled","Disabled", "OnlyTrialsWithoutPaymentMethod"}] String PolicyValue; [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; diff --git a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/1-Create.ps1 deleted file mode 100644 index b516274848..0000000000 --- a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/1-Create.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -<# -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 - { - - } -} diff --git a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/2-Update.ps1 index b516274848..527caf2fd9 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/2-Update.ps1 @@ -21,6 +21,15 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + CommerceSelfServicePurchase "Power Apps per user" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Ensure = "Present"; + PolicyValue = "Enabled"; + ProductId = "CFQ7TTC0LH2H"; + ProductName = "Power Apps per user"; + TenantId = $TenantId; + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/3-Remove.ps1 deleted file mode 100644 index b516274848..0000000000 --- a/Modules/Microsoft365DSC/Examples/Resources/CommerceSelfServicePurchase/3-Remove.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -<# -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 - { - - } -} diff --git a/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 b/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 index cddc8e7de3..0f7630c61a 100644 --- a/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 +++ b/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 @@ -20,10 +20,17 @@ function Invoke-M365DSCLicensingWebRequest Authorization = (Get-MSCloudLoginConnectionProfile -Workload 'Licensing').AccessToken } + $bodyValue = $null + if (-not [System.String]::IsNullOrEmpty($Body)) + { + $bodyValue = ConvertTo-Json $Body -Depth 10 -Compress + } + $response = Invoke-WebRequest -Method $Method ` -Uri $Uri ` -Headers $headers ` - -Body $Body ` + -Body $bodyValue ` + -ContentType 'application-json' ` -UseBasicParsing $result = ConvertFrom-Json $response.Content return $result diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.CommerceSelfServicePurchase.Tests.ps1 similarity index 61% rename from Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 rename to Tests/Unit/Microsoft365DSC/Microsoft365DSC.CommerceSelfServicePurchase.Tests.ps1 index 780e0f343d..acbaf8d52f 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.CommerceSelfServicePurchase.Tests.ps1 @@ -35,8 +35,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return "Credentials" } - ##TODO - Mock any Remove/Set/New cmdlets - # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } @@ -47,13 +45,14 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance should exist but it DOES NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + PolicyValue = "Enabled"; + ProductId = "CFQ7TTC0LH2H"; + ProductName = "Power Apps per user"; + Ensure = 'Present' + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return $null - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Invoke-M365DSCLicensingWebRequest -MockWith { return $null } } @@ -63,55 +62,27 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { It 'Should return false from the Test method' { Test-TargetResource @testParams | Should -Be $false } - - It 'Should create a new instance from the Set method' { - ##TODO - Replace the New-Cmdlet by the appropriate one - Set-TargetResource @testParams - Should -Invoke -CommandName New-Cmdlet -Exactly 1 - } - } - - Context -Name "The instance exists but it SHOULD NOT" -Fixture { - BeforeAll { - $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Absent' - Credential = $Credential; - } - - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { - return @{ - - } - } - } - It 'Should return Values from the Get method' { - (Get-TargetResource @testParams).Ensure | Should -Be 'Present' - } - It 'Should return false from the Test method' { - Test-TargetResource @testParams | Should -Be $false - } - - It 'Should remove the instance from the Set method' { - Set-TargetResource @testParams - ##TODO - Replace the Remove-Cmdlet by the appropriate one - Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 - } } Context -Name "The instance exists and values are already in the desired state" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + PolicyValue = "Enabled"; + ProductId = "CFQ7TTC0LH2H"; + ProductName = "Power Apps per user"; + Ensure = 'Present' + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return the desired values - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Invoke-M365DSCLicensingWebRequest -MockWith { return @{ - + items = @( + @{ + policyValue = "Enabled" + productId = "CFQ7TTC0LH2H"; + productName = "Power Apps per user"; + } + ) } } } @@ -124,15 +95,22 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance exists and values are NOT in the desired state" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + PolicyValue = "Enabled"; + ProductId = "CFQ7TTC0LH2H"; + ProductName = "Power Apps per user"; + Ensure = 'Present' + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return a drift - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Invoke-M365DSCLicensingWebRequest -MockWith { return @{ - + items = @( + @{ + policyValue = "Disabled" # Drift + productId = "CFQ7TTC0LH2H"; + productName = "Power Apps per user"; + } + ) } } } @@ -147,8 +125,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { It 'Should call the Set method' { Set-TargetResource @testParams - ##TODO - Replace the Update-Cmdlet by the appropriate one - Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + Should -Invoke -CommandName Invoke-M365DSCLicensingWebRequest -Exactly 2 } } @@ -160,10 +137,15 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Invoke-M365DSCLicensingWebRequest -MockWith { return @{ - + items = @( + @{ + policyValue = "Enabled" + productId = "CFQ7TTC0LH2H"; + productName = "Power Apps per user"; + } + ) } } } From 777730f027b8f38dc249523066d2a4a4bb5c6f71 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 16:55:31 -0500 Subject: [PATCH 18/30] Updated MSCloudLoginAssistant --- CHANGELOG.md | 2 ++ Modules/Microsoft365DSC/Dependencies/Manifest.psd1 | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05fca66753..eb97558e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ * MISC * Changed the CIMInstance logic of various resources to us common logic. * Added support for specifying a proxy in Update-M365DSCModule. +* DEPENDENCIES + * Updated MSCloudLoginAssistant to version 1.1.36. # 1.25.129.3 diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index b8b8009b49..75ba341060 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -122,7 +122,7 @@ }, @{ ModuleName = "MSCloudLoginAssistant" - RequiredVersion = "1.1.35" + RequiredVersion = "1.1.36" }, @{ ModuleName = 'PnP.PowerShell' From 069380990cd3c29d953af8d186f58ecaa9f70aff Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 17:00:54 -0500 Subject: [PATCH 19/30] Update MSFT_CommerceSelfServicePurchase.psm1 --- .../MSFT_CommerceSelfServicePurchase.psm1 | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 index 3e70bbc24a..da39fcb1ad 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 @@ -73,17 +73,9 @@ function Get-TargetResource else { $uri = (Get-MSCloudLoginConnectionProfile -Workload 'Licensing').HostUrl + "/v1/policies/AllowSelfServicePurchase/products/$($ProductId)" - $instance = Invoke-M365DSCLicensingWebRequest -Uri $uri -Method 'GET' + $instances = Invoke-M365DSCLicensingWebRequest -Uri $uri -Method 'GET' + $instance = $instances.items | Where-Object -FilterScript {$_.ProductId -eq $ProductId} } - if ($null -eq $instance.items) - { - return $nullResult - } - elseif ($instance.items.Length -gt 1) - { - throw "Multiple instances with ProductId {$($ProductId)} were found." - } - $instance = $instance.items[0] $results = @{ ProductId = $instance.ProductId From 2e94d8248a4d0d1b3695d3121429adcf5e0ffe4f Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 17:02:12 -0500 Subject: [PATCH 20/30] Update M365DSCLicensingHelper.psm1 --- .../Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 b/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 index 0f7630c61a..0f3d582353 100644 --- a/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 +++ b/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCLicensingHelper.psm1 @@ -30,7 +30,7 @@ function Invoke-M365DSCLicensingWebRequest -Uri $Uri ` -Headers $headers ` -Body $bodyValue ` - -ContentType 'application-json' ` + -ContentType 'application/json' ` -UseBasicParsing $result = ConvertFrom-Json $response.Content return $result From 496324ece737475d15601fa1b6725529d3f0cb3d Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 17:24:50 -0500 Subject: [PATCH 21/30] Update MSFT_CommerceSelfServicePurchase.psm1 --- .../MSFT_CommerceSelfServicePurchase.psm1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 index da39fcb1ad..d24aa8468f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 @@ -77,6 +77,11 @@ function Get-TargetResource $instance = $instances.items | Where-Object -FilterScript {$_.ProductId -eq $ProductId} } + if ($null -eq $instance) + { + return $nullResult + } + $results = @{ ProductId = $instance.ProductId ProductName = $instance.ProductName From b4e32f8663c81f0e5155ace9bc5e209fd6bc5387 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 4 Feb 2025 17:34:43 -0500 Subject: [PATCH 22/30] Fixes --- .../MSFT_CommerceSelfServicePurchase.psm1 | 3 +-- ...5DSC.CommerceSelfServicePurchase.Tests.ps1 | 20 ++++++------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 index d24aa8468f..4e3bde1a8d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_CommerceSelfServicePurchase/MSFT_CommerceSelfServicePurchase.psm1 @@ -73,8 +73,7 @@ function Get-TargetResource else { $uri = (Get-MSCloudLoginConnectionProfile -Workload 'Licensing').HostUrl + "/v1/policies/AllowSelfServicePurchase/products/$($ProductId)" - $instances = Invoke-M365DSCLicensingWebRequest -Uri $uri -Method 'GET' - $instance = $instances.items | Where-Object -FilterScript {$_.ProductId -eq $ProductId} + $instance = Invoke-M365DSCLicensingWebRequest -Uri $uri -Method 'GET' } if ($null -eq $instance) diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.CommerceSelfServicePurchase.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.CommerceSelfServicePurchase.Tests.ps1 index acbaf8d52f..4ed6ad62f0 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.CommerceSelfServicePurchase.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.CommerceSelfServicePurchase.Tests.ps1 @@ -76,13 +76,9 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName Invoke-M365DSCLicensingWebRequest -MockWith { return @{ - items = @( - @{ - policyValue = "Enabled" - productId = "CFQ7TTC0LH2H"; - productName = "Power Apps per user"; - } - ) + policyValue = "Enabled" + productId = "CFQ7TTC0LH2H"; + productName = "Power Apps per user"; } } } @@ -104,13 +100,9 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName Invoke-M365DSCLicensingWebRequest -MockWith { return @{ - items = @( - @{ - policyValue = "Disabled" # Drift - productId = "CFQ7TTC0LH2H"; - productName = "Power Apps per user"; - } - ) + policyValue = "Disabled" # Drift + productId = "CFQ7TTC0LH2H"; + productName = "Power Apps per user"; } } } From a8bd5158162f2e11c94eb58ff96c69c875d72b35 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Tue, 4 Feb 2025 23:24:38 +0000 Subject: [PATCH 23/30] Updated Resources and Cmdlet documentation pages --- .../azure/CommerceSelfServicePurchase.md | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/docs/resources/azure/CommerceSelfServicePurchase.md diff --git a/docs/docs/resources/azure/CommerceSelfServicePurchase.md b/docs/docs/resources/azure/CommerceSelfServicePurchase.md new file mode 100644 index 0000000000..dab6e24bb7 --- /dev/null +++ b/docs/docs/resources/azure/CommerceSelfServicePurchase.md @@ -0,0 +1,64 @@ +# CommerceSelfServicePurchase + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **ProductId** | Key | String | Unique ID of the product. | | +| **ProductName** | Write | String | Name of the product | | +| **PolicyValue** | Write | String | Can be Enabled or Disabled. | `Enabled`, `Disabled`, `OnlyTrialsWithoutPaymentMethod` | +| **Ensure** | Write | String | Present ensures the instance exists, absent ensures it is removed. | `Absent`, `Present` | +| **Credential** | Write | PSCredential | Credentials of the workload's Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + + +## Description + +Manages the Self Purchase policies in commerce. + +## Permissions + +## Examples + +### Example 1 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + CommerceSelfServicePurchase "Power Apps per user" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Ensure = "Present"; + PolicyValue = "Enabled"; + ProductId = "CFQ7TTC0LH2H"; + ProductName = "Power Apps per user"; + TenantId = $TenantId; + } + } +} +``` + From dda3ee5bc3a0908825b05c5c404be7d4c38ccd2f Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Tue, 4 Feb 2025 23:26:51 +0000 Subject: [PATCH 24/30] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 4b01e1ce40..0b2e35b51d 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -11180,6 +11180,61 @@ } ] }, + { + "ClassName": "MSFT_CommerceSelfServicePurchase", + "Parameters": [ + { + "CIMType": "String", + "Name": "ProductId", + "Option": "Key" + }, + { + "CIMType": "String", + "Name": "ProductName", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "PolicyValue", + "Option": "Write" + }, + { + "CIMType": "string", + "Name": "Ensure", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_DefenderDeviceAuthenticatedScanDefinitionAuthenticationParams", "Parameters": [ From 0fd3cba9c2b305535e94bba94ad2c153b5875da8 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 5 Feb 2025 10:10:38 -0500 Subject: [PATCH 25/30] Fixing Unit Tests --- .../MSFT_AADAdminConsentRequestPolicy.psm1 | 9 +++++---- Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 | 6 +++++- ...icrosoft365DSC.AADAdminConsentRequestPolicy.Tests.ps1 | 4 ++-- ...ft365DSC.AADAuthenticationMethodPolicyEmail.Tests.ps1 | 4 ++-- ...65DSC.AADAuthenticationMethodPolicyHardware.Tests.ps1 | 4 ++-- ...soft365DSC.AADAuthenticationMethodPolicySms.Tests.ps1 | 4 ++-- ...65DSC.AADAuthenticationMethodPolicySoftware.Tests.ps1 | 4 ++-- ...oft365DSC.AADAuthenticationMethodPolicyX509.Tests.ps1 | 4 ++-- ...CrossTenantAccessPolicyConfigurationDefault.Tests.ps1 | 2 +- 9 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAdminConsentRequestPolicy/MSFT_AADAdminConsentRequestPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAdminConsentRequestPolicy/MSFT_AADAdminConsentRequestPolicy.psm1 index 8662b3533a..81ef4cb4d9 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAdminConsentRequestPolicy/MSFT_AADAdminConsentRequestPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAdminConsentRequestPolicy/MSFT_AADAdminConsentRequestPolicy.psm1 @@ -358,12 +358,13 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + $testTargetResource = $true Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - #Compare Cim instances - foreach ($key in $PSBoundParameters.Keys) - { + #Compare Cim instances + foreach ($key in $PSBoundParameters.Keys) + { $source = $PSBoundParameters.$key $target = $CurrentValues.$key if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') @@ -382,7 +383,7 @@ function Test-TargetResource $ValuesToCheck.Remove($key) | Out-Null } } - } + } $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` diff --git a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 index ade8a6832c..b0c0e7c6d0 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 @@ -846,7 +846,11 @@ function Compare-M365DSCComplexObject $compareResult = $true $ordinalComparison = [System.String]::Equals($referenceObject, $differenceObject, [System.StringComparison]::Ordinal) - if ($ordinalComparison) + if (-not $ordinalComparison) + { + $compareResult = $false + } + elseif ($ordinalComparison) { $compareResult = $null } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAdminConsentRequestPolicy.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAdminConsentRequestPolicy.Tests.ps1 index 8c913408a9..ffdc8e6f41 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAdminConsentRequestPolicy.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAdminConsentRequestPolicy.Tests.ps1 @@ -65,7 +65,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance =$null $Script:ExportMode = $false } # Test contexts @@ -157,7 +157,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { IsEnabled = $true NotifyReviewers = $False; RemindersEnabled = $True; - RequestDurationInDays = 30; + RequestDurationInDays = 29; #drift Reviewers = @( @{ Query = "/v1.0/users/e362df2b-8f61-4e5a-9e5e-c6069f3ed2ee" diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyEmail.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyEmail.Tests.ps1 index 28aa707b8c..c589de3da5 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyEmail.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyEmail.Tests.ps1 @@ -40,7 +40,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance =$null $Script:ExportMode = $false } # Test contexts @@ -257,7 +257,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } ) Id = "Email" - State = "enabled" + State = "disabled" #drift } } } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyHardware.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyHardware.Tests.ps1 index 0e851d7849..038e750eed 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyHardware.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyHardware.Tests.ps1 @@ -40,7 +40,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance =$null $Script:ExportMode = $false } # Test contexts @@ -250,7 +250,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } ) Id = "HardwareOath" - State = "enabled" + State = "disabled" #drift } } } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicySms.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicySms.Tests.ps1 index 4f17ec150d..a74168d1f9 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicySms.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicySms.Tests.ps1 @@ -40,7 +40,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance =$null $Script:ExportMode = $false } # Test contexts @@ -250,7 +250,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } ) Id = "Sms" - State = "enabled" + State = "disabled" #drift } } } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicySoftware.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicySoftware.Tests.ps1 index a3cb95ca17..8ce780ab86 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicySoftware.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicySoftware.Tests.ps1 @@ -40,7 +40,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance =$null $Script:ExportMode = $false } # Test contexts @@ -250,7 +250,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } ) Id = "SoftwareOath" - State = "enabled" + State = "disabled" #drift } } } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyX509.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyX509.Tests.ps1 index a717fc5802..c66f82f913 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyX509.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAuthenticationMethodPolicyX509.Tests.ps1 @@ -40,7 +40,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance =$null $Script:ExportMode = $false } # Test contexts @@ -361,7 +361,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } ) Id = "X509Certificate" - State = "enabled" + State = "disabled" #drift } } } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADCrossTenantAccessPolicyConfigurationDefault.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADCrossTenantAccessPolicyConfigurationDefault.Tests.ps1 index 762fca96ae..a2fd67819d 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADCrossTenantAccessPolicyConfigurationDefault.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADCrossTenantAccessPolicyConfigurationDefault.Tests.ps1 @@ -50,7 +50,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance =$null $Script:ExportMode = $false } # Test contexts From 6854987f816b0c2d49e168c23166964a6804c10f Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 5 Feb 2025 12:32:36 -0500 Subject: [PATCH 26/30] Unit Tests Fixes --- ...MSFT_AADCustomAuthenticationExtension.psm1 | 1 + ...eLifecycleWorkflowCustomTaskExtension.psm1 | 32 ++++++++++++------- .../Modules/M365DSCDRGUtil.psm1 | 2 +- ...cycleWorkflowCustomTaskExtension.Tests.ps1 | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 index 5346c2e532..6232ccaf5f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADCustomAuthenticationExtension/MSFT_AADCustomAuthenticationExtension.psm1 @@ -498,6 +498,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + $testTargetResource = $true #Compare Cim instances foreach ($key in $PSBoundParameters.Keys) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension/MSFT_AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension/MSFT_AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension.psm1 index d911ffb734..1ab23ab5eb 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension/MSFT_AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension/MSFT_AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension.psm1 @@ -389,6 +389,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + $testTargetResource = $true Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" @@ -405,23 +406,30 @@ function Test-TargetResource if (-not $testResult) { - break + Write-Verbose "TestResult returned False for $source" + $testTargetResource = $false + } + else + { + $ValuesToCheck.Remove($key) | Out-Null } - - $ValuesToCheck.Remove($key) | Out-Null } } - if ($testResult) - { - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys - } - Write-Verbose -Message "Test-TargetResource returned $testResult" + 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 - return $testResult + if (-not $TestResult) + { + $testTargetResource = $false + } + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" + return $testTargetResource } function Export-TargetResource diff --git a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 index b0c0e7c6d0..dd68740f2d 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 @@ -862,7 +862,7 @@ function Compare-M365DSCComplexObject -DifferenceObject ($differenceObject) } - if ($null -ne $compareResult -and -not $compareResult) + if ($null -ne $compareResult -and $compareResult.Length -gt 0) { Write-Verbose -Message "Configuration drift - simple object key: $key" Write-Verbose -Message "Source {$sourceValue}" diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension.Tests.ps1 index cf2811f30c..a82cc21fc1 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityGovernanceLifecycleWorkflowCustomTaskExtension.Tests.ps1 @@ -51,7 +51,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance = $null $Script:ExportMode = $false } # Test contexts From 96ad7afac06930c7bdb779c7e8927f34b3aa4ab1 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 5 Feb 2025 13:57:20 -0500 Subject: [PATCH 27/30] Additional Unit Tests Fixes --- Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 | 3 ++- .../Microsoft365DSC.AADClaimsMappingPolicy.Tests.ps1 | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 index dd68740f2d..6e37c78116 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 @@ -786,7 +786,8 @@ function Compare-M365DSCComplexObject #Both keys aren't null or empty if (($null -ne $Source.$key) -and ($null -ne $Target.$key)) { - if ($Source.$key.GetType().FullName -like '*CimInstance*' -or $Source.$key.GetType().FullName -like '*hashtable*') + if ($Source.$key.GetType().FullName -like '*CimInstance*' -or $Source.$key.GetType().FullName -like '*hashtable*' -or ` + $Source.$key.GetType().Name -eq 'Object[]') { if ($Source.$key.GetType().FullName -like '*CimInstance' -and ( $Source.$key.CimClass.CimClassName -eq 'MSFT_DeviceManagementConfigurationPolicyAssignments' -or diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADClaimsMappingPolicy.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADClaimsMappingPolicy.Tests.ps1 index a462d49748..4b0dc9bbd7 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADClaimsMappingPolicy.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADClaimsMappingPolicy.Tests.ps1 @@ -49,7 +49,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } - $Script:exportedInstances =$null + $Script:exportedInstance =$null $Script:ExportMode = $false } # Test contexts From f5a8ea92f21b1e731a4b339471cf4d0475dfc2a7 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 5 Feb 2025 16:11:27 -0500 Subject: [PATCH 28/30] Fixes --- CHANGELOG.md | 2 + .../MSFT_SCRoleGroupMember.psm1 | 31 ++- .../Modules/M365DSCDRGUtil.psm1 | 184 +++++++++--------- 3 files changed, 123 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91aa0bbe46..9f828872b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ FIXES [#5717](https://github.com/microsoft/Microsoft365DSC/issues/5717) * CommerceSelfServicePurchase * Initial release. +* SCRoleGroupMember + * Error handling if the Members parameter is omitted. * MISC * Changed the CIMInstance logic of various resources to us common logic. * Added support for specifying a proxy in Update-M365DSCModule. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCRoleGroupMember/MSFT_SCRoleGroupMember.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCRoleGroupMember/MSFT_SCRoleGroupMember.psm1 index 64e0ac78e1..4698ab806d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCRoleGroupMember/MSFT_SCRoleGroupMember.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCRoleGroupMember/MSFT_SCRoleGroupMember.psm1 @@ -90,12 +90,21 @@ function Get-TargetResource } # Get RoleGroup Members if RoleGroup exists. - $roleGroupMember = Get-RoleGroupMember -Identity $Name | Select-Object Name + $roleGroupMembers = Get-RoleGroupMember -Identity $Name | Select-Object Name + + if ($roleGroupMembers.Length -eq 0) + { + $roleGroupMembersValue = @() + } + else + { + $roleGroupMembersValue = $roleGroupMembers.Name + } $result = @{ Name = $RoleGroup.Name Description = $RoleGroup.Description - Members = $roleGroupMember.Name + Members = $roleGroupMembersValue Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId @@ -193,10 +202,23 @@ function Set-TargetResource -InboundParameters $PSBoundParameters # CASE: Role Group has different member values than the desired ones - if ($Ensure -eq 'Present' -and $currentRoleGroupConfig.Ensure -eq 'Present' -and $null -ne (Compare-Object -ReferenceObject $($currentRoleGroupConfig.Members) -DifferenceObject $Members)) + if ([System.String]::IsNullOrEmpty($Members)) + { + $Members = @() + } + if ([System.String]::IsNullOrEmpty($currentRoleGroupConfig.Members)) + { + $currentRoleGroupConfig.Members = @() + } + + $differences = $null + if ($Members.Count -gt 0 -or $currentRoleGroupConfig.Members.Count -gt 0) + { + $differences = (Compare-Object -ReferenceObject $($currentRoleGroupConfig.Members) -DifferenceObject $Members) + } + if ($Ensure -eq 'Present' -and $currentRoleGroupConfig.Ensure -eq 'Present' -and $null -ne $differences) { Write-Verbose -Message "Role Group '$($Name)' exists, but members need updating." - $differences = Compare-Object -ReferenceObject $($currentRoleGroupConfig.Members) -DifferenceObject $Members foreach ($difference in $differences) { if ($difference.SideIndicator -eq '=>') @@ -294,6 +316,7 @@ function Test-TargetResource Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" $ValuesToCheck = $PSBoundParameters + $ValuesToCheck.Remove('Description') | Out-Null $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` diff --git a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 index 6e37c78116..538080fda8 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 @@ -750,125 +750,129 @@ function Compare-M365DSCComplexObject foreach ($key in $keys) { - #Matching possible key names between Source and Target - $sourceValue = $Source.$key - - # Some classes might contain default properties that have the same name as the key, - # so we need to check if the key is present in the target object --> Hashtable <-> IsReadOnly property - if ($key -in $targetKeys) - { - $targetValue = $Target.$key - } - else + if (($target.GetType().Name -eq 'Hashtable' -and $target.ContainsKey($key)) -or ` + ($target.GetType().Name -eq 'CIMInstance' -and $null -ne $target.$key)) { - $targetValue = $null - } + #Matching possible key names between Source and Target + $sourceValue = $Source.$key - #One of the item is null and not the other - if (($Source.$key.Length -eq 0) -xor ($targetValue.Length -eq 0)) - { - if ($null -eq $Source.$key) + # Some classes might contain default properties that have the same name as the key, + # so we need to check if the key is present in the target object --> Hashtable <-> IsReadOnly property + if ($key -in $targetKeys) { - $sourceValue = 'null' + $targetValue = $Target.$key } - - if ($null -eq $targetValue) + else { - $targetValue = 'null' + $targetValue = $null } - Write-Verbose -Message "Configuration drift - key: $key" - Write-Verbose -Message "Source {$sourceValue}" - Write-Verbose -Message "Target {$targetValue}" - return $false - } - - #Both keys aren't null or empty - if (($null -ne $Source.$key) -and ($null -ne $Target.$key)) - { - if ($Source.$key.GetType().FullName -like '*CimInstance*' -or $Source.$key.GetType().FullName -like '*hashtable*' -or ` - $Source.$key.GetType().Name -eq 'Object[]') + #One of the item is null and not the other + if (($Source.$key.Length -eq 0) -xor ($targetValue.Length -eq 0)) { - if ($Source.$key.GetType().FullName -like '*CimInstance' -and ( - $Source.$key.CimClass.CimClassName -eq 'MSFT_DeviceManagementConfigurationPolicyAssignments' -or - $Source.$key.CimClass.CimClassName -like 'MSFT_DeviceManagementMobileAppAssignment' -or - $Source.$key.CimClass.CimClassName -like 'MSFT_Intune*Assignments' - )) + if ($null -eq $Source.$key) { - $compareResult = Compare-M365DSCIntunePolicyAssignment ` - -Source @($Source.$key) ` - -Target @($Target.$key) - } - else - { - #Recursive call for complex object - $compareResult = Compare-M365DSCComplexObject ` - -Source $Source.$key ` - -Target $Target.$key + $sourceValue = 'null' } - if (-not $compareResult) + if ($null -eq $targetValue) { - Write-Verbose -Message "Configuration drift - complex object key: $key" - Write-Verbose -Message "Source {$sourceValue}" - Write-Verbose -Message "Target {$targetValue}" - return $false + $targetValue = 'null' } + + Write-Verbose -Message "Configuration drift - key: $key" + Write-Verbose -Message "Source {$sourceValue}" + Write-Verbose -Message "Target {$targetValue}" + return $false } - else - { - #Simple object comparison - $referenceObject = $Target.$key - $differenceObject = $Source.$key - #Identifying date from the current values - $targetType = ($Target.$key.GetType()).Name - if ($targetType -like '*Date*') + #Both keys aren't null or empty + if (($null -ne $Source.$key) -and ($null -ne $Target.$key)) + { + if ($Source.$key.GetType().FullName -like '*CimInstance*' -or $Source.$key.GetType().FullName -like '*hashtable*' -or ` + $Source.$key.GetType().Name -eq 'Object[]') { - $compareResult = $true - $sourceDate = [DateTime]$Source.$key - if ($sourceDate -ne $targetType) + if ($Source.$key.GetType().FullName -like '*CimInstance' -and ( + $Source.$key.CimClass.CimClassName -eq 'MSFT_DeviceManagementConfigurationPolicyAssignments' -or + $Source.$key.CimClass.CimClassName -like 'MSFT_DeviceManagementMobileAppAssignment' -or + $Source.$key.CimClass.CimClassName -like 'MSFT_Intune*Assignments' + )) { - $compareResult = $null + $compareResult = Compare-M365DSCIntunePolicyAssignment ` + -Source @($Source.$key) ` + -Target @($Target.$key) } - } - elseif ($targetType -eq 'String') - { - # Align line breaks - if (-not [System.String]::IsNullOrEmpty($referenceObject)) + else { - $referenceObject = $referenceObject.Replace("`r`n", "`n") + #Recursive call for complex object + $compareResult = Compare-M365DSCComplexObject ` + -Source $Source.$key ` + -Target $Target.$key } - if (-not [System.String]::IsNullOrEmpty($differenceObject)) + if (-not $compareResult) { - $differenceObject = $differenceObject.Replace("`r`n", "`n") + Write-Verbose -Message "Configuration drift - complex object key: $key" + Write-Verbose -Message "Source {$sourceValue}" + Write-Verbose -Message "Target {$targetValue}" + return $false } + } + else + { + #Simple object comparison + $referenceObject = $Target.$key + $differenceObject = $Source.$key - $compareResult = $true - $ordinalComparison = [System.String]::Equals($referenceObject, $differenceObject, [System.StringComparison]::Ordinal) - if (-not $ordinalComparison) + #Identifying date from the current values + $targetType = ($Target.$key.GetType()).Name + if ($targetType -like '*Date*') + { + $compareResult = $true + $sourceDate = [DateTime]$Source.$key + if ($sourceDate -ne $targetType) + { + $compareResult = $null + } + } + elseif ($targetType -eq 'String') { - $compareResult = $false + # Align line breaks + if (-not [System.String]::IsNullOrEmpty($referenceObject)) + { + $referenceObject = $referenceObject.Replace("`r`n", "`n") + } + + if (-not [System.String]::IsNullOrEmpty($differenceObject)) + { + $differenceObject = $differenceObject.Replace("`r`n", "`n") + } + + $compareResult = $true + $ordinalComparison = [System.String]::Equals($referenceObject, $differenceObject, [System.StringComparison]::Ordinal) + if (-not $ordinalComparison) + { + $compareResult = $false + } + elseif ($ordinalComparison) + { + $compareResult = $null + } } - elseif ($ordinalComparison) + else { - $compareResult = $null + $compareResult = Compare-Object ` + -ReferenceObject ($referenceObject) ` + -DifferenceObject ($differenceObject) } - } - else - { - $compareResult = Compare-Object ` - -ReferenceObject ($referenceObject) ` - -DifferenceObject ($differenceObject) - } - if ($null -ne $compareResult -and $compareResult.Length -gt 0) - { - Write-Verbose -Message "Configuration drift - simple object key: $key" - Write-Verbose -Message "Source {$sourceValue}" - Write-Verbose -Message "Target {$targetValue}" - return $false + if ($null -ne $compareResult -and $compareResult.Length -gt 0) + { + Write-Verbose -Message "Configuration drift - simple object key: $key" + Write-Verbose -Message "Source {$sourceValue}" + Write-Verbose -Message "Target {$targetValue}" + return $false + } } } } From aa36b9e822af68491c3b68ac3035f8016c1d96ba Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 5 Feb 2025 16:42:02 -0500 Subject: [PATCH 29/30] Fixes --- .../MSFT_SCRoleGroupMember.psm1 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCRoleGroupMember/MSFT_SCRoleGroupMember.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCRoleGroupMember/MSFT_SCRoleGroupMember.psm1 index 4698ab806d..bf15678b89 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCRoleGroupMember/MSFT_SCRoleGroupMember.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCRoleGroupMember/MSFT_SCRoleGroupMember.psm1 @@ -202,20 +202,20 @@ function Set-TargetResource -InboundParameters $PSBoundParameters # CASE: Role Group has different member values than the desired ones + $MembersValue = $Members if ([System.String]::IsNullOrEmpty($Members)) { - $Members = @() + $MembersValue = @() } + + $currentMembersValue = $currentRoleGroupConfig.Members if ([System.String]::IsNullOrEmpty($currentRoleGroupConfig.Members)) { - $currentRoleGroupConfig.Members = @() + $currentMembersValue = @() } - $differences = $null - if ($Members.Count -gt 0 -or $currentRoleGroupConfig.Members.Count -gt 0) - { - $differences = (Compare-Object -ReferenceObject $($currentRoleGroupConfig.Members) -DifferenceObject $Members) - } + $differences = Compare-Object -ReferenceObject $currentMembersValue -DifferenceObject $MembersValue + if ($Ensure -eq 'Present' -and $currentRoleGroupConfig.Ensure -eq 'Present' -and $null -ne $differences) { Write-Verbose -Message "Role Group '$($Name)' exists, but members need updating." From cd932a89903ee2d45736a6d18cd66cb44ae0c11b Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Thu, 6 Feb 2025 00:00:17 -0500 Subject: [PATCH 30/30] Release 1.25.205.1 --- CHANGELOG.md | 12 ++++++- ...SFT_AADConnectorGroupApplicationProxy.psm1 | 2 +- .../MSFT_SCPolicyConfig.psm1 | 2 +- Modules/Microsoft365DSC/Microsoft365DSC.psd1 | 34 +++++++++++++------ 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb97558e47..b9f0c0d93a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,24 @@ # Change log for Microsoft365DSC -# UNRELEASED +# 1.25.205.1 * AADConditionalAccessPolicy * Changed the InsiderRiskTypes property to a string array. +* AADConnectorGroupApplicationProxy + * Fixes an issue where the Get-TargetResource was not able to retrieve + instances by names. +* AADGroup + * Refactored logic for CIM Instance evaluation in Test-TargetResource. * AADServicePrincipal * Fixes comparison of assigned roles for null values. FIXES [#5717](https://github.com/microsoft/Microsoft365DSC/issues/5717) * CommerceSelfServicePurchase * Initial release. +* SCPolicyConfig + * Fixes and issue where Get-TargetResource was returning an empty array + instead of a null value when no values were defined. +* SCRoleGroupMember + * Error handling if the Members parameter is omitted. * MISC * Changed the CIMInstance logic of various resources to us common logic. * Added support for specifying a proxy in Update-M365DSCModule. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/MSFT_AADConnectorGroupApplicationProxy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/MSFT_AADConnectorGroupApplicationProxy.psm1 index ccbbbd5ffc..4461da2f98 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/MSFT_AADConnectorGroupApplicationProxy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/MSFT_AADConnectorGroupApplicationProxy.psm1 @@ -83,7 +83,7 @@ function Get-TargetResource if ($null -eq $getValue -and -not [string]::IsNullOrEmpty($Id)) { Write-Verbose -Message "Could not find an Azure AD Connector Group Application Proxy with Name {$Name}" - if (-not [string]::IsNullOrEmpty($DisplayName)) + if (-not [string]::IsNullOrEmpty($Name)) { $getValue = Get-MgBetaOnPremisePublishingProfileConnectorGroup -OnPremisesPublishingProfileId 'applicationProxy' -Filter "Name eq '$Name'" -ErrorAction Stop } diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCPolicyConfig/MSFT_SCPolicyConfig.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCPolicyConfig/MSFT_SCPolicyConfig.psm1 index 44dcc01cb4..73129306fd 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCPolicyConfig/MSFT_SCPolicyConfig.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCPolicyConfig/MSFT_SCPolicyConfig.psm1 @@ -440,7 +440,7 @@ function Get-TargetResource $DlpNetworkShareGroupsValue += $entry } - $QuarantineParametersValue = @() + $QuarantineParametersValue = $null if ($null -ne ($EndpointDlpGlobalSettingsValue | Where-Object { $_.Setting -eq 'QuarantineParameters' })) { $quarantineInfo = [Array]($EndpointDlpGlobalSettingsValue | Where-Object { $_.Setting -eq 'QuarantineParameters' }).Value diff --git a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 index e6cf6d519d..9bf591d877 100644 --- a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 +++ b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 @@ -3,7 +3,7 @@ # # Generated by: Microsoft Corporation # -# Generated on: 2025-01-31 +# Generated on: 2025-02-05 @{ @@ -11,7 +11,7 @@ # RootModule = '' # Version number of this module. - ModuleVersion = '1.25.129.3' + ModuleVersion = '1.25.205.1' # Supported PSEditions # CompatiblePSEditions = @() @@ -148,16 +148,28 @@ IconUri = 'https://github.com/microsoft/Microsoft365DSC/blob/Dev/Modules/Microsoft365DSC/Dependencies/Images/Logo.png?raw=true' # ReleaseNotes of this module - ReleaseNotes = '* EXOMigrationEndpoint - * Added support for EntpointType value of ExchangeRemoteMove. -* M365DSCRuleEvaluation - * Changed logic to evaluate cases where the rule results in no results. -* SPDLPComplianceRule - * Fix for the SensitiveInfoType ID cleaning logic in Get-TargetResource. + ReleaseNotes = '* AADConditionalAccessPolicy + * Changed the InsiderRiskTypes property to a string array. +* AADConnectorGroupApplicationProxy + * Fixes an issue where the Get-TargetResource was not able to retrieve + instances by names. +* AADGroup + * Refactored logic for CIM Instance evaluation in Test-TargetResource. +* AADServicePrincipal + * Fixes comparison of assigned roles for null values. + FIXES [#5717](https://github.com/microsoft/Microsoft365DSC/issues/5717) +* CommerceSelfServicePurchase + * Initial release. +* SCPolicyConfig + * Fixes and issue where Get-TargetResource was returning an empty array + instead of a null value when no values were defined. +* SCRoleGroupMember + * Error handling if the Members parameter is omitted. +* MISC + * Changed the CIMInstance logic of various resources to us common logic. + * Added support for specifying a proxy in Update-M365DSCModule. * DEPENDENCIES - * Updated Microsoft.PowerApps.Administration.PowerShell to version 2.0.205. - * Updated MicrosoftTeams to version 6.8.0. - * Updated MSCloudLoginAssistant to version 1.1.35.' + * Updated MSCloudLoginAssistant to version 1.1.36.' # Flag to indicate whether the module requires explicit user acceptance for install/update # RequireLicenseAcceptance = $false