From 20f23407cc596a2811f0eb44cd0ed70463946c90 Mon Sep 17 00:00:00 2001 From: Ricardo Mestre Date: Wed, 7 Feb 2024 22:42:52 +0000 Subject: [PATCH 01/11] Fix remaining issues of resource --- CHANGELOG.md | 9 ++++++ ...SettingCatalogASRRulesPolicyWindows10.psm1 | 30 +++++++++---------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65b0f8be87..c1440eeedd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change log for Microsoft365DSC +# UNRELEASED + +* IntuneSettingCatalogASRRulesPolicyWindows10 + * Fix removal of resource if Identity comes from another tenant or is not + present in blueprint + * Fix Test-TargetResource by not comparing Identity since it might be from + another tenant or not present in blueprint + FIXES [#4302](https://github.com/microsoft/Microsoft365DSC/issues/4302) + # 1.24.207.1 * IntuneDeviceEnrollmentPlatformRestriction diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneSettingCatalogASRRulesPolicyWindows10/MSFT_IntuneSettingCatalogASRRulesPolicyWindows10.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneSettingCatalogASRRulesPolicyWindows10/MSFT_IntuneSettingCatalogASRRulesPolicyWindows10.psm1 index 4be3e15f1b..062a372eaf 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneSettingCatalogASRRulesPolicyWindows10/MSFT_IntuneSettingCatalogASRRulesPolicyWindows10.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneSettingCatalogASRRulesPolicyWindows10/MSFT_IntuneSettingCatalogASRRulesPolicyWindows10.psm1 @@ -183,7 +183,14 @@ function Get-TargetResource try { #Retrieve policy general settings - $policy = Get-MgBetaDeviceManagementConfigurationPolicy -DeviceManagementConfigurationPolicyId $Identity -ErrorAction silentlyContinue + try + { + $policy = Get-MgBetaDeviceManagementConfigurationPolicy -DeviceManagementConfigurationPolicyId $Identity -ErrorAction Stop + } + catch + { + $policy = $null + } if ($null -eq $policy) { @@ -453,13 +460,7 @@ function Set-TargetResource #endregion $currentPolicy = Get-TargetResource @PSBoundParameters - $PSBoundParameters.Remove('Ensure') | Out-Null - $PSBoundParameters.Remove('Credential') | Out-Null - $PSBoundParameters.Remove('ApplicationId') | Out-Null - $PSBoundParameters.Remove('TenantId') | Out-Null - $PSBoundParameters.Remove('ApplicationSecret') | Out-Null - $PSBoundParameters.Remove('CertificateThumbprint') | Out-Null - $PSBoundParameters.Remove('ManagedIdentity') | Out-Null + $PSBoundParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters $templateReferenceId = 'e8c053d6-9f95-42b1-a7f1-ebfd71c67a4b_1' @@ -533,7 +534,7 @@ function Set-TargetResource elseif ($Ensure -eq 'Absent' -and $currentPolicy.Ensure -eq 'Present') { Write-Verbose -Message "Removing Endpoint Protection Attack Surface Protection rules Policy {$DisplayName}" - Remove-MgBetaDeviceManagementConfigurationPolicy -DeviceManagementConfigurationPolicyId $Identity + Remove-MgBetaDeviceManagementConfigurationPolicy -DeviceManagementConfigurationPolicyId $currentPolicy.Identity } } @@ -709,15 +710,12 @@ function Test-TargetResource Write-Verbose -Message "Testing configuration of Endpoint Protection Attack Surface Protection rules Policy {$DisplayName}" $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() + $ValuesToCheck = Remove-M365DSCAuthenticationParameter -BoundParameters $ValuesToCheck + $ValuesToCheck.Remove('Identity') | Out-Null Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" - Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" - - $ValuesToCheck = $PSBoundParameters - $ValuesToCheck.Remove('Credential') | Out-Null - $ValuesToCheck.Remove('ApplicationId') | Out-Null - $ValuesToCheck.Remove('TenantId') | Out-Null - $ValuesToCheck.Remove('ApplicationSecret') | Out-Null + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" if ($CurrentValues.Ensure -ne $PSBoundParameters.Ensure) { From a3b9db8da0371117214452585f46dc88d30ee355 Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Wed, 14 Feb 2024 17:16:59 +0100 Subject: [PATCH 02/11] Updated unit tests to check for description field in schema --- Tests/QA/Microsoft365DSC.Resources.Tests.ps1 | 207 ++++++++++++------- 1 file changed, 138 insertions(+), 69 deletions(-) diff --git a/Tests/QA/Microsoft365DSC.Resources.Tests.ps1 b/Tests/QA/Microsoft365DSC.Resources.Tests.ps1 index 529e7530e5..73e9870f07 100644 --- a/Tests/QA/Microsoft365DSC.Resources.Tests.ps1 +++ b/Tests/QA/Microsoft365DSC.Resources.Tests.ps1 @@ -8,12 +8,107 @@ } } -Describe -Name "Check schema for resource ''" -ForEach $schemaFiles{ - BeforeAll { - function Get-MofSchemaObject +BeforeAll { + function Get-MofSchemaObject + { + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $FileName + ) + + $temporaryPath = (Get-Item -Path env:TEMP).Value + + #region Workaround for OMI_BaseResource inheritance not resolving. + + $filePath = (Resolve-Path -Path $FileName).Path + $tempFilePath = Join-Path -Path $temporaryPath -ChildPath "DscMofHelper_$((New-Guid).Guid).tmp" + $rawContent = (Get-Content -Path $filePath -Raw) -replace '\s*:\s*OMI_BaseResource' + + Set-Content -LiteralPath $tempFilePath -Value $rawContent -ErrorAction 'Stop' + + # .NET methods don't like PowerShell drives + $tempFilePath = Convert-Path -Path $tempFilePath + + #endregion + + try + { + $exceptionCollection = [System.Collections.ObjectModel.Collection[System.Exception]]::new() + $moduleInfo = [System.Tuple]::Create('Module', [System.Version] '1.0.0') + + $class = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportClasses( + $tempFilePath, $moduleInfo, $exceptionCollection + ) + + if ($null -eq $class) + { + throw "No classes found in the schema file" + } + } + catch + { + throw "Failed to import classes from file $FileName. Error $_" + } + finally + { + Remove-Item -LiteralPath $tempFilePath -Force + } + + foreach ($currentCimClass in $class) + { + $attributes = foreach ($property in $currentCimClass.CimClassProperties) + { + $state = switch ($property.flags) + { + { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::Key } + { + 'Key' + } + { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::Required } + { + 'Required' + } + { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::ReadOnly } + { + 'Read' + } + default + { + 'Write' + } + } + + @{ + Name = $property.Name + State = $state + DataType = $property.CimType + ValueMap = $property.Qualifiers.Where( { $_.Name -eq 'ValueMap' }).Value + IsArray = $property.CimType -gt 16 + Description = $property.Qualifiers.Where( { $_.Name -eq 'Description' }).Value + EmbeddedInstance = $property.Qualifiers.Where( { $_.Name -eq 'EmbeddedInstance' }).Value + } + } + + @{ + ClassName = $currentCimClass.CimClassName + Attributes = $attributes + ClassVersion = $currentCimClass.CimClassQualifiers.Where( { $_.Name -eq 'ClassVersion' }).Value + FriendlyName = $currentCimClass.CimClassQualifiers.Where( { $_.Name -eq 'FriendlyName' }).Value + } + } + } +} + +Describe -Name "Check schema for resource ''" -ForEach $schemaFiles { + BeforeDiscovery { + function Confirm-MofSchema { [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] + [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] @@ -44,95 +139,69 @@ Describe -Name "Check schema for resource ''" -ForEach $schemaFile $class = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportClasses( $tempFilePath, $moduleInfo, $exceptionCollection ) + + if ($null -eq $class) + { + return $false + } + else + { + return $true + } } catch { - throw "Failed to import classes from file $FileName. Error $_" + return $false } finally { Remove-Item -LiteralPath $tempFilePath -Force } - - foreach ($currentCimClass in $class) - { - $attributes = foreach ($property in $currentCimClass.CimClassProperties) - { - $state = switch ($property.flags) - { - { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::Key } - { - 'Key' - } - { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::Required } - { - 'Required' - } - { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::ReadOnly } - { - 'Read' - } - default - { - 'Write' - } - } - - @{ - Name = $property.Name - State = $state - DataType = $property.CimType - ValueMap = $property.Qualifiers.Where( { $_.Name -eq 'ValueMap' }).Value - IsArray = $property.CimType -gt 16 - Description = $property.Qualifiers.Where( { $_.Name -eq 'Description' }).Value - EmbeddedInstance = $property.Qualifiers.Where( { $_.Name -eq 'EmbeddedInstance' }).Value - } - } - - @{ - ClassName = $currentCimClass.CimClassName - Attributes = $attributes - ClassVersion = $currentCimClass.CimClassQualifiers.Where( { $_.Name -eq 'ClassVersion' }).Value - FriendlyName = $currentCimClass.CimClassQualifiers.Where( { $_.Name -eq 'FriendlyName' }).Value - } - } } - } - It 'Schema should be read successfully' { - { Get-MofSchemaObject -FileName $FullName } | Should -Not -Throw + $skipTest = -not (Confirm-MofSchema -FileName $FullName) } - It 'Schema should have a Key parameter' { - $mofSchemas = Get-MofSchemaObject -FileName $FullName - $attributes = ($mofSchemas | Where-Object { [String]::IsNullOrEmpty($_.FriendlyName) -eq $false }).Attributes - $keyCount = ($attributes | Where-Object { $_.State -eq 'Key' }).Count - $keyCount | Should -BeGreaterThan 1 + Context 'Validate if the schema is correct' { + It 'Schema should be read successfully' { + { $mofSchemas = Get-MofSchemaObject -FileName $FullName } | Should -Not -Throw + } } - It 'Schema should contain an instance of all used subclasses' { - $mofSchemas = Get-MofSchemaObject -FileName $FullName + Context 'Run all schema checks' -Skip:$skipTest { + BeforeAll { + $mofSchemas = Get-MofSchemaObject -FileName $FullName + } - $errors = 0 + It 'Schema should have a Key parameter' { + $attributes = ($mofSchemas | Where-Object { [String]::IsNullOrEmpty($_.FriendlyName) -eq $false }).Attributes + $keyCount = ($attributes | Where-Object { $_.State -eq 'Key' }).Count + $keyCount | Should -BeGreaterThan 1 + } - $availableClasses = $mofSchemas.ClassName | Where-Object -FilterScript { $_ -ne $ResourceName } + It 'Schema should contain an instance of all used subclasses' { + $availableClasses = $mofSchemas.ClassName | Where-Object -FilterScript { $_ -ne $ResourceName } - foreach ($mofSchema in $mofSchemas) - { - foreach ($attribute in $mofSchema.Attributes) + foreach ($mofSchema in $mofSchemas) { - if ([String]::IsNullOrEmpty($attribute.EmbeddedInstance) -eq $false -and $attribute.EmbeddedInstance -ne "MSFT_Credential") + foreach ($attribute in $mofSchema.Attributes) { - if ($attribute.EmbeddedInstance -notin $availableClasses) + if ([String]::IsNullOrEmpty($attribute.EmbeddedInstance) -eq $false -and $attribute.EmbeddedInstance -ne 'MSFT_Credential') { - Write-Host "[ERROR] Property $($attribute.Name) in class $($mofSchema.ClassName) / Specified EmbeddedInstance: $($attribute.EmbeddedInstance) not found!" -ForegroundColor Red - $errors++ + $attribute.EmbeddedInstance | Should -BeIn $availableClasses } } } } - $errors | Should -Be 0 + It 'Schema should have a description for all properties' { + foreach ($mofSchema in $mofSchemas) + { + foreach ($attribute in $mofSchema.Attributes) + { + $attribute.Description | Should -Not -BeNullOrEmpty + } + } + } } - } From 0c6c571f1094fb4ace1c00f813b4a218895e7754 Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Wed, 14 Feb 2024 17:16:59 +0100 Subject: [PATCH 03/11] Updated unit tests to check for description field in schema --- Tests/QA/Microsoft365DSC.Resources.Tests.ps1 | 207 ++++++++++++------- 1 file changed, 138 insertions(+), 69 deletions(-) diff --git a/Tests/QA/Microsoft365DSC.Resources.Tests.ps1 b/Tests/QA/Microsoft365DSC.Resources.Tests.ps1 index 529e7530e5..73e9870f07 100644 --- a/Tests/QA/Microsoft365DSC.Resources.Tests.ps1 +++ b/Tests/QA/Microsoft365DSC.Resources.Tests.ps1 @@ -8,12 +8,107 @@ } } -Describe -Name "Check schema for resource ''" -ForEach $schemaFiles{ - BeforeAll { - function Get-MofSchemaObject +BeforeAll { + function Get-MofSchemaObject + { + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $FileName + ) + + $temporaryPath = (Get-Item -Path env:TEMP).Value + + #region Workaround for OMI_BaseResource inheritance not resolving. + + $filePath = (Resolve-Path -Path $FileName).Path + $tempFilePath = Join-Path -Path $temporaryPath -ChildPath "DscMofHelper_$((New-Guid).Guid).tmp" + $rawContent = (Get-Content -Path $filePath -Raw) -replace '\s*:\s*OMI_BaseResource' + + Set-Content -LiteralPath $tempFilePath -Value $rawContent -ErrorAction 'Stop' + + # .NET methods don't like PowerShell drives + $tempFilePath = Convert-Path -Path $tempFilePath + + #endregion + + try + { + $exceptionCollection = [System.Collections.ObjectModel.Collection[System.Exception]]::new() + $moduleInfo = [System.Tuple]::Create('Module', [System.Version] '1.0.0') + + $class = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportClasses( + $tempFilePath, $moduleInfo, $exceptionCollection + ) + + if ($null -eq $class) + { + throw "No classes found in the schema file" + } + } + catch + { + throw "Failed to import classes from file $FileName. Error $_" + } + finally + { + Remove-Item -LiteralPath $tempFilePath -Force + } + + foreach ($currentCimClass in $class) + { + $attributes = foreach ($property in $currentCimClass.CimClassProperties) + { + $state = switch ($property.flags) + { + { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::Key } + { + 'Key' + } + { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::Required } + { + 'Required' + } + { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::ReadOnly } + { + 'Read' + } + default + { + 'Write' + } + } + + @{ + Name = $property.Name + State = $state + DataType = $property.CimType + ValueMap = $property.Qualifiers.Where( { $_.Name -eq 'ValueMap' }).Value + IsArray = $property.CimType -gt 16 + Description = $property.Qualifiers.Where( { $_.Name -eq 'Description' }).Value + EmbeddedInstance = $property.Qualifiers.Where( { $_.Name -eq 'EmbeddedInstance' }).Value + } + } + + @{ + ClassName = $currentCimClass.CimClassName + Attributes = $attributes + ClassVersion = $currentCimClass.CimClassQualifiers.Where( { $_.Name -eq 'ClassVersion' }).Value + FriendlyName = $currentCimClass.CimClassQualifiers.Where( { $_.Name -eq 'FriendlyName' }).Value + } + } + } +} + +Describe -Name "Check schema for resource ''" -ForEach $schemaFiles { + BeforeDiscovery { + function Confirm-MofSchema { [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] + [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] @@ -44,95 +139,69 @@ Describe -Name "Check schema for resource ''" -ForEach $schemaFile $class = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportClasses( $tempFilePath, $moduleInfo, $exceptionCollection ) + + if ($null -eq $class) + { + return $false + } + else + { + return $true + } } catch { - throw "Failed to import classes from file $FileName. Error $_" + return $false } finally { Remove-Item -LiteralPath $tempFilePath -Force } - - foreach ($currentCimClass in $class) - { - $attributes = foreach ($property in $currentCimClass.CimClassProperties) - { - $state = switch ($property.flags) - { - { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::Key } - { - 'Key' - } - { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::Required } - { - 'Required' - } - { $_ -band [Microsoft.Management.Infrastructure.CimFlags]::ReadOnly } - { - 'Read' - } - default - { - 'Write' - } - } - - @{ - Name = $property.Name - State = $state - DataType = $property.CimType - ValueMap = $property.Qualifiers.Where( { $_.Name -eq 'ValueMap' }).Value - IsArray = $property.CimType -gt 16 - Description = $property.Qualifiers.Where( { $_.Name -eq 'Description' }).Value - EmbeddedInstance = $property.Qualifiers.Where( { $_.Name -eq 'EmbeddedInstance' }).Value - } - } - - @{ - ClassName = $currentCimClass.CimClassName - Attributes = $attributes - ClassVersion = $currentCimClass.CimClassQualifiers.Where( { $_.Name -eq 'ClassVersion' }).Value - FriendlyName = $currentCimClass.CimClassQualifiers.Where( { $_.Name -eq 'FriendlyName' }).Value - } - } } - } - It 'Schema should be read successfully' { - { Get-MofSchemaObject -FileName $FullName } | Should -Not -Throw + $skipTest = -not (Confirm-MofSchema -FileName $FullName) } - It 'Schema should have a Key parameter' { - $mofSchemas = Get-MofSchemaObject -FileName $FullName - $attributes = ($mofSchemas | Where-Object { [String]::IsNullOrEmpty($_.FriendlyName) -eq $false }).Attributes - $keyCount = ($attributes | Where-Object { $_.State -eq 'Key' }).Count - $keyCount | Should -BeGreaterThan 1 + Context 'Validate if the schema is correct' { + It 'Schema should be read successfully' { + { $mofSchemas = Get-MofSchemaObject -FileName $FullName } | Should -Not -Throw + } } - It 'Schema should contain an instance of all used subclasses' { - $mofSchemas = Get-MofSchemaObject -FileName $FullName + Context 'Run all schema checks' -Skip:$skipTest { + BeforeAll { + $mofSchemas = Get-MofSchemaObject -FileName $FullName + } - $errors = 0 + It 'Schema should have a Key parameter' { + $attributes = ($mofSchemas | Where-Object { [String]::IsNullOrEmpty($_.FriendlyName) -eq $false }).Attributes + $keyCount = ($attributes | Where-Object { $_.State -eq 'Key' }).Count + $keyCount | Should -BeGreaterThan 1 + } - $availableClasses = $mofSchemas.ClassName | Where-Object -FilterScript { $_ -ne $ResourceName } + It 'Schema should contain an instance of all used subclasses' { + $availableClasses = $mofSchemas.ClassName | Where-Object -FilterScript { $_ -ne $ResourceName } - foreach ($mofSchema in $mofSchemas) - { - foreach ($attribute in $mofSchema.Attributes) + foreach ($mofSchema in $mofSchemas) { - if ([String]::IsNullOrEmpty($attribute.EmbeddedInstance) -eq $false -and $attribute.EmbeddedInstance -ne "MSFT_Credential") + foreach ($attribute in $mofSchema.Attributes) { - if ($attribute.EmbeddedInstance -notin $availableClasses) + if ([String]::IsNullOrEmpty($attribute.EmbeddedInstance) -eq $false -and $attribute.EmbeddedInstance -ne 'MSFT_Credential') { - Write-Host "[ERROR] Property $($attribute.Name) in class $($mofSchema.ClassName) / Specified EmbeddedInstance: $($attribute.EmbeddedInstance) not found!" -ForegroundColor Red - $errors++ + $attribute.EmbeddedInstance | Should -BeIn $availableClasses } } } } - $errors | Should -Be 0 + It 'Schema should have a description for all properties' { + foreach ($mofSchema in $mofSchemas) + { + foreach ($attribute in $mofSchema.Attributes) + { + $attribute.Description | Should -Not -BeNullOrEmpty + } + } + } } - } From 1f39e26ac21c68e82b677af2e0c204c8f21a33ba Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Wed, 14 Feb 2024 17:19:02 +0100 Subject: [PATCH 04/11] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b04ca09a5d..1c1febe3c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ * Fix nested change detection for CIMInstances * Fix IntuneDeviceEnrolllmentPlatformRestriction comparison in report FIXES [#4291](https://github.com/microsoft/Microsoft365DSC/issues/4291) + * Added new QA test to check for missing description in resource schema # 1.24.207.2 From 32fa1cce2e7202e594d1a03bec8433c1bf9880cf Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Wed, 14 Feb 2024 17:24:29 +0100 Subject: [PATCH 05/11] Removed incorrect empty string --- CHANGELOG.md | 3 +++ .../MSFT_AADConditionalAccessPolicy.psm1 | 12 ++++++------ .../MSFT_AADConditionalAccessPolicy.schema.mof | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c1febe3c9..6ba895ab13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ # UNRELEASED +* AADConditionalAccessPolicy + * Removed invalid empty string value that was added to the validate set + of two parameters. * AADRoleEligibilityScheduleRequest * Fixed an issue where an error was thrown if no requests were found instead of simply returning the Null object. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 index 7f3e7c076f..276bf4a184 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.psm1 @@ -71,7 +71,7 @@ function Get-TargetResource [Parameter()] [System.String] - [ValidateSet('', 'all', 'enumerated', 'unknownFutureValue')] + [ValidateSet('all', 'enumerated', 'unknownFutureValue')] $IncludeExternalTenantsMembershipKind, [Parameter()] @@ -85,7 +85,7 @@ function Get-TargetResource [Parameter()] [System.String] - [ValidateSet('', 'all', 'enumerated', 'unknownFutureValue')] + [ValidateSet('all', 'enumerated', 'unknownFutureValue')] $ExcludeExternalTenantsMembershipKind, [Parameter()] @@ -755,7 +755,7 @@ function Set-TargetResource [Parameter()] [System.String] - [ValidateSet('', 'all', 'enumerated', 'unknownFutureValue')] + [ValidateSet('all', 'enumerated', 'unknownFutureValue')] $IncludeExternalTenantsMembershipKind, [Parameter()] @@ -769,7 +769,7 @@ function Set-TargetResource [Parameter()] [System.String] - [ValidateSet('', 'all', 'enumerated', 'unknownFutureValue')] + [ValidateSet('all', 'enumerated', 'unknownFutureValue')] $ExcludeExternalTenantsMembershipKind, [Parameter()] @@ -1738,7 +1738,7 @@ function Test-TargetResource [Parameter()] [System.String] - [ValidateSet('', 'all', 'enumerated', 'unknownFutureValue')] + [ValidateSet('all', 'enumerated', 'unknownFutureValue')] $IncludeExternalTenantsMembershipKind, [Parameter()] @@ -1752,7 +1752,7 @@ function Test-TargetResource [Parameter()] [System.String] - [ValidateSet('', 'all', 'enumerated', 'unknownFutureValue')] + [ValidateSet('all', 'enumerated', 'unknownFutureValue')] $ExcludeExternalTenantsMembershipKind, [Parameter()] diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.schema.mof index 9d1203c2af..67ec7174fa 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/MSFT_AADConditionalAccessPolicy.schema.mof @@ -16,10 +16,10 @@ class MSFT_AADConditionalAccessPolicy : OMI_BaseResource [Write, Description("AAD Admin Roles in scope of the Policy.")] String IncludeRoles[]; [Write, Description("AAD Admin Roles out of scope of the Policy.")] String ExcludeRoles[]; [Write, Description("Represents the Included internal guests or external user types. This is a multi-valued property. Supported values are: b2bCollaborationGuest, b2bCollaborationMember, b2bDirectConnectUser, internalGuest, OtherExternalUser, serviceProvider and unknownFutureValue."), ValueMap{"none","internalGuest","b2bCollaborationGuest","b2bCollaborationMember","b2bDirectConnectUser","otherExternalUser","serviceProvider","unknownFutureValue"}, Values{"none","internalGuest","b2bCollaborationGuest","b2bCollaborationMember","b2bDirectConnectUser","otherExternalUser","serviceProvider","unknownFutureValue"}] String IncludeGuestOrExternalUserTypes[]; - [Write, Description("Represents the Included Tenants membership kind. The possible values are: all, enumerated, unknownFutureValue. enumerated references an object of conditionalAccessEnumeratedExternalTenants derived type."), ValueMap{"","all","enumerated","unknownFutureValue"}, Values{"","all","enumerated","unknownFutureValue"}] String IncludeExternalTenantsMembershipKind; + [Write, Description("Represents the Included Tenants membership kind. The possible values are: all, enumerated, unknownFutureValue. enumerated references an object of conditionalAccessEnumeratedExternalTenants derived type."), ValueMap{"all","enumerated","unknownFutureValue"}, Values{"all","enumerated","unknownFutureValue"}] String IncludeExternalTenantsMembershipKind; [Write, Description("Represents the Included collection of tenant ids in the scope of Conditional Access for guests and external users policy targeting.")] String IncludeExternalTenantsMembers[]; [Write, Description("Represents the Excluded internal guests or external user types. This is a multi-valued property. Supported values are: b2bCollaborationGuest, b2bCollaborationMember, b2bDirectConnectUser, internalGuest, OtherExternalUser, serviceProvider and unknownFutureValue."), ValueMap{"none","internalGuest","b2bCollaborationGuest","b2bCollaborationMember","b2bDirectConnectUser","otherExternalUser","serviceProvider","unknownFutureValue"}, Values{"none","internalGuest","b2bCollaborationGuest","b2bCollaborationMember","b2bDirectConnectUser","otherExternalUser","serviceProvider","unknownFutureValue"}] String ExcludeGuestOrExternalUserTypes[]; - [Write, Description("Represents the Excluded Tenants membership kind. The possible values are: all, enumerated, unknownFutureValue. enumerated references an object of conditionalAccessEnumeratedExternalTenants derived type."), ValueMap{"","all","enumerated","unknownFutureValue"}, Values{"","all","enumerated","unknownFutureValue"}] String ExcludeExternalTenantsMembershipKind; + [Write, Description("Represents the Excluded Tenants membership kind. The possible values are: all, enumerated, unknownFutureValue. enumerated references an object of conditionalAccessEnumeratedExternalTenants derived type."), ValueMap{"all","enumerated","unknownFutureValue"}, Values{"all","enumerated","unknownFutureValue"}] String ExcludeExternalTenantsMembershipKind; [Write, Description("Represents the Excluded collection of tenant ids in the scope of Conditional Access for guests and external users policy targeting.")] String ExcludeExternalTenantsMembers[]; [Write, Description("Client Device Platforms in scope of the Policy.")] String IncludePlatforms[]; [Write, Description("Client Device Platforms out of scope of the Policy.")] String ExcludePlatforms[]; From b515483137b86b82ed6acf1f2d24e3e57a5d9a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andi=20Kr=C3=BCger?= <15608729+andikrueger@users.noreply.github.com> Date: Wed, 14 Feb 2024 20:18:06 +0100 Subject: [PATCH 06/11] AADUser: Ensure: Absent logic seems wrong Fixes #4265 --- CHANGELOG.md | 3 ++ .../MSFT_AADUser/MSFT_AADUser.psm1 | 49 ++++++++++--------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b04ca09a5d..8b60b1e459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * AADRoleEligibilityScheduleRequest * Fixed an issue where an error was thrown if no requests were found instead of simply returning the Null object. +* AADUser + * Fixed and issue where an user would be created even if the resrouce was set to absent. + FIXES [[#4265](https://github.com/microsoft/Microsoft365DSC/issues/4265)] * EXOMobileDeviceMailboxPolicy * Fixes an issue where an empty MinPasswordLength value was always passed down to the update logic flow. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADUser/MSFT_AADUser.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADUser/MSFT_AADUser.psm1 index e5756268a4..7f278519cb 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADUser/MSFT_AADUser.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADUser/MSFT_AADUser.psm1 @@ -180,8 +180,8 @@ function Get-TargetResource } else { - Write-Verbose -Message "Retrieving user from the exported instances" - $user = $Script:M365DSCExportInstances | Where-Object -FilterScript {$_.UserPrincipalName -eq $UserPrincipalName} + Write-Verbose -Message 'Retrieving user from the exported instances' + $user = $Script:M365DSCExportInstances | Where-Object -FilterScript { $_.UserPrincipalName -eq $UserPrincipalName } } Write-Verbose -Message "Found User $($UserPrincipalName)" @@ -193,7 +193,7 @@ function Get-TargetResource } # return membership of static groups only - [array]$currentMemberOf = (Get-MgUserMemberOfAsGroup -UserId $UserPrincipalName -All | Where-Object -FilterScript {$_.GroupTypes -notcontains 'DynamicMembership'}).DisplayName + [array]$currentMemberOf = (Get-MgUserMemberOfAsGroup -UserId $UserPrincipalName -All | Where-Object -FilterScript { $_.GroupTypes -notcontains 'DynamicMembership' }).DisplayName $userPasswordPolicyInfo = $user | Select-Object UserprincipalName, @{ N = 'PasswordNeverExpires'; E = { $_.PasswordPolicies -contains 'DisablePasswordExpiration' } @@ -204,7 +204,7 @@ function Get-TargetResource { $Script:allDirectoryRoleAssignment = Get-MgBetaRoleManagementDirectoryRoleAssignment -All } - $assignedRoles = $Script:allDirectoryRoleAssignment | Where-Object -FilterScript {$_.PrincipalId -eq $user.Id} + $assignedRoles = $Script:allDirectoryRoleAssignment | Where-Object -FilterScript { $_.PrincipalId -eq $user.Id } $rolesValue = @() if ($null -eq $Script:allAssignedRoles -and $assignedRoles.Length -gt 0) @@ -213,7 +213,7 @@ function Get-TargetResource } foreach ($assignedRole in $assignedRoles) { - $currentRoleInfo = $Script:allAssignedRoles | Where-Object -FilterScript {$_.Id -eq $assignedRole.RoleDefinitionId} + $currentRoleInfo = $Script:allAssignedRoles | Where-Object -FilterScript { $_.Id -eq $assignedRole.RoleDefinitionId } $rolesValue += $currentRoleInfo.DisplayName } @@ -418,7 +418,7 @@ function Set-TargetResource Write-Verbose -Message "Removing User {$UserPrincipalName}" Remove-MgUser -UserId $UserPrincipalName } - else + elseif ($Ensure -eq 'Present') { $PasswordPolicies = $null if ($PasswordNeverExpires) @@ -498,7 +498,7 @@ function Set-TargetResource if ($null -ne $Password) { - Write-Verbose -Message "PasswordProfile property will not be updated" + Write-Verbose -Message 'PasswordProfile property will not be updated' } $CreationParams.Add('UserId', $UserPrincipalName) @@ -584,20 +584,20 @@ function Set-TargetResource if ($null -eq $group) { New-M365DSCLogEntry -Message 'Error updating data:' ` - -Exception "Attempting to add a user to a group that doesn't exist" ` - -Source $($MyInvocation.MyCommand.Source) ` - -TenantId $TenantId ` - -Credential $Credential + -Exception "Attempting to add a user to a group that doesn't exist" ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential throw "Group '$memberOfGroup' does not exist in tenant" } if ($group.GroupTypes -contains 'DynamicMembership') { New-M365DSCLogEntry -Message 'Error updating data:' ` - -Exception "Attempting to add a user to a dynamic group" ` - -Source $($MyInvocation.MyCommand.Source) ` - -TenantId $TenantId ` - -Credential $Credential + -Exception 'Attempting to add a user to a dynamic group' ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential throw "Cannot add user $UserPrincipalName to group '$memberOfGroup' because it is a dynamic group" } @@ -615,20 +615,20 @@ function Set-TargetResource if ($null -eq $group) { New-M365DSCLogEntry -Message 'Error updating data:' ` - -Exception "Attempting to add a user to a group that doesn't exist" ` - -Source $($MyInvocation.MyCommand.Source) ` - -TenantId $TenantId ` - -Credential $Credential + -Exception "Attempting to add a user to a group that doesn't exist" ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential throw "Group '$($_.InputObject)' does not exist in tenant" } if ($group.GroupTypes -contains 'DynamicMembership') { New-M365DSCLogEntry -Message 'Error updating data:' ` - -Exception "Attempting to add a user to a dynamic group" ` - -Source $($MyInvocation.MyCommand.Source) ` - -TenantId $TenantId ` - -Credential $Credential + -Exception 'Attempting to add a user to a dynamic group' ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential throw "Cannot add user $UserPrincipalName to group '$($_.InputObject)' because it is a dynamic group" } @@ -914,7 +914,8 @@ function Export-TargetResource Property = $propertiesToRetrieve ErrorAction = 'Stop' } - if ($Filter -like "*endsWith*") { + if ($Filter -like '*endsWith*') + { $ExportParameters.Add('CountVariable', 'count') $ExportParameters.Add('ConsistencyLevel', 'eventual') } From 62b663cccfe6d648a2fa65f0a08a320b3bdca979 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 14 Feb 2024 19:38:56 +0000 Subject: [PATCH 07/11] Updated Resources and Cmdlet documentation pages --- docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md b/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md index bbeec98b03..d0da6f7b76 100644 --- a/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md +++ b/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md @@ -19,10 +19,10 @@ | **IncludeRoles** | Write | StringArray[] | AAD Admin Roles in scope of the Policy. | | | **ExcludeRoles** | Write | StringArray[] | AAD Admin Roles out of scope of the Policy. | | | **IncludeGuestOrExternalUserTypes** | Write | StringArray[] | Represents the Included internal guests or external user types. This is a multi-valued property. Supported values are: b2bCollaborationGuest, b2bCollaborationMember, b2bDirectConnectUser, internalGuest, OtherExternalUser, serviceProvider and unknownFutureValue. | `none`, `internalGuest`, `b2bCollaborationGuest`, `b2bCollaborationMember`, `b2bDirectConnectUser`, `otherExternalUser`, `serviceProvider`, `unknownFutureValue` | -| **IncludeExternalTenantsMembershipKind** | Write | String | Represents the Included Tenants membership kind. The possible values are: all, enumerated, unknownFutureValue. enumerated references an object of conditionalAccessEnumeratedExternalTenants derived type. | ``, `all`, `enumerated`, `unknownFutureValue` | +| **IncludeExternalTenantsMembershipKind** | Write | String | Represents the Included Tenants membership kind. The possible values are: all, enumerated, unknownFutureValue. enumerated references an object of conditionalAccessEnumeratedExternalTenants derived type. | `all`, `enumerated`, `unknownFutureValue` | | **IncludeExternalTenantsMembers** | Write | StringArray[] | Represents the Included collection of tenant ids in the scope of Conditional Access for guests and external users policy targeting. | | | **ExcludeGuestOrExternalUserTypes** | Write | StringArray[] | Represents the Excluded internal guests or external user types. This is a multi-valued property. Supported values are: b2bCollaborationGuest, b2bCollaborationMember, b2bDirectConnectUser, internalGuest, OtherExternalUser, serviceProvider and unknownFutureValue. | `none`, `internalGuest`, `b2bCollaborationGuest`, `b2bCollaborationMember`, `b2bDirectConnectUser`, `otherExternalUser`, `serviceProvider`, `unknownFutureValue` | -| **ExcludeExternalTenantsMembershipKind** | Write | String | Represents the Excluded Tenants membership kind. The possible values are: all, enumerated, unknownFutureValue. enumerated references an object of conditionalAccessEnumeratedExternalTenants derived type. | ``, `all`, `enumerated`, `unknownFutureValue` | +| **ExcludeExternalTenantsMembershipKind** | Write | String | Represents the Excluded Tenants membership kind. The possible values are: all, enumerated, unknownFutureValue. enumerated references an object of conditionalAccessEnumeratedExternalTenants derived type. | `all`, `enumerated`, `unknownFutureValue` | | **ExcludeExternalTenantsMembers** | Write | StringArray[] | Represents the Excluded collection of tenant ids in the scope of Conditional Access for guests and external users policy targeting. | | | **IncludePlatforms** | Write | StringArray[] | Client Device Platforms in scope of the Policy. | | | **ExcludePlatforms** | Write | StringArray[] | Client Device Platforms out of scope of the Policy. | | From b4e7d476f04653cd2490262a163192f4ac9ab5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andi=20Kr=C3=BCger?= <15608729+andikrueger@users.noreply.github.com> Date: Wed, 14 Feb 2024 21:05:27 +0100 Subject: [PATCH 08/11] Terms of use: Agreement.Read.All and Agreement.ReadWrite.All are missing Fixes #3329 --- CHANGELOG.md | 2 ++ .../settings.json | 26 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ba895ab13..16d2b1ecb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * AADConditionalAccessPolicy * Removed invalid empty string value that was added to the validate set of two parameters. + * Updated permission reference for app-onlzy authentication. + FIXES [[#3329](https://github.com/microsoft/Microsoft365DSC/issues/3329)] * AADRoleEligibilityScheduleRequest * Fixed an issue where an error was thrown if no requests were found instead of simply returning the Null object. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/settings.json index 5d9c48c4c3..21929cd0e8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/settings.json @@ -52,16 +52,40 @@ }, "application": { "read": [ + { + "name": "Agreement.Read.All" + }, + { + "name": "Group.Read.All" + }, { "name": "Policy.Read.All" + }, + { + "name": "RoleManagement.Read.Directory" + }, + { + "name": "User.Read.All" } ], "update": [ { - "name": "Application.Read.All" + "name": "Agreement.Read.All" + }, + { + "name": "Group.Read.All" + }, + { + "name": "Policy.Read.All" }, { "name": "Policy.ReadWrite.ConditionalAccess" + }, + { + "name": "RoleManagement.Read.Directory" + }, + { + "name": "User.Read.All" } ] } From c5de137652353ad3dfde2fd8efe8608afae01cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andi=20Kr=C3=BCger?= <15608729+andikrueger@users.noreply.github.com> Date: Wed, 14 Feb 2024 21:09:01 +0100 Subject: [PATCH 09/11] Added Application.Read.All to permission list --- .../MSFT_AADConditionalAccessPolicy/settings.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/settings.json index 21929cd0e8..bf18ad856d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConditionalAccessPolicy/settings.json @@ -55,6 +55,9 @@ { "name": "Agreement.Read.All" }, + { + "name": "Application.Read.All" + }, { "name": "Group.Read.All" }, @@ -72,6 +75,9 @@ { "name": "Agreement.Read.All" }, + { + "name": "Application.Read.All" + }, { "name": "Group.Read.All" }, From 26abeb3b3dc4846fc862bf2b48782156974e2248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andi=20Kr=C3=BCger?= <15608729+andikrueger@users.noreply.github.com> Date: Wed, 14 Feb 2024 21:19:54 +0100 Subject: [PATCH 10/11] SPOSharingSettings: Multiple sites returned from Get-PnPTenantSite Fixes #2759 --- CHANGELOG.md | 3 +++ .../MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ba895ab13..0ccb99ff9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,9 @@ * Fix typo in variable which made it export incorrectly and report that resource was not in correct state due to testing an incorrect value FIXES [#3972](https://github.com/microsoft/Microsoft365DSC/issues/3972) +* SPOSharingSettings + * Fixed an issue where the resource would return multiple sites. + FIXES [#2759](https://github.com/microsoft/Microsoft365DSC/issues/2759) * DEPENDENCIES * Updated DSCParser to version 1.4.0.2. * Updated Microsoft.Graph dependencies to version 2.13.1. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 index b8dd694459..b8c873870f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 @@ -520,7 +520,7 @@ function Set-TargetResource Set-PnPTenant @CurrentParameters | Out-Null if ($SetMySharingCapability) { - $mysite = Get-PnPTenantSite | Where-Object { $_.Url -match '-my.sharepoint.com/' } + $mysite = Get-PnPTenantSite | Where-Object { $_.Url -match '-my.sharepoint.com/' -and $_.Template -notmatch '^RedirectSite#' } Set-PnPTenantSite -Identity $mysite.Url -SharingCapability $MySiteSharingCapability } } From a60886faf4864242cdc010e5fd7faadcc3348871 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 14 Feb 2024 21:48:22 +0000 Subject: [PATCH 11/11] Updated Resources and Cmdlet documentation pages --- docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md b/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md index d0da6f7b76..41ab4778b6 100644 --- a/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md +++ b/docs/docs/resources/azure-ad/AADConditionalAccessPolicy.md @@ -80,11 +80,11 @@ To authenticate with the Microsoft Graph API, this resource required the followi - **Read** - - Policy.Read.All + - Agreement.Read.All, Application.Read.All, Group.Read.All, Policy.Read.All, RoleManagement.Read.Directory, User.Read.All - **Update** - - Application.Read.All, Policy.ReadWrite.ConditionalAccess + - Agreement.Read.All, Application.Read.All, Group.Read.All, Policy.Read.All, Policy.ReadWrite.ConditionalAccess, RoleManagement.Read.Directory, User.Read.All ## Examples