From f079ce5a6835b38c147c7d6f745586363f5018da Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 10 Oct 2023 12:19:46 -0400 Subject: [PATCH] Fixes #3744 --- CHANGELOG.md | 6 + ...SFT_AADRoleEligibilityScheduleRequest.psm1 | 127 ++++++++++++++++-- ...DRoleEligibilityScheduleRequest.schema.mof | 1 + .../Dependencies/Manifest.psd1 | 34 ++--- ...ADRoleEligibilityScheduleRequest.Tests.ps1 | 8 +- 5 files changed, 145 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89e90e73cc..cee6d2da81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,14 @@ # UNRELEASED +* AADRoleEligibilityScheduleRequest + * Added support for groups assignment. + FIXES [#3744](https://github.com/microsoft/Microsoft365DSC/issues/3744) + * EXODistributionGroup * Fixes the export of group membership to use Identity. +* DEPENDENCIES + * Updated Micrsoft.Graph dependencies to version 2.7.0. # 1.23.1004.1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 index 06c671a95c..873eddbbce 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 @@ -12,6 +12,11 @@ [System.String] $RoleDefinition, + [Parameter()] + [ValidateSet('User', 'Group')] + [System.String] + $PrincipalType = 'User', + [Parameter()] [System.String] $Id, @@ -120,7 +125,25 @@ if ($null -ne $Script:exportedInstances -and $Script:ExportMode) { Write-Verbose -Message "Getting Role Eligibility by PrincipalId and RoleDefinitionId" - $PrincipalId = (Get-MgUser -Filter "UserPrincipalName eq '$Principal'").Id + if ($PrincipalType -eq 'User') + { + $PrincipalIdValue = Get-MgUser -Filter "UserPrincipalName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalTypeValue = 'User' + } + if ($null -eq $PrincipalIdValue -or $PrincipalType -eq 'Group') + { + $PrincipalIdValue = Get-MgGroup -Filter "DisplayName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalTypeValue = 'Group' + } + + if ($null -ne $PrincipalIdValue) + { + $PrincipalId = $PrincipalIdValue.Id + } + else + { + return $nullResult + } Write-Verbose -Message "Found Principal {$PrincipalId}" $RoleDefinitionId = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id $request = $Script:exportedInstances | Where-Object -FilterScript {$_.PrincipalId -eq $PrincipalId -and $_.RoleDefinitionId -eq $RoleDefinition} @@ -128,7 +151,28 @@ else { Write-Verbose -Message "Getting Role Eligibility by PrincipalId and RoleDefinitionId" - $PrincipalId = (Get-MgUser -Filter "UserPrincipalName eq '$Principal'").Id + if ($PrincipalType -eq 'User') + { + Write-Verbose -Message "Retrieving principal {$Principal} of type {$PrincipalType}" + $PrincipalIdValue = Get-MgUser -Filter "UserPrincipalName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalTypeValue = 'User' + } + + if ($null -eq $PrincipalIdValue -or $PrincipalType -eq 'Group') + { + Write-Verbose -Message "Retrieving principal {$Principal} of type {$PrincipalType}" + $PrincipalIdValue = Get-MgGroup -Filter "DisplayName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalTypeValue = 'Group' + } + + if ($null -ne $PrincipalIdValue) + { + $PrincipalId = $PrincipalIdValue.Id + } + else + { + return $nullResult + } Write-Verbose -Message "Found Principal {$PrincipalId}" $RoleDefinitionId = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id Write-Verbose -Message "Found Role {$RoleDefinitionId}" @@ -142,7 +186,21 @@ } Write-Verbose -Message "Found existing AADRolelLigibilityScheduleRequest" - $PrincipalValue = Get-MgUser -UserId $request.PrincipalId + if ($PrincipalType -eq 'User') + { + $PrincipalInstance = Get-MgUser -UserId $request.PrincipalId -ErrorAction SilentlyContinue + $PrincipalTypeValue = 'User' + } + if ($null -eq $PrincipalInstance -or $PrincipalType -eq 'Group') + { + $PrincipalInstance = Get-MGGroup -GroupId $request.PrincipalId -ErrorAction SilentlyContinue + $PrincipalTypeValue = 'Group' + } + + if ($null -eq $PrincipalInstance) + { + return $nullResult + } $RoleDefinitionValue = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $request.RoleDefinitionId $ScheduleInfoValue = @{} @@ -195,8 +253,19 @@ } } + $PrincipalValue = $null + if ($PrincipalTypeValue -eq 'User') + { + $PrincipalValue = $PrincipalInstance.UserPrincipalName + } + elseif ($PrincipalTypeValue -eq 'Group') + { + $PrincipalValue = $PrincipalInstance.DisplayName + } + $results = @{ - Principal = $PrincipalValue.UserPrincipalName + Principal = $PrincipalValue + PrincipalType = $PrincipalTypeValue RoleDefinition = $RoleDefinitionValue.DisplayName DirectoryScopeId = $request.DirectoryScopeId AppScopeId = $request.AppScopeId @@ -242,6 +311,11 @@ function Set-TargetResource [System.String] $RoleDefinition, + [Parameter()] + [ValidateSet('User', 'Group')] + [System.String] + $PrincipalType = 'User', + [Parameter()] [System.String] $Id, @@ -339,8 +413,24 @@ function Set-TargetResource $ParametersOps = ([Hashtable]$PSBoundParameters).clone() - $PrincipalIdValue = (Get-MgUser -Filter "UserPrincipalName eq '$Principal'").Id - $ParametersOps.Add("PrincipalId", $PrincipalIdValue) + if ($PrincipalType -eq 'User') + { + [Array]$PrincipalIdValue = (Get-MgUser -Filter "UserPrincipalName eq '$Principal'").Id + } + elseif ($PrincipalType -eq 'Group') + { + [Array]$PrincipalIdValue = (Get-MgGroup -Filter "DisplayName eq '$Principal'").Id + } + + if ($null -eq $PrincipalIdValue) + { + throw "Couldn't find Principal {$PrincipalId} of type {$PrincipalType}" + } + elseif ($PrincipalIdValue.Length -gt 1) + { + throw "Multiple Principal with ID {$PrincipalId} of type {$PrincipalType} were found. Cannot create schedule." + } + $ParametersOps.Add("PrincipalId", $PrincipalIdValue[0]) $ParametersOps.Remove("Principal") | Out-Null $RoleDefinitionIdValue = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id @@ -408,24 +498,24 @@ function Set-TargetResource Write-Verbose -Message "ScheduleInfo: $(Convert-M365DscHashtableToString -Hashtable $ScheduleInfoValue)" $ParametersOps.ScheduleInfo = $ScheduleInfoValue } - + $ParametersOps.Remove("PrincipalType") | Out-Null if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { - Write-Verbose -Message "Creating an Azure AD Role Eligibility Schedule Request for user {$Principal} and role {$RoleDefinition}" + Write-Verbose -Message "Creating a Role Eligibility Schedule Request for user {$Principal} and role {$RoleDefinition}" $ParametersOps.Remove("Id") | Out-Null - + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $ParametersOps)" New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @ParametersOps } elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { - Write-Verbose -Message "Updating the Azure AD Role Eligibility Schedule Request for user {$Principal} and role {$RoleDefinition}" + Write-Verbose -Message "Updating the Role Eligibility Schedule Request for user {$Principal} and role {$RoleDefinition}" $ParametersOps.Remove("Id") | Out-Null $ParametersOps.Action = 'AdminUpdate' New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @ParametersOps } elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { - Write-Verbose -Message "Removing the Azure AD Role Eligibility Schedule Request for user {$Principal} and role {$RoleDefinition}" + Write-Verbose -Message "Removing the Role Eligibility Schedule Request for user {$Principal} and role {$RoleDefinition}" $ParametersOps.Remove("Id") | Out-Null $ParametersOps.Action = 'AdminRemove' New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @ParametersOps @@ -446,6 +536,11 @@ function Test-TargetResource [System.String] $RoleDefinition, + [Parameter()] + [ValidateSet('User', 'Group')] + [System.String] + $PrincipalType = 'User', + [Parameter()] [System.String] $Id, @@ -632,8 +727,14 @@ function Export-TargetResource { $Script:ExportMode = $true #region resource generator code - [array] $Script:exportedInstances = Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -All ` - -Filter "Status ne 'Revoked'" -ErrorAction Stop + $schedules = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -All -ErrorAction Stop + [array] $Script:exportedInstances = @() + foreach ($schedule in $schedules) + { + [array] $allRequests = Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -All ` + -Filter "Status ne 'Revoked'" -ErrorAction Stop + [array] $Script:exportedInstances += $allRequests | Where-Object -FilterScript {$_.TargetScheduleId -eq $schedule.Id} + } #endregion $i = 1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.schema.mof index 5c235fe044..be1c792e30 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.schema.mof @@ -55,6 +55,7 @@ class MSFT_AADRoleEligibilityScheduleRequest : OMI_BaseResource { [Key, Description("User Principal Name of the eligibility request.")] String Principal; [Key, Description("Role associated with the eligibility request.")] String RoleDefinition; + [Write, Description("Represented the type of principal to assign the request to. Accepted values are: Group and User."), ValueMap{"Group","User"}, Values{"Group","User"}] String PrincipalType; [Write, Description("Identifier of the directory object representing the scope of the role eligibility. The scope of an role eligibility determines the set of resources for which the principal has been granted access. Directory scopes are shared scopes stored in the directory that are understood by multiple applications. Use / for tenant-wide scope. Use appScopeId to limit the scope to an application only. Either directoryScopeId or appScopeId is required.")] String DirectoryScopeId; [Write, Description("Identifier for the Role Eligibility Schedule Request.")] String Id; [Write, Description("Identifier of the app-specific scope when the role eligibility is scoped to an app. The scope of a role eligibility determines the set of resources for which the principal is eligible to access. App scopes are scopes that are defined and understood by this application only. Use / for tenant-wide app scopes. Use directoryScopeId to limit the scope to particular directory objects, for example, administrative units. Either directoryScopeId or appScopeId is required.")] String AppScopeId; diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index e18fb34ca4..94a2b82257 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -10,71 +10,71 @@ }, @{ ModuleName = 'Microsoft.Graph.Applications' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Authentication' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Devices.CorporateManagement' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Administration' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Enrollment' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.DirectoryManagement' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.Governance' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.SignIns' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Reports' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Teams' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.DeviceManagement.Administration' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DirectoryObjects' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Groups' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Planner' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Users' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.Graph.Users.Actions' - RequiredVersion = '2.6.1' + RequiredVersion = '2.7.0' }, @{ ModuleName = 'Microsoft.PowerApps.Administration.PowerShell' diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 index 7705e80f38..a3330e6392 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 @@ -24,7 +24,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { $Global:CurrentModeIsExport = $false $secpasswd = ConvertTo-SecureString 'test@password1' -AsPlainText -Force $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) - + $Script:exportedInstances = $null Mock -CommandName Add-M365DSCTelemetryEvent -MockWith { } @@ -51,6 +51,11 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Id = '12345' } } + Mock -CommandName Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -MockWith { + return @{ + Id = '12345-12345-12345-12345-12345' + } + } # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { @@ -262,6 +267,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { type = 'afterDateTime' } }; + TargetScheduleId = "12345-12345-12345-12345-12345" } } }