From 6c62a502bd80f434c97ec6056680faaa4405b0a9 Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Sat, 14 Oct 2023 17:25:12 +0200 Subject: [PATCH 1/3] 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 2/3] 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 b05cd2a629e5cdf191ce595a2d8fa5e7a9140468 Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Tue, 31 Oct 2023 09:06:40 +0100 Subject: [PATCH 3/3] 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