diff --git a/.github/workflows/Scheduled - Generate Permission List.yml b/.github/workflows/Scheduled - Generate Permission List.yml deleted file mode 100644 index 55965360ac..0000000000 --- a/.github/workflows/Scheduled - Generate Permission List.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Generate Permissions List -on: - push: - branches: - - Master - - Dev - schedule: - - cron: "0 0 * * *" - -jobs: - # This workflow contains a single job called "build" - GeneratePermissionsList: - # The type of runner that the job will run on - runs-on: windows-latest - - permissions: write-all - - # Only when run from the main repo - if: github.repository == 'microsoft/Microsoft365DSC' - # Steps represent a sequence of tasks that will be executed as part of the job - - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - name: Checkout Repository - uses: actions/checkout@v3 - - - name: Install Dependencies - shell: powershell - run: | - Import-Module './Modules/Microsoft365DSC/Microsoft365DSC.psd1' -Force; - Import-Module './Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1' -Force; - Update-M365DSCModule - - name: Get Permissions List - shell: powershell - run: | - Import-Module './Tests/TestHarness.psm1' -Force; - $permissions = Get-M365DSCAllGraphPermissionsList - $permissions -join ',' | Out-File '.\Tests\QA\Graph.PermissionList.txt' - - name: Commit Permissions List - shell: powershell - run: | - git config --local user.email "nicharl@microsoft.com" - git config --local user.name "NikCharlebois" - git add D:/a/Microsoft365DSC/Microsoft365DSC/Tests/QA/* - git pull - git commit -m "Updated Graph Permissions List" - git push - $SHA = git rev-parse HEAD - echo "commitid=$SHA" >> $env:GITHUB_OUTPUT diff --git a/CHANGELOG.md b/CHANGELOG.md index fc43269dc1..a977310eae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ * Initial release. * AADEntitlementManagementSettings * Added support for ApplicationSecret +* AADLifecycleWorkflowSettings + * Initial release. * ADOPermissionGroupSettings * Initial release. * EXOMigrationEndpoint diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/settings.json index 8394b8c68b..aa6b7ca3b9 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADConnectorGroupApplicationProxy/settings.json @@ -6,7 +6,7 @@ "delegated": { "read": [ { - "name": "Directory.ReadWrite.All" + "name": "Directory.Read.All" } ], "update": [ @@ -18,7 +18,7 @@ "application": { "read": [ { - "name": "Directory.ReadWrite.All" + "name": "Directory.Read.All" } ], "update": [ diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/MSFT_AADLifecycleWorkflowSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/MSFT_AADLifecycleWorkflowSettings.psm1 new file mode 100644 index 0000000000..94f4ab10ca --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/MSFT_AADLifecycleWorkflowSettings.psm1 @@ -0,0 +1,368 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $IsSingleInstance, + + [Parameter()] + [System.UInt32] + $WorkflowScheduleIntervalInHours, + + [Parameter()] + [System.String] + $SenderDomain, + + [Parameter()] + [System.Boolean] + $UseCompanyBranding, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters | Out-Null + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $nullResult = $PSBoundParameters + try + { + $instance = Get-MgBetaIdentityGovernanceLifecycleWorkflowSetting -ErrorAction SilentlyContinue + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + IsSingleInstance = 'Yes' + WorkflowScheduleIntervalInHours = $instance.WorkflowScheduleIntervalInHours + SenderDomain = $instance.EmailSettings.SenderDomain + UseCompanyBranding = $instance.EmailSettings.UseCompanyBranding + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + return [System.Collections.Hashtable] $results + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $IsSingleInstance, + + [Parameter()] + [System.UInt32] + $WorkflowScheduleIntervalInHours, + + [Parameter()] + [System.String] + $SenderDomain, + + [Parameter()] + [System.Boolean] + $UseCompanyBranding, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $updateSettings = @{ + WorkflowScheduleIntervalInHours = $WorkflowScheduleIntervalInHours + EmailSettings = @{ + SenderDomain = $SenderDomain + UseCompanyBranding = $UseCompanyBranding + } + } + Write-Verbose -Message "Updating the lifecycle workflow settings with payload: $payload" + Update-MgBetaIdentityGovernanceLifecycleWorkflowSetting @updateSettings +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $IsSingleInstance, + + [Parameter()] + [System.UInt32] + $WorkflowScheduleIntervalInHours, + + [Parameter()] + [System.String] + $SenderDomain, + + [Parameter()] + [System.Boolean] + $UseCompanyBranding, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + Write-Verbose -Message "Test-TargetResource returned $testResult" + + return $testResult +} + +function Export-TargetResource +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + try + { + $Script:ExportMode = $true + [array] $Script:exportedInstances = Get-MgBetaIdentityGovernanceLifecycleWorkflowSetting -ErrorAction Stop + + $i = 1 + $dscContent = '' + if ($Script:exportedInstances.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + foreach ($config in $Script:exportedInstances) + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + + $displayedKey = $config.Id + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + $params = @{ + IsSingleInstance = 'Yes' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + $dscContent += $currentDSCBlock + Save-M365DSCPartialExport -Content $currentDSCBlock ` + -FileName $Global:PartialExportFileName + $i++ + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + return $dscContent + } + catch + { + Write-Host $Global:M365DSCEmojiRedX + + New-M365DSCLogEntry -Message 'Error during Export:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return '' + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/MSFT_AADLifecycleWorkflowSettings.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/MSFT_AADLifecycleWorkflowSettings.schema.mof new file mode 100644 index 0000000000..aade973744 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/MSFT_AADLifecycleWorkflowSettings.schema.mof @@ -0,0 +1,15 @@ +[ClassVersion("1.0.0.0"), FriendlyName("AADLifecycleWorkflowSettings")] +class MSFT_AADLifecycleWorkflowSettings : OMI_BaseResource +{ + [Key, Description("Only valid value is 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance; + [Write, Description("Specifies the domain that should be used when sending email notifications. This domain must be verified in order to be used. We recommend that you use a domain that has the appropriate DNS records to facilitate email validation, like SPF, DKIM, DMARC, and MX, because this then complies with the RFC compliance for sending and receiving email. For details, see Learn more about Exchange Online Email Routing.")] String SenderDomain; + [Write, Description("The interval in hours at which all workflows running in the tenant should be scheduled for execution. This interval has a minimum value of 1 and a maximum value of 24. The default value is 3 hours.")] UInt32 WorkflowScheduleIntervalInHours; + [Write, Description("Specifies if the organization's banner logo should be included in email notifications. The banner logo will replace the Microsoft logo at the top of the email notification. If true the banner logo will be taken from the tenant's branding settings. This value can only be set to true if the organizationalBranding bannerLogo property is set.")] Boolean UseCompanyBranding; + + [Write, Description("Credentials of the workload's Admin"), EmbeddedInstance("MSFT_Credential")] string Credential; + [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; + [Write, Description("Id of the Azure Active Directory tenant used for authentication.")] String TenantId; + [Write, Description("Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication.")] String CertificateThumbprint; + [Write, Description("Managed ID being used for authentication.")] Boolean ManagedIdentity; + [Write, Description("Access token used for authentication.")] String AccessTokens[]; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/readme.md new file mode 100644 index 0000000000..747af821a1 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/readme.md @@ -0,0 +1,6 @@ + +# AADLifecycleWorkflowSettings + +## Description + +Update the properties of a lifecycleManagementSettings object. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/settings.json new file mode 100644 index 0000000000..ec4851eb0b --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADLifecycleWorkflowSettings/settings.json @@ -0,0 +1,28 @@ +{ + "resourceName": "AADLifecycleWorkflowSettings", + "description": "Update the properties of a lifecycleManagementSettings object.", + "roles": { + "read": [], + "update": [] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [ + { + "name": "LifecycleWorkflows.Read.All" + } + ], + "update": [ + { + "name": "LifecycleWorkflows.ReadWrite.All" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADConnectorGroupApplicationProxy/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADConnectorGroupApplicationProxy/3-Remove.ps1 index 79c6eefb37..4e38370dc0 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/AADConnectorGroupApplicationProxy/3-Remove.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/AADConnectorGroupApplicationProxy/3-Remove.ps1 @@ -28,6 +28,7 @@ Configuration Example TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint Ensure = "Absent"; + Name = "testgroup-new"; Id = "4984dcf7-d9e9-4663-90b4-5db09f92a669"; } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADLifecycleWorkflowSettings/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADLifecycleWorkflowSettings/2-Update.ps1 new file mode 100644 index 0000000000..b6db1874a8 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADLifecycleWorkflowSettings/2-Update.ps1 @@ -0,0 +1,35 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + AADLifecycleWorkflowSettings "AADLifecycleWorkflowSettings" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + IsSingleInstance = "Yes"; + SenderDomain = "microsoft.com"; + TenantId = $TenantId; + UseCompanyBranding = $True; + WorkflowScheduleIntervalInHours = 10; + } + } +} diff --git a/Tests/QA/Graph.PermissionList.txt b/Tests/QA/Graph.PermissionList.txt index 826113f235..9afee7e857 100644 Binary files a/Tests/QA/Graph.PermissionList.txt and b/Tests/QA/Graph.PermissionList.txt differ diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADLifecycleWorkflowSettings.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADLifecycleWorkflowSettings.Tests.ps1 new file mode 100644 index 0000000000..0581150cca --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADLifecycleWorkflowSettings.Tests.ps1 @@ -0,0 +1,113 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath '..\..\Unit' ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Microsoft365.psm1' ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Generic.psm1' ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\UnitTestHelper.psm1' ` + -Resolve) + +$CurrentScriptPath = $PSCommandPath.Split('\') +$CurrentScriptName = $CurrentScriptPath[$CurrentScriptPath.Length -1] +$ResourceName = $CurrentScriptName.Split('.')[1] +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource $ResourceName -GenericStubModule $GenericStubPath + +Describe -Name $Global:DscHelper.DescribeHeader -Fixture { + InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { + Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope + BeforeAll { + + $secpasswd = ConvertTo-SecureString (New-Guid | Out-String) -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credentials" + } + + Mock -CommandName Update-MgBetaIdentityGovernanceLifecycleWorkflowSetting -MockWith { + } + + Mock -CommandName Get-MgBetaIdentityGovernanceLifecycleWorkflowSetting -MockWith { + return @{ + EmailSettings = @{ + SenderDomain = 'contoso.com' + UseCompanyBranding = $True; + } + WorkflowScheduleIntervalInHours = 10; + + } + } + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + $Script:exportedInstances =$null + $Script:ExportMode = $false + } + # Test contexts + + Context -Name "The instance exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = "Yes"; + SenderDomain = "contoso.com"; + UseCompanyBranding = $True; + WorkflowScheduleIntervalInHours = 10; + Credential = $Credential; + } + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "The instance exists and values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = "Yes"; + SenderDomain = "contoso.com"; + UseCompanyBranding = $True; + WorkflowScheduleIntervalInHours = 11; # Drift + Credential = $Credential; + } + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Update-MgBetaIdentityGovernanceLifecycleWorkflowSetting -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 90905c17b8..9f2c2cb47c 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -94293,6 +94293,26 @@ function Update-MgBetaDeviceManagementWindowsQualityUpdateProfile #endregion +function Update-MgBetaIdentityGovernanceLifecycleWorkflowSetting +{ + [CmdletBinding()] + param( + [Parameter()] + [System.Object] + $EmailSettings, + + [Parameter()] + [System.UInt32] + $WorkflowScheduleIntervalInHours + ) +} + +function Get-MgBetaIdentityGovernanceLifecycleWorkflowSetting +{ + [CmdletBinding()] + param() +} + #region MgBetaDeviceManagementWindowsQualityUpdateProfileAssignment function Get-MgBetaDeviceManagementWindowsQualityUpdateProfileAssignment {