From 6c62a502bd80f434c97ec6056680faaa4405b0a9 Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Sat, 14 Oct 2023 17:25:12 +0200 Subject: [PATCH 01/16] Add IntuneDeviceCleanupRule Resource --- CHANGELOG.md | 3 + .../MSFT_IntuneDeviceCleanupRule.psm1 | 381 ++++++++++++++++++ .../MSFT_IntuneDeviceCleanupRule.schema.mof | 13 + .../MSFT_IntuneDeviceCleanupRule/readme.md | 6 + .../settings.json | 32 ++ .../1-SetDeviceCleanupRule.ps1 | 24 ++ ...ft365DSC.IntuneDeviceCleanupRule.Tests.ps1 | 118 ++++++ 7 files changed, 577 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceCleanupRule.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 18858d6238..53ef52bf46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ * TeamsUserPolicyAssignment * Initial release. FIXES [#3777](https://github.com/microsoft/Microsoft365DSC/issues/3777) +* IntuneDeviceCleanupRule + * Initial release. + FIXES [#3599](https://github.com/microsoft/Microsoft365DSC/issues/3599) # 1.23.1011.1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.psm1 new file mode 100644 index 0000000000..73fe0f16b8 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.psm1 @@ -0,0 +1,381 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.Int32] + [ValidateRange(30, 270)] + $DeviceInactivityBeforeRetirementInDays, + + [Parameter()] + [System.String] + [ValidateSet('Absent', 'Present')] + $Ensure = 'Present', + + [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 + ) + + if ($Enabled -and $DeviceInactivityBeforeRetirementInDays -eq 0) + { + throw [System.ArgumentException]::new('DeviceInactivityBeforeRetirementInDays must be greater than 30 and less than 270 when Enabled is set to true.') + } + + Write-Verbose -Message "Checking for the Intune Device Cleanup Rule" + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $nullResult = $PSBoundParameters + $nullResult.Ensure = 'Absent' + + try + { + $url = "https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings" + $cleanupRule = Invoke-MgGraphRequest -Method GET -Uri $url -ErrorAction Stop + + $return = @{ + Enabled = $cleanupRule.deviceInactivityBeforeRetirementInDays -gt 0 + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + Managedidentity = $ManagedIdentity.IsPresent + } + + if ($return.Enabled) + { + $return.Add('DeviceInactivityBeforeRetirementInDays', $cleanupRule.deviceInactivityBeforeRetirementInDays) + } + + return $return + } + catch + { + 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.Boolean] + $Enabled, + + [Parameter()] + [System.Int32] + [ValidateRange(30, 270)] + $DeviceInactivityBeforeRetirementInDays, + + [Parameter()] + [System.String] + [ValidateSet('Absent', 'Present')] + $Ensure = 'Present', + + [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 + ) + + if ($Enabled -and $DeviceInactivityBeforeRetirementInDays -eq 0) + { + throw [System.ArgumentException]::new('DeviceInactivityBeforeRetirementInDays must be greater than 30 and less than 270 when Enabled is set to true.') + } + + Write-Verbose -Message "Updating Device Cleanup Rule" + + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $url = "https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings" + $body = @{ + DeviceInactivityBeforeRetirementInDays = "$(if ($Enabled) { $DeviceInactivityBeforeRetirementInDays } else { 0 })" + } + Invoke-MgGraphRequest -Method PATCH -Uri $url -Body ($body | ConvertTo-Json) +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.Int32] + [ValidateRange(30, 270)] + $DeviceInactivityBeforeRetirementInDays, + + [Parameter()] + [System.String] + [ValidateSet('Absent', 'Present')] + $Ensure = 'Present', + + [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 + ) + + if ($Enabled -and $DeviceInactivityBeforeRetirementInDays -eq 0) + { + throw [System.ArgumentException]::new('DeviceInactivityBeforeRetirementInDays must be greater than 30 and less than 270 when Enabled is set to true.') + } + + #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 Device Cleanup Rule" + + $CurrentValues = Get-TargetResource @PSBoundParameters + + 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('ApplicationSecret') | Out-Null + $ValuesToCheck.Remove('TenantId') | Out-Null + + if ($CurrentValues.Enabled -eq $false) { + $ValuesToCheck.Remove('DeviceInactivityBeforeRetirementInDays') | Out-Null + } + + $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + Write-Verbose -Message "Test-TargetResource returned $TestResult" + + return $TestResult +} + +function Export-TargetResource +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity + ) + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + try + { + $url = "https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings" + [array]$cleanupRules = Invoke-MgGraphRequest -Method GET -Uri $url -ErrorAction Stop + $i = 1 + $dscContent = '' + if ($categories.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + + foreach ($cleanupRule in $cleanupRules) + { + Write-Host " |---[$i/$($cleanupRules.Count)] Cleanup Rule" -NoNewline + $params = @{ + Enabled = $cleanupRule.deviceInactivityBeforeRetirementInDays -gt 0 + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + ApplicationSecret = $ApplicationSecret + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + Managedidentity = $ManagedIdentity.IsPresent + } + + if ($params.Enabled) { + $params.Add('DeviceInactivityBeforeRetirementInDays', $cleanupRule.deviceInactivityBeforeRetirementInDays) + } + + $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 + { + if ($_.Exception -like '*401*' -or $_.ErrorDetails.Message -like "*`"ErrorCode`":`"Forbidden`"*") + { + Write-Host "`r`n $($Global:M365DSCEmojiYellowCircle) The current tenant is not registered for Intune." + } + else + { + 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_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.schema.mof new file mode 100644 index 0000000000..e8bdae35df --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.schema.mof @@ -0,0 +1,13 @@ +[ClassVersion("1.0.0.0"), FriendlyName("IntuneDeviceCleanupRule")] +class MSFT_IntuneDeviceCleanupRule : OMI_BaseResource +{ + [Key, Description("Indicates whether the cleanup rule is enabled.")] Boolean Enabled; + [Write, Description("Number of days until Intune devices are deleted. Minimum: 30, Maximum: 270.")] UInt32 DeviceInactivityBeforeRetirementInDays; + [Write, Description("Present ensures the category exists, absent ensures it is removed."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] string Ensure; + [Write, Description("Credentials of the Intune 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("Secret of the Azure Active Directory tenant used for authentication."), EmbeddedInstance("MSFT_Credential")] String ApplicationSecret; + [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; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/readme.md new file mode 100644 index 0000000000..b5aa2bc25a --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/readme.md @@ -0,0 +1,6 @@ + +# IntuneDeviceCleanupRule + +## Description + +This resource configures the Intune device cleanup rule. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/settings.json new file mode 100644 index 0000000000..0ca5cbbdbb --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/settings.json @@ -0,0 +1,32 @@ +{ + "resourceName": "IntuneDeviceCleanupRule", + "description": "This resource configures the Intune device cleanup rule.", + "permissions": { + "graph": { + "delegated": { + "read": [ + { + "name": "DeviceManagementManagedDevices.Read.All" + } + ], + "update": [ + { + "name": "DeviceManagementManagedDevices.ReadWrite.All" + } + ] + }, + "application": { + "read": [ + { + "name": "DeviceManagementManagedDevices.Read.All" + } + ], + "update": [ + { + "name": "DeviceManagementManagedDevices.ReadWrite.All" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 b/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 new file mode 100644 index 0000000000..e309830206 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 @@ -0,0 +1,24 @@ +<# +This example sets the device cleanup rule. +#> + +Configuration Example +{ + param( + [Parameter(Mandatory = $true)] + [PSCredential] + $credsGlobalAdmin + ) + Import-DscResource -ModuleName Microsoft365DSC + + node localhost + { + IntuneDeviceCleanupRule 'Example' + { + Enabled = $true + DeviceInactivityBeforeRetirementInDays = 30 + Ensure = 'Present' + Credential = $credsGlobalAdmin + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceCleanupRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceCleanupRule.Tests.ps1 new file mode 100644 index 0000000000..549df8c0a9 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceCleanupRule.Tests.ps1 @@ -0,0 +1,118 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath '..\..\Unit' ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Microsoft365.psm1' ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Generic.psm1' ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\UnitTestHelper.psm1' ` + -Resolve) + +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource 'IntuneDeviceCleanupRule' -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 'Pass@word1' -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' + } + + Mock -CommandName Invoke-MgGraphRequest -MockWith { + } + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + } + + # Test contexts + Context -Name 'When the policy already exists and is NOT in the Desired State' -Fixture { + BeforeAll { + $testParams = @{ + Enabled = $true + DeviceInactivityBeforeRetirementInDays = 30 + Ensure = 'Present' + Credential = $Credential + } + + Mock -CommandName Invoke-MgGraphRequest -MockWith { + return @{ + DeviceInactivityBeforeRetirementInDays = 0 + } + } + } + + It 'Should return Present 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 update the device cleanup rule from the Set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Invoke-MgGraphRequest -Exactly 1 + } + } + + Context -Name 'When the policy already exists and IS in the Desired State' -Fixture { + BeforeAll { + $testParams = @{ + Enabled = $true + DeviceInactivityBeforeRetirementInDays = 30 + Ensure = 'Present' + Credential = $Credential + } + + Mock -CommandName Invoke-MgGraphRequest -MockWith { + return @{ + DeviceInactivityBeforeRetirementInDays = 30 + } + } + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential + } + + Mock -CommandName Invoke-MgGraphRequest -MockWith { + return @{ + DeviceInactivityBeforeRetirementInDays = 30 + } + } + } + + 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 6d0c39b385008ed133a602112b74991ba11f24cf Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Tue, 17 Oct 2023 14:49:47 +0200 Subject: [PATCH 02/16] Update IntuneDeviceCleanupRule to single instance --- .../MSFT_IntuneDeviceCleanupRule.psm1 | 23 ++++++++++++++++--- .../MSFT_IntuneDeviceCleanupRule.schema.mof | 1 + ...ft365DSC.IntuneDeviceCleanupRule.Tests.ps1 | 6 +++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.psm1 index 73fe0f16b8..f60d059e3f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.psm1 @@ -4,6 +4,11 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + [Parameter(Mandatory = $true)] [System.Boolean] $Enabled, @@ -69,11 +74,12 @@ function Get-TargetResource try { - $url = "https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings" + $url = $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.ResourceUrl + "beta/deviceManagement/managedDeviceCleanupSettings" $cleanupRule = Invoke-MgGraphRequest -Method GET -Uri $url -ErrorAction Stop $return = @{ Enabled = $cleanupRule.deviceInactivityBeforeRetirementInDays -gt 0 + IsSingleInstance = 'Yes' Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId @@ -107,6 +113,11 @@ function Set-TargetResource [CmdletBinding()] param ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + [Parameter(Mandatory = $true)] [System.Boolean] $Enabled, @@ -168,7 +179,7 @@ function Set-TargetResource Add-M365DSCTelemetryEvent -Data $data #endregion - $url = "https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings" + $url = $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.ResourceUrl + "beta/deviceManagement/managedDeviceCleanupSettings" $body = @{ DeviceInactivityBeforeRetirementInDays = "$(if ($Enabled) { $DeviceInactivityBeforeRetirementInDays } else { 0 })" } @@ -181,6 +192,11 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + [Parameter(Mandatory = $true)] [System.Boolean] $Enabled, @@ -310,7 +326,7 @@ function Export-TargetResource try { - $url = "https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupSettings" + $url = $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.ResourceUrl + "beta/deviceManagement/managedDeviceCleanupSettings" [array]$cleanupRules = Invoke-MgGraphRequest -Method GET -Uri $url -ErrorAction Stop $i = 1 $dscContent = '' @@ -329,6 +345,7 @@ function Export-TargetResource $params = @{ Enabled = $cleanupRule.deviceInactivityBeforeRetirementInDays -gt 0 Ensure = 'Present' + IsSingleInstance = 'Yes' Credential = $Credential ApplicationId = $ApplicationId ApplicationSecret = $ApplicationSecret diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.schema.mof index e8bdae35df..8e83fcef35 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceCleanupRule/MSFT_IntuneDeviceCleanupRule.schema.mof @@ -1,6 +1,7 @@ [ClassVersion("1.0.0.0"), FriendlyName("IntuneDeviceCleanupRule")] class MSFT_IntuneDeviceCleanupRule : OMI_BaseResource { + [Key, Description("Only valid value is 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance; [Key, Description("Indicates whether the cleanup rule is enabled.")] Boolean Enabled; [Write, Description("Number of days until Intune devices are deleted. Minimum: 30, Maximum: 270.")] UInt32 DeviceInactivityBeforeRetirementInDays; [Write, Description("Present ensures the category exists, absent ensures it is removed."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] string Ensure; diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceCleanupRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceCleanupRule.Tests.ps1 index 549df8c0a9..35a043cb2a 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceCleanupRule.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceCleanupRule.Tests.ps1 @@ -41,12 +41,13 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } # Test contexts - Context -Name 'When the policy already exists and is NOT in the Desired State' -Fixture { + Context -Name 'When the policy is NOT in the Desired State' -Fixture { BeforeAll { $testParams = @{ Enabled = $true DeviceInactivityBeforeRetirementInDays = 30 Ensure = 'Present' + IsSingleInstance = 'Yes' Credential = $Credential } @@ -71,12 +72,13 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } } - Context -Name 'When the policy already exists and IS in the Desired State' -Fixture { + Context -Name 'When the policy IS in the Desired State' -Fixture { BeforeAll { $testParams = @{ Enabled = $true DeviceInactivityBeforeRetirementInDays = 30 Ensure = 'Present' + IsSingleInstance = 'Yes' Credential = $Credential } From ce1c9082796fb6ef8c8553fa1d38f7d8709663e8 Mon Sep 17 00:00:00 2001 From: deb Date: Thu, 26 Oct 2023 01:33:03 +0530 Subject: [PATCH 03/16] fix: typo --- docs/docs/contributing/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/contributing/getting-started.md b/docs/docs/contributing/getting-started.md index f89ab37973..78127933e3 100644 --- a/docs/docs/contributing/getting-started.md +++ b/docs/docs/contributing/getting-started.md @@ -10,7 +10,7 @@ The Microsoft365DSC project is open to contribution from the community. In order ![Infographic](../Images/CreateFork.jpg){ align=center width=500 } -- A dialog box asking you to select the account to fork the repository will appear. Select you account from the list. +- A dialog box asking you to select the account to fork the repository will appear. Select your account from the list. ![Select Account to fork under](../Images/ForkLocation.jpg){ width=500 } From 017fefc98226bace3459cee715d6d76f9548c4a5 Mon Sep 17 00:00:00 2001 From: William-francillette Date: Wed, 25 Oct 2023 21:43:26 +0100 Subject: [PATCH 04/16] fix approvers and reviewers + M365DSCDRGUtil:convertComplextoHashtable --- ...nagementAccessPackageAssignmentPolicy.psm1 | 91 +++++++++++++++++-- .../Modules/M365DSCDRGUtil.psm1 | 8 +- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADEntitlementManagementAccessPackageAssignmentPolicy/MSFT_AADEntitlementManagementAccessPackageAssignmentPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADEntitlementManagementAccessPackageAssignmentPolicy/MSFT_AADEntitlementManagementAccessPackageAssignmentPolicy.psm1 index 099402bc79..030d514a65 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADEntitlementManagementAccessPackageAssignmentPolicy/MSFT_AADEntitlementManagementAccessPackageAssignmentPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADEntitlementManagementAccessPackageAssignmentPolicy/MSFT_AADEntitlementManagementAccessPackageAssignmentPolicy.psm1 @@ -126,7 +126,7 @@ function Get-TargetResource Write-Verbose -Message "Found access package assignment policy with id {$($getValue.Id)} and DisplayName {$DisplayName}" #region Format AccessReviewSettings - $formattedAccessReviewSettings = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $getValue.AccessReviewSettings + $formattedAccessReviewSettings = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $getValue.AccessReviewSettings -Verbose if($null -ne $formattedAccessReviewSettings) { $formattedAccessReviewSettings.remove('additionalProperties') | Out-Null @@ -139,6 +139,7 @@ function Get-TargetResource if (-not [String]::isNullOrEmpty($setting.AdditionalProperties.id)) { $user = Get-MgUser -UserId $setting.AdditionalProperties.id -ErrorAction SilentlyContinue + if ($null -ne $user) { $setting.add('Id', $user.UserPrincipalName) @@ -148,7 +149,7 @@ function Get-TargetResource { $setting.add('ManagerLevel', $setting.AdditionalProperties.managerLevel) } - $setting.remove('additionalProperties') | Out-Null + $setting.remove('AdditionalProperties') | Out-Null } } #endregion @@ -170,7 +171,11 @@ function Get-TargetResource $setting.add('odataType', $setting.AdditionalProperties.'@odata.type') if (-not [String]::isNullOrEmpty($setting.AdditionalProperties.id)) { - $setting.add('Id', $setting.AdditionalProperties.id) + $user = Get-MgUser -UserId $setting.AdditionalProperties.id -ErrorAction SilentlyContinue + if ($null -ne $user) + { + $setting.add('Id', $user.UserPrincipalName) + } } if (-not [String]::isNullOrEmpty($setting.AdditionalProperties.managerLevel)) { @@ -187,7 +192,11 @@ function Get-TargetResource $setting.add('odataType', $setting.AdditionalProperties.'@odata.type') if (-not [String]::isNullOrEmpty($setting.AdditionalProperties.id)) { - $setting.add('Id', $setting.AdditionalProperties.id) + $user = Get-MgUser -UserId $setting.AdditionalProperties.id -ErrorAction SilentlyContinue + if ($null -ne $user) + { + $setting.add('Id', $user.UserPrincipalName) + } } if (-not [String]::isNullOrEmpty($setting.AdditionalProperties.managerLevel)) { @@ -462,6 +471,36 @@ function Set-TargetResource } } } + if ( $null -ne $CreateParameters.RequestApprovalSettings.ApprovalStages.PrimaryApprovers) + { + for ($i = 0; $i -lt $CreateParameters.RequestApprovalSettings.ApprovalStages.PrimaryApprovers.Length; $i++) + { + $primaryApprover = $CreateParameters.RequestApprovalSettings.ApprovalStages.PrimaryApprovers[$i] + if ($null -ne $primaryApprover.id) + { + $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($primaryApprover.Id.Split('@')[0])')" -ErrorAction SilentlyContinue + if ($null -ne $user) + { + $CreateParameters.RequestApprovalSettings.ApprovalStages.PrimaryApprovers[$i].Id = $user.Id + } + } + } + } + if ( $null -ne $CreateParameters.RequestApprovalSettings.ApprovalStages.EscalationApprovers) + { + for ($i = 0; $i -lt $CreateParameters.RequestApprovalSettings.ApprovalStages.EscalationApprovers.Length; $i++) + { + $escalationApprover = $CreateParameters.RequestApprovalSettings.ApprovalStages.EscalationApprovers[$i] + if ($null -ne $escalationApprover.id) + { + $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($escalationApprover.Id.Split('@')[0])')" -ErrorAction SilentlyContinue + if ($null -ne $user) + { + $CreateParameters.RequestApprovalSettings.ApprovalStages.EscalationApprovers[$i].Id = $user.Id + } + } + } + } if ($null -ne $CreateParameters.RequestorSettings -and $null -ne $CreateParameters.RequestorSettings.AllowedRequestors) { for ($i = 0; $i -lt $CreateParameters.RequestorSettings.AllowedRequestors.Length; $i++) @@ -527,6 +566,36 @@ function Set-TargetResource } } } + if ($null -ne $UpdateParameters.RequestApprovalSettings.ApprovalStages.PrimaryApprovers) + { + for ($i = 0; $i -lt $UpdateParameters.RequestApprovalSettings.ApprovalStages.PrimaryApprovers.Length; $i++) + { + $primaryApprover = $UpdateParameters.RequestApprovalSettings.ApprovalStages.PrimaryApprovers[$i] + if ($null -ne $primaryApprover.id) + { + $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($primaryApprover.Id.Split('@')[0])')" -ErrorAction SilentlyContinue + if ($null -ne $user) + { + $UpdateParameters.RequestApprovalSettings.ApprovalStages.PrimaryApprovers[$i].Id = $user.Id + } + } + } + } + if ($null -ne $UpdateParameters.RequestApprovalSettings.ApprovalStages.EscalationApprovers) + { + for ($i = 0; $i -lt $UpdateParameters.RequestApprovalSettings.ApprovalStages.EscalationApprovers.Length; $i++) + { + $escalationApprover = $UpdateParameters.RequestApprovalSettings.ApprovalStages.EscalationApprovers[$i] + if ($null -ne $escalationApprover.id) + { + $user = Get-MgUser -Filter "startswith(UserPrincipalName, '$($escalationApprover.Id.Split('@')[0])')" -ErrorAction SilentlyContinue + if ($null -ne $user) + { + $UpdateParameters.RequestApprovalSettings.ApprovalStages.EscalationApprovers[$i].Id = $user.Id + } + } + } + } if ($null -ne $UpdateParameters.RequestorSettings -and $null -ne $UpdateParameters.RequestorSettings.AllowedRequestors) { #Write-Verbose -Message "Updating Requestors' Id" @@ -810,7 +879,17 @@ function Export-TargetResource if ($null -ne $Results.AccessReviewSettings) { - $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.AccessReviewSettings -CIMInstanceName MicrosoftGraphassignmentreviewsettings + $complexMapping = @( + @{ + Name = 'Reviewers' + CimInstanceName = 'MicrosoftGraphuserset' + IsRequired = $false + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.AccessReviewSettings ` + -CIMInstanceName MicrosoftGraphassignmentreviewsettings ` + -ComplexTypeMapping $complexMapping if ($complexTypeStringResult) { $Results.AccessReviewSettings = $complexTypeStringResult @@ -939,7 +1018,7 @@ function Export-TargetResource if ($null -ne $Results.AccessReviewSettings) { $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'AccessReviewSettings' - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Reviewers' + #$currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Reviewers' } if ($null -ne $Results.Questions ) { diff --git a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 index 53c6531a34..318f1becae 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCDRGUtil.psm1 @@ -134,21 +134,21 @@ function Get-M365DSCDRGComplexTypeToHashtable return , [hashtable[]]$results } + if ($ComplexObject.getType().fullname -like '*Dictionary*') { $results = @{} $ComplexObject = [hashtable]::new($ComplexObject) $keys = $ComplexObject.Keys + foreach ($key in $keys) { if ($null -ne $ComplexObject.$key) { $keyName = $key - $keyType = $ComplexObject.$key.gettype().fullname - - if ($keyType -like '*CimInstance*' -or $keyType -like '*Dictionary*' -or $keyType -like 'Microsoft.Graph.PowerShell.Models.*' -or $keyType -like '*[[\]]') + if ($keyType -like '*CimInstance*' -or $keyType -like '*Dictionary*' -or $keyType -like 'Microsoft.Graph.PowerShell.Models.*' -or $keyType -like 'Microsoft.Graph.Beta.PowerShell.Models.*' -or $keyType -like '*[[\]]') { $hash = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject.$key @@ -185,7 +185,7 @@ function Get-M365DSCDRGComplexTypeToHashtable if ($null -ne $ComplexObject.$keyName) { $keyType = $ComplexObject.$keyName.gettype().fullname - if ($keyType -like '*CimInstance*' -or $keyType -like '*Dictionary*' -or $keyType -like 'Microsoft.Graph.PowerShell.Models.*') + if ($keyType -like '*CimInstance*' -or $keyType -like '*Dictionary*' -or $keyType -like 'Microsoft.Graph.*PowerShell.Models.*') { $hash = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject.$keyName From c9d1f4cbdafcac992c4668b49cc0fa4bfb679b14 Mon Sep 17 00:00:00 2001 From: William-francillette Date: Wed, 25 Oct 2023 21:48:30 +0100 Subject: [PATCH 05/16] add changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0943e19ce1..9d374d6822 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ # UNRELEASED +* AADEntitlementManagementAccessPackageAssignmentPolicy + * Fixes an issue where reviewers were not properly exported +* M365DSCDRGUTIL + * Fixes an issue with Get-M365DSCDRGComplexTypeToHashtable where Beta cmdlet were not recognized for recursive calls + FIXES [#3448](https://github.com/microsoft/Microsoft365DSC/issues/3448) * AADAttributeSet * Initial Release. * AADAuthenticationContext From cde3fb2ba93524c5f22fef14fda5ed37b51f920c Mon Sep 17 00:00:00 2001 From: deb Date: Fri, 27 Oct 2023 22:44:16 +0530 Subject: [PATCH 06/16] fixed typos --- docs/docs/concepts/personas.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/concepts/personas.md b/docs/docs/concepts/personas.md index f5da2b61f4..7efaac432e 100644 --- a/docs/docs/concepts/personas.md +++ b/docs/docs/concepts/personas.md @@ -69,7 +69,7 @@ This article describes the personas we've identified for Microsoft365DSC and pro Description: -

The Security Administrators are responsible for defining new Entra Identity policies, make updates to exsting ones and monitor them for configuration drifts at scale and across one or multiple tenants. Their goal is to ensure the overal security of the tenant by ensuring only authorized users can perform certain tasks. They are dealing with components such as:

+

The Security Administrators are responsible for defining new Entra Identity policies, make updates to existing ones and monitor them for configuration drifts at scale and across one or multiple tenants. Their goal is to ensure the overal security of the tenant by ensuring only authorized users can perform certain tasks. They are dealing with components such as:

  • AADAuthenticationMethodPolicy
  • AADAuthorizationPolicy
  • @@ -149,7 +149,7 @@ This article describes the personas we've identified for Microsoft365DSC and pro Description: -

    The Teams Collaboration Administrators are responsible for ensuring the proper functioning of the Teams collaboration features, such as managing channel, managing teams, etc. and for their associated policies (e.g., Teams Channel Policies, Teams Messaging Policies, etc.). They are dealing with components such as:

    +

    The Teams Collaboration Administrators are responsible for ensuring the proper functioning of the Teams collaboration features, such as managing channels, managing teams, etc. and for their associated policies (e.g., Teams Channel Policies, Teams Messaging Policies, etc.). They are dealing with components such as:

    • TeamsAppPermissionPolicy
    • TeamsChannel
    • From 1da75aac4a7573fa2730a49da80103bb2ae9252e Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Fri, 27 Oct 2023 15:05:38 -0700 Subject: [PATCH 07/16] Fixes #3787 --- CHANGELOG.md | 6 +++ ...SFT_AADRoleEligibilityScheduleRequest.psm1 | 49 ++++++++++--------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93a773633d..4ba3b82a9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change log for Microsoft365DSC +# UNRELEASED + +* AADRoleEligibilityScheduleRequest + * Fixes how the Get method retrieves existing instances for Groups. + FIXES [#3787](https://github.com/microsoft/Microsoft365DSC/issues/3787) + # 1.23.1025.1 * AADEntitlementManagementAccessPackageAssignmentPolicy diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 index 873eddbbce..f7a9a96153 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 @@ -177,10 +177,11 @@ $RoleDefinitionId = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id Write-Verbose -Message "Found Role {$RoleDefinitionId}" + $schedule = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -Filter "PrincipalId eq '$PrincipalId' and RoleDefinitionId eq '$RoleDefinitionId'" $request = Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -Filter "PrincipalId eq '$PrincipalId' and RoleDefinitionId eq '$RoleDefinitionId'" } } - if ($null -eq $request) + if ($null -eq $schedule) { return $nullResult } @@ -188,12 +189,12 @@ Write-Verbose -Message "Found existing AADRolelLigibilityScheduleRequest" if ($PrincipalType -eq 'User') { - $PrincipalInstance = Get-MgUser -UserId $request.PrincipalId -ErrorAction SilentlyContinue + $PrincipalInstance = Get-MgUser -UserId $schedule.PrincipalId -ErrorAction SilentlyContinue $PrincipalTypeValue = 'User' } if ($null -eq $PrincipalInstance -or $PrincipalType -eq 'Group') { - $PrincipalInstance = Get-MGGroup -GroupId $request.PrincipalId -ErrorAction SilentlyContinue + $PrincipalInstance = Get-MGGroup -GroupId $schedule.PrincipalId -ErrorAction SilentlyContinue $PrincipalTypeValue = 'Group' } @@ -201,47 +202,47 @@ { return $nullResult } - $RoleDefinitionValue = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $request.RoleDefinitionId + $RoleDefinitionValue = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $schedule.RoleDefinitionId $ScheduleInfoValue = @{} - if ($null -ne $request.ScheduleInfo.Expiration) + if ($null -ne $schedule.ScheduleInfo.Expiration) { $expirationValue = @{ - duration = $request.ScheduleInfo.Expiration.Duration - type = $request.ScheduleInfo.Expiration.Type + duration = $schedule.ScheduleInfo.Expiration.Duration + type = $schedule.ScheduleInfo.Expiration.Type } - if ($null -ne $request.ScheduleInfo.Expiration.EndDateTime) + if ($null -ne $schedule.ScheduleInfo.Expiration.EndDateTime) { - $expirationValue.Add('endDateTime', $request.ScheduleInfo.Expiration.EndDateTime.ToString("yyyy-MM-ddThh:mm:ssZ")) + $expirationValue.Add('endDateTime', $schedule.ScheduleInfo.Expiration.EndDateTime.ToString("yyyy-MM-ddThh:mm:ssZ")) } $ScheduleInfoValue.Add('expiration', $expirationValue) } - if ($null -ne $request.ScheduleInfo.Recurrence) + if ($null -ne $schedule.ScheduleInfo.Recurrence) { $recurrenceValue = @{ pattern = @{ - dayOfMonth = $request.ScheduleInfo.Recurrence.Pattern.dayOfMonth - daysOfWeek = $request.ScheduleInfo.Recurrence.Pattern.daysOfWeek - firstDayOfWeek = $request.ScheduleInfo.Recurrence.Pattern.firstDayOfWeek - index = $request.ScheduleInfo.Recurrence.Pattern.index - interval = $request.ScheduleInfo.Recurrence.Pattern.interval - month = $request.ScheduleInfo.Recurrence.Pattern.month - type = $request.ScheduleInfo.Recurrence.Pattern.type + dayOfMonth = $schedule.ScheduleInfo.Recurrence.Pattern.dayOfMonth + daysOfWeek = $schedule.ScheduleInfo.Recurrence.Pattern.daysOfWeek + firstDayOfWeek = $schedule.ScheduleInfo.Recurrence.Pattern.firstDayOfWeek + index = $schedule.ScheduleInfo.Recurrence.Pattern.index + interval = $schedule.ScheduleInfo.Recurrence.Pattern.interval + month = $schedule.ScheduleInfo.Recurrence.Pattern.month + type = $schedule.ScheduleInfo.Recurrence.Pattern.type } range = @{ - endDate = $request.ScheduleInfo.Recurrence.Range.endDate - numberOfOccurrences = $request.ScheduleInfo.Recurrence.Range.numberOfOccurrences - recurrenceTimeZone = $request.ScheduleInfo.Recurrence.Range.recurrenceTimeZone - startDate = $request.ScheduleInfo.Recurrence.Range.startDate - type = $request.ScheduleInfo.Recurrence.Range.type + endDate = $schedule.ScheduleInfo.Recurrence.Range.endDate + numberOfOccurrences = $schedule.ScheduleInfo.Recurrence.Range.numberOfOccurrences + recurrenceTimeZone = $schedule.ScheduleInfo.Recurrence.Range.recurrenceTimeZone + startDate = $schedule.ScheduleInfo.Recurrence.Range.startDate + type = $schedule.ScheduleInfo.Recurrence.Range.type } } $ScheduleInfoValue.Add('Recurrence', $recurrenceValue) } - if ($null -ne $request.ScheduleInfo.StartDateTime) + if ($null -ne $schedule.ScheduleInfo.StartDateTime) { - $ScheduleInfoValue.Add('StartDateTime', $request.ScheduleInfo.StartDateTime.ToString("yyyy-MM-ddThh:mm:ssZ")) + $ScheduleInfoValue.Add('StartDateTime', $schedule.ScheduleInfo.StartDateTime.ToString("yyyy-MM-ddThh:mm:ssZ")) } $ticketInfoValue = $null From 7623067713c67b4a75ce4287dbe6314a84f8da55 Mon Sep 17 00:00:00 2001 From: Philippe Kernevez Date: Mon, 30 Oct 2023 12:08:40 +0100 Subject: [PATCH 08/16] Use sub call to retreive Applications --- CHANGELOG.md | 6 +++ .../MSFT_TeamsComplianceRecordingPolicy.psm1 | 11 +++- ...C.TeamsComplianceRecordingPolicy.Tests.ps1 | 52 +++++++++++++++---- Tests/Unit/Stubs/Microsoft365.psm1 | 13 +++++ 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93a773633d..41e5d95300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change log for Microsoft365DSC +# UNRELEASED + +* TeamsComplianceRecordingPolicy + * Fix an issue where the Compliance Application ID wasn't properly retrieved. + FIXES [#3848](https://github.com/microsoft/Microsoft365DSC/issues/3848) + # 1.23.1025.1 * AADEntitlementManagementAccessPackageAssignmentPolicy diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_TeamsComplianceRecordingPolicy/MSFT_TeamsComplianceRecordingPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_TeamsComplianceRecordingPolicy/MSFT_TeamsComplianceRecordingPolicy.psm1 index feffced474..14e0c8c493 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_TeamsComplianceRecordingPolicy/MSFT_TeamsComplianceRecordingPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_TeamsComplianceRecordingPolicy/MSFT_TeamsComplianceRecordingPolicy.psm1 @@ -74,11 +74,20 @@ function Get-TargetResource { return $nullResult } + $recordingApplications = [Array](Get-CsTeamsComplianceRecordingApplication -Filter "$($instance.Identity)/*") + if ($null -eq $recordingApplications) + { + $recordingApplications = @() + } + $recordApplicationIds = @() + foreach ($app in $recordingApplications) { + $recordApplicationIds += @{Id=$app.Id} + } Write-Verbose -Message "Found an instance with Identity {$Identity}" $results = @{ Identity = $instance.Identity - ComplianceRecordingApplications = [Array]$instance.ComplianceRecordingApplications.Id + ComplianceRecordingApplications = $recordApplicationIds Description = $instance.Description DisableComplianceRecordingAudioNotificationForCalls = $instance.DisableComplianceRecordingAudioNotificationForCalls Enabled = $instance.Enabled diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.TeamsComplianceRecordingPolicy.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.TeamsComplianceRecordingPolicy.Tests.ps1 index a0d97fe0c9..6ea4b41bd0 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.TeamsComplianceRecordingPolicy.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.TeamsComplianceRecordingPolicy.Tests.ps1 @@ -63,7 +63,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Description = 'FakeStringValue' Enabled = $True DisableComplianceRecordingAudioNotificationForCalls = $True - ComplianceRecordingApplications = 'FakeStringValue' + ComplianceRecordingApplications = @(@{Id="123456"}) Identity = 'FakeStringValue' Ensure = 'Present' Credential = $Credential @@ -95,7 +95,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Description = 'FakeStringValue' Enabled = $True DisableComplianceRecordingAudioNotificationForCalls = $True - ComplianceRecordingApplications = 'FakeStringValue' + ComplianceRecordingApplications = @(@{Id='123456'}) Identity = 'FakeStringValue' Ensure = 'Absent' Credential = $Credential @@ -107,15 +107,27 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Description = 'FakeStringValue' Enabled = $True DisableComplianceRecordingAudioNotificationForCalls = $True - ComplianceRecordingApplications = @{Id='FakeStringValue'} + ComplianceRecordingApplications = "Microsoft.Teams.Policy.Aministration.Cmdlets.Core.CompianceRecordingApplication" Identity = 'FakeStringValue' - } } + Mock -CommandName Get-CsTeamsComplianceRecordingApplication -MockWith { + return @{ + Identity = 'FakeStringValue/123456' + Id = '123456' + } + } + } It 'Should return Values from the Get method' { - (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + $Result = (Get-TargetResource @testParams) + $Result.Ensure | Should -Be 'Present' + $Result.ComplianceRecordingApplications.Length | Should -Be 1 + $Result.ComplianceRecordingApplications[0].Id | Should -Be '123456' + Should -Invoke -CommandName Get-CsTeamsComplianceRecordingPolicy -Exactly 1 + Should -Invoke -CommandName Get-CsTeamsComplianceRecordingApplication -ParameterFilter {$Filter -eq 'FakeStringValue/*'} -Exactly 1 + } It 'Should return true from the Test method' { @@ -135,7 +147,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Description = 'FakeStringValue' Enabled = $True DisableComplianceRecordingAudioNotificationForCalls = $True - ComplianceRecordingApplications = 'FakeStringValue' + ComplianceRecordingApplications = @(@{Id='123456'}) Identity = 'FakeStringValue' Ensure = 'Present' Credential = $Credential @@ -147,11 +159,17 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Description = 'FakeStringValue' Enabled = $True DisableComplianceRecordingAudioNotificationForCalls = $True - ComplianceRecordingApplications = @{Id='FakeStringValue'} + ComplianceRecordingApplications = "Microsoft.Teams.Policy.Aministration.Cmdlets.Core.CompianceRecordingApplication" Identity = 'FakeStringValue' } } + Mock -CommandName Get-CsTeamsComplianceRecordingApplication -MockWith { + return @{ + Identity = 'FakeStringValue/123456' + Id = '123456' + } + } } It 'Should return true from the Test method' { @@ -166,7 +184,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Description = 'FakeStringValue' Enabled = $True DisableComplianceRecordingAudioNotificationForCalls = $True - ComplianceRecordingApplications = 'FakeStringValue' + ComplianceRecordingApplications = @{Id='123456'} Identity = 'FakeStringValue' Ensure = 'Present' Credential = $Credential @@ -178,10 +196,17 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Description = 'FakeStringValueDrift' #Drift Enabled = $False DisableComplianceRecordingAudioNotificationForCalls = $False - ComplianceRecordingApplications = @{Id='FakeStringValueDrift'} #Drift + ComplianceRecordingApplications = "Microsoft.Teams.Policy.Aministration.Cmdlets.Core.CompianceRecordingApplication" Identity = 'FakeStringValue' } } + + Mock -CommandName Get-CsTeamsComplianceRecordingApplication -MockWith { + return @{ + Identity = 'FakeStringValue/123456Drift' + Id = '123456Drift' #Drift + } + } } It 'Should return Values from the Get method' { @@ -212,11 +237,18 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Description = 'FakeStringValue' Enabled = $True DisableComplianceRecordingAudioNotificationForCalls = $True - ComplianceRecordingApplications = @{Id='FakeStringValue'} + ComplianceRecordingApplications = "Microsoft.Teams.Policy.Aministration.Cmdlets.Core.CompianceRecordingApplication" Identity = 'FakeStringValue' } } + Mock -CommandName Get-CsTeamsComplianceRecordingApplication -MockWith { + return @{ + Identity = 'FakeStringValue/123456' + Id = '123456' + } + } + } It 'Should Reverse Engineer resource from the Export method' { diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 6c9faaecea..db2d079c8b 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -70486,6 +70486,19 @@ function Get-CsTeamsComplianceRecordingPolicy $Identity ) } +function Get-CsTeamsComplianceRecordingApplication +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Filter, + + [Parameter()] + [System.String] + $Identity + ) +} function Get-CsTeamsEmergencyCallingPolicy { [CmdletBinding()] From f927989ae877d783ac5344c433b9921122077567 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 30 Oct 2023 10:48:59 -0400 Subject: [PATCH 09/16] Update MSFT_AADRoleEligibilityScheduleRequest.psm1 --- ...SFT_AADRoleEligibilityScheduleRequest.psm1 | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 index f7a9a96153..f662a15551 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 @@ -181,6 +181,11 @@ $request = Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -Filter "PrincipalId eq '$PrincipalId' and RoleDefinitionId eq '$RoleDefinitionId'" } } + else + { + $RoleDefinitionId = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id + $schedule = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -Filter "PrincipalId eq '$($request.PrincipalId)' and RoleDefinitionId eq '$RoleDefinitionId'" + } if ($null -eq $schedule) { return $nullResult @@ -189,12 +194,12 @@ Write-Verbose -Message "Found existing AADRolelLigibilityScheduleRequest" if ($PrincipalType -eq 'User') { - $PrincipalInstance = Get-MgUser -UserId $schedule.PrincipalId -ErrorAction SilentlyContinue + $PrincipalInstance = Get-MgUser -UserId $request.PrincipalId -ErrorAction SilentlyContinue $PrincipalTypeValue = 'User' } if ($null -eq $PrincipalInstance -or $PrincipalType -eq 'Group') { - $PrincipalInstance = Get-MGGroup -GroupId $schedule.PrincipalId -ErrorAction SilentlyContinue + $PrincipalInstance = Get-MGGroup -GroupId $request.PrincipalId -ErrorAction SilentlyContinue $PrincipalTypeValue = 'Group' } @@ -202,7 +207,6 @@ { return $nullResult } - $RoleDefinitionValue = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $schedule.RoleDefinitionId $ScheduleInfoValue = @{} @@ -255,11 +259,11 @@ } $PrincipalValue = $null - if ($PrincipalTypeValue -eq 'User') + if ($PrincipalType -eq 'User') { $PrincipalValue = $PrincipalInstance.UserPrincipalName } - elseif ($PrincipalTypeValue -eq 'Group') + if ($null -eq $PrincipalValue -or $PrincipalTypeValue -eq 'Group') { $PrincipalValue = $PrincipalInstance.DisplayName } @@ -267,7 +271,7 @@ $results = @{ Principal = $PrincipalValue PrincipalType = $PrincipalTypeValue - RoleDefinition = $RoleDefinitionValue.DisplayName + RoleDefinition = $RoleDefinition DirectoryScopeId = $request.DirectoryScopeId AppScopeId = $request.AppScopeId Action = $request.Action @@ -730,10 +734,10 @@ function Export-TargetResource #region resource generator code $schedules = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -All -ErrorAction Stop [array] $Script:exportedInstances = @() - foreach ($schedule in $schedules) - { - [array] $allRequests = Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -All ` + [array] $allRequests = Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -All ` -Filter "Status ne 'Revoked'" -ErrorAction Stop + foreach ($schedule in $schedules) + { [array] $Script:exportedInstances += $allRequests | Where-Object -FilterScript {$_.TargetScheduleId -eq $schedule.Id} } #endregion @@ -752,10 +756,12 @@ function Export-TargetResource { $displayedKey = $request.Id Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + + $RoleDefinitionId = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $request.RoleDefinitionId $params = @{ Id = $request.Id Principal = $request.PrincipalId - RoleDefinition = 'TempDefinition' + RoleDefinition = $RoleDefinitionId.DisplayName ScheduleInfo = 'TempSchedule' Ensure = 'Present' Credential = $Credential From c6359a09dcdeee20b5a108405e0f05dacb61611f Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 30 Oct 2023 11:20:54 -0400 Subject: [PATCH 10/16] Fixes Unit Tests --- ...SFT_AADRoleEligibilityScheduleRequest.psm1 | 2 +- ...ADRoleEligibilityScheduleRequest.Tests.ps1 | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 index f662a15551..2e4b3c31ac 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 @@ -186,7 +186,7 @@ $RoleDefinitionId = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id $schedule = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -Filter "PrincipalId eq '$($request.PrincipalId)' and RoleDefinitionId eq '$RoleDefinitionId'" } - if ($null -eq $schedule) + if ($null -eq $schedule -or $null -eq $request) { return $nullResult } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 index a3330e6392..6f7082cd94 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 @@ -25,6 +25,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { $secpasswd = ConvertTo-SecureString 'test@password1' -AsPlainText -Force $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) $Script:exportedInstances = $null + $Script:ExportMode = $null Mock -CommandName Add-M365DSCTelemetryEvent -MockWith { } @@ -159,8 +160,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { RoleDefinition = "Teams Communications Administrator"; ScheduleInfo = New-CimInstance -ClassName MSFT_AADRoleEligibilityScheduleRequestSchedule -Property @{ - expiration = New-CimInstance -ClassName MSFT_AADRoleEligibilityScheduleRequestScheduleExpiration -Property @{ - + expiration = New-CimInstance -ClassName MSFT_AADRoleEligibilityScheduleRequestScheduleExpiration -Property @{ type = 'afterDateTime' } -ClientOnly } -ClientOnly @@ -182,6 +182,21 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { }; } } + Mock -CommandName Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -MockWith { + return @{ + Action = "AdminAssign"; + Id = '12345-12345-12345-12345-12345' + DirectoryScopeId = "/"; + IsValidationOnly = $False; + PrincipalId = "123456"; + RoleDefinitionId = "12345"; + ScheduleInfo = @{ + expiration = @{ + type = 'afterDateTime' + } + }; + } + } } It 'Should return Values from the Get method' { From b05cd2a629e5cdf191ce595a2d8fa5e7a9140468 Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Tue, 31 Oct 2023 09:06:40 +0100 Subject: [PATCH 11/16] Fix IntuneDeviceCleanupRule example --- .../Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 b/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 index e309830206..5299c98ef4 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceCleanupRule/1-SetDeviceCleanupRule.ps1 @@ -16,6 +16,7 @@ Configuration Example IntuneDeviceCleanupRule 'Example' { Enabled = $true + IsSingleInstance = 'Yes' DeviceInactivityBeforeRetirementInDays = 30 Ensure = 'Present' Credential = $credsGlobalAdmin From 11ce9f3c5b1bdd8b90c9267c15e18388037e5d51 Mon Sep 17 00:00:00 2001 From: Philippe Kernevez Date: Tue, 31 Oct 2023 17:32:10 +0100 Subject: [PATCH 12/16] Region could be empty --- CHANGELOG.md | 3 +++ .../MSFT_SCSecurityFilter/MSFT_SCSecurityFilter.psm1 | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba3b82a9c..d0edad6b5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * AADRoleEligibilityScheduleRequest * Fixes how the Get method retrieves existing instances for Groups. FIXES [#3787](https://github.com/microsoft/Microsoft365DSC/issues/3787) +* SCSecurityFilter + * Fixes an issue because Region could be empty + FIXES: [#3854](https://github.com/microsoft/Microsoft365DSC/issues/3854) # 1.23.1025.1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCSecurityFilter/MSFT_SCSecurityFilter.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCSecurityFilter/MSFT_SCSecurityFilter.psm1 index 595704ada4..1e87cf5340 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCSecurityFilter/MSFT_SCSecurityFilter.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCSecurityFilter/MSFT_SCSecurityFilter.psm1 @@ -37,13 +37,13 @@ function Get-TargetResource 'IND', # India 'JPN', # Japan 'LAM', # Latin America - 'NAM' # North America + 'NAM', # North America + '' # NOT MANDATORY )] [System.String] $Region, # And the DSC ones - [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -227,7 +227,8 @@ function Set-TargetResource 'IND', # India 'JPN', # Japan 'LAM', # Latin America - 'NAM' # North America + 'NAM', # North America + '' # NOT MANDATORY )] [System.String] $Region, @@ -393,7 +394,8 @@ function Test-TargetResource 'IND', # India 'JPN', # Japan 'LAM', # Latin America - 'NAM' # North America + 'NAM', # North America + '' # NOT MANDATORY )] [System.String] $Region, From d1b6bf9a37dc2facbcc8b7b322e61dcd59b48c70 Mon Sep 17 00:00:00 2001 From: Jonas Cassier <61315302+JonasCassier@users.noreply.github.com> Date: Wed, 1 Nov 2023 09:52:27 +0100 Subject: [PATCH 13/16] Fixes parameter validation of ExternalUserExpireInDays and ExternalUserExpirationRequired. --- .../MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 index 3511c3159f..f56913aaa1 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SPOSharingSettings/MSFT_SPOSharingSettings.psm1 @@ -447,9 +447,9 @@ function Set-TargetResource Write-Warning -Message 'The sharing capabilities for the tenant are not configured to be ExternalUserAndGuestSharing for that the RequireAnonymousLinksExpireInDays property cannot be configured' $CurrentParameters.Remove('RequireAnonymousLinksExpireInDays') | Out-Null } - if ($SharingCapability -ne 'ExternalUserExpirationRequired') + if ($ExternalUserExpireInDays -and $ExternalUserExpirationRequired -eq $false) { - Write-Warning -Message 'The sharing capabilities for the tenant are not configured to be ExternalUserExpirationRequired for that the ExternalUserExpireInDays property cannot be configured' + Write-Warning -Message 'ExternalUserExpirationRequired is set to be false. For that the ExternalUserExpireInDays property cannot be configured' $CurrentParameters.Remove('ExternalUserExpireInDays') | Out-Null } if ($RequireAcceptingAccountMatchInvitedAccount -eq $false) From 0b5762219d3ba002c7bc9f5e790c0299a3903caa Mon Sep 17 00:00:00 2001 From: Jonas Cassier <61315302+JonasCassier@users.noreply.github.com> Date: Wed, 1 Nov 2023 09:53:41 +0100 Subject: [PATCH 14/16] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba3b82a9c..576a6ed763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * AADRoleEligibilityScheduleRequest * Fixes how the Get method retrieves existing instances for Groups. FIXES [#3787](https://github.com/microsoft/Microsoft365DSC/issues/3787) +* SPOSharingSettings + * Fixes parameter validation of ExternalUserExpireInDays and ExternalUserExpirationRequired. + FIXES [#3856](https://github.com/microsoft/Microsoft365DSC/issues/3856) # 1.23.1025.1 From 8ef59b205966dc1a816b990860b6562b29379a99 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 1 Nov 2023 13:46:13 +0000 Subject: [PATCH 15/16] Updated Resources and Cmdlet documentation pages --- .../intune/IntuneDeviceCleanupRule.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 docs/docs/resources/intune/IntuneDeviceCleanupRule.md diff --git a/docs/docs/resources/intune/IntuneDeviceCleanupRule.md b/docs/docs/resources/intune/IntuneDeviceCleanupRule.md new file mode 100644 index 0000000000..9c6fe539b7 --- /dev/null +++ b/docs/docs/resources/intune/IntuneDeviceCleanupRule.md @@ -0,0 +1,78 @@ +# IntuneDeviceCleanupRule + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **IsSingleInstance** | Key | String | Only valid value is 'Yes'. | `Yes` | +| **Enabled** | Key | Boolean | Indicates whether the cleanup rule is enabled. | | +| **DeviceInactivityBeforeRetirementInDays** | Write | UInt32 | Number of days until Intune devices are deleted. Minimum: 30, Maximum: 270. | | +| **Ensure** | Write | String | Present ensures the category exists, absent ensures it is removed. | `Present`, `Absent` | +| **Credential** | Write | PSCredential | Credentials of the Intune 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. | | +| **ApplicationSecret** | Write | PSCredential | Secret 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. | | + + +## Description + +This resource configures the Intune device cleanup rule. + +## Permissions + +### Microsoft Graph + +To authenticate with the Microsoft Graph API, this resource required the following permissions: + +#### Delegated permissions + +- **Read** + + - DeviceManagementManagedDevices.Read.All + +- **Update** + + - DeviceManagementManagedDevices.ReadWrite.All + +#### Application permissions + +- **Read** + + - DeviceManagementManagedDevices.Read.All + +- **Update** + + - DeviceManagementManagedDevices.ReadWrite.All + +## Examples + +### Example 1 + +This example sets the device cleanup rule. + +```powershell +Configuration Example +{ + param( + [Parameter(Mandatory = $true)] + [PSCredential] + $credsGlobalAdmin + ) + Import-DscResource -ModuleName Microsoft365DSC + + node localhost + { + IntuneDeviceCleanupRule 'Example' + { + Enabled = $true + IsSingleInstance = 'Yes' + DeviceInactivityBeforeRetirementInDays = 30 + Ensure = 'Present' + Credential = $credsGlobalAdmin + } + } +} +``` + From be9362dbb10fd5a840ee9e0d213270d99e01ccce Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 1 Nov 2023 10:14:03 -0400 Subject: [PATCH 16/16] Release 1.23.1101.1 --- CHANGELOG.md | 2 +- Modules/Microsoft365DSC/Microsoft365DSC.psd1 | 32 ++++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de743dfec..3697a33a6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log for Microsoft365DSC -# UNRELEASED +# 1.23.1101.1 * AADRoleEligibilityScheduleRequest * Fixes how the Get method retrieves existing instances for Groups. diff --git a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 index e9001ef1e7..83383dfbf0 100644 --- a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 +++ b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 @@ -3,7 +3,7 @@ # # Generated by: Microsoft Corporation # -# Generated on: 2023-10-25 +# Generated on: 2023-11-01 @{ @@ -11,7 +11,7 @@ # RootModule = '' # Version number of this module. - ModuleVersion = '1.23.1025.1' + ModuleVersion = '1.23.1101.1' # Supported PSEditions # CompatiblePSEditions = @() @@ -140,24 +140,18 @@ IconUri = 'https://github.com/microsoft/Microsoft365DSC/blob/Dev/Modules/Microsoft365DSC/Dependencies/Images/Logo.png?raw=true' # ReleaseNotes of this module - ReleaseNotes = '* AADApplication - * Changes to how permissions drifts are logged. - FIXES [#3830](https://github.com/microsoft/Microsoft365DSC/issues/3830) - * AADAttributeSet - * Initial Release. - * AADAuthenticationContext - * Initial Release. - * AADConditionalAccessPolicy - * Adds support for Authentication Context. - FIXES [#3813](https://github.com/microsoft/Microsoft365DSC/issues/3813) - * AADSocialIdentityProvider - * Initial release. + ReleaseNotes = '* AADRoleEligibilityScheduleRequest + * Fixes how the Get method retrieves existing instances for Groups. + FIXES [#3787](https://github.com/microsoft/Microsoft365DSC/issues/3787) + * SCSecurityFilter + * Fixes an issue because Region could be empty + FIXES: [#3854](https://github.com/microsoft/Microsoft365DSC/issues/3854) + * SPOSharingSettings + * Fixes parameter validation of ExternalUserExpireInDays and ExternalUserExpirationRequired. + FIXES [#3856](https://github.com/microsoft/Microsoft365DSC/issues/3856) * TeamsComplianceRecordingPolicy - * Fixes an issue where the Compliance Application ID was not properly retrieved. - FIXES [#3712](https://github.com/microsoft/Microsoft365DSC/issues/3712) - * DEPENDENCIES - * Updated Microsoft.Graph dependencies to version 2.8.0. - * Updated MicrosoftTeams dependency to version 5.7.1.' + * Fix an issue where the Compliance Application ID was not properly retrieved. + FIXES [#3848](https://github.com/microsoft/Microsoft365DSC/issues/3848)' # Flag to indicate whether the module requires explicit user acceptance for install/update # RequireLicenseAcceptance = $false