From c0169d2d22a37d54cdd86e48f54dadda415d8b4d Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Fri, 10 Jan 2025 14:24:02 -0500 Subject: [PATCH 1/4] Initial --- .../MSFT_AADRoleEligibilitySchedule.psm1 | 688 ++++++++++++++++++ ...MSFT_AADRoleEligibilitySchedule.schema.mof | 67 ++ .../MSFT_AADRoleEligibilitySchedule/readme.md | 6 + .../settings.json | 32 + .../AADRoleEligibilitySchedule/1-Create.ps1 | 26 + .../AADRoleEligibilitySchedule/2-Update.ps1 | 26 + .../AADRoleEligibilitySchedule/3-Remove.ps1 | 26 + ...65DSC.AADRoleEligibilitySchedule.Tests.ps1 | 178 +++++ 8 files changed, 1049 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilitySchedule.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.psm1 new file mode 100644 index 0000000000..647c13d327 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.psm1 @@ -0,0 +1,688 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Principal, + + [Parameter(Mandatory = $true)] + [System.String] + $RoleDefinition, + + [Parameter(Mandatory = $true)] + [ValidateSet('User', 'Group', 'ServicePrincipal')] + [System.String] + $PrincipalType, + + [Parameter()] + [System.String] + $Id, + + [Parameter(Mandatory = $true)] + [System.String] + $DirectoryScopeId, + + [Parameter()] + [System.String] + $AppScopeId, + + [Parameter()] + [ValidateSet('adminAssign', 'adminUpdate', 'adminRemove', 'selfActivate', 'selfDeactivate', 'adminExtend', 'adminRenew', 'selfExtend', 'selfRenew', 'unknownFutureValue')] + [System.String] + $Action, + + [Parameter()] + [System.String] + $Justification, + + [Parameter()] + [System.Boolean] + $IsValidationOnly, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $ScheduleInfo, + + [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, + + [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 + $nullResult.Ensure = 'Absent' + try + { + $request = $null + if (-not [System.String]::IsNullOrEmpty($Id)) + { + if ($null -ne $Script:exportedInstances -and $Script:ExportMode) + { + $request = $Script:exportedInstances | Where-Object -FilterScript { $_.Id -eq $Id } + } + else + { + Write-Verbose -Message "Getting Role Eligibility by Id {$Id}" + $request = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -UnifiedRoleEligibilityScheduleId $Id ` + -ErrorAction SilentlyContinue + } + } + + Write-Verbose -Message 'Getting Role Eligibility by PrincipalId and RoleDefinitionId' + $PrincipalValue = $null + if ($PrincipalType -eq 'User') + { + Write-Verbose -Message "Retrieving Principal by UserPrincipalName {$Principal}" + $PrincipalInstance = Get-MgUser -Filter "UserPrincipalName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalValue = $PrincipalInstance.UserPrincipalName + } + elseif ($null -eq $PrincipalIdValue -and $PrincipalType -eq 'Group') + { + Write-Verbose -Message "Retrieving Principal by DisplayName {$Principal}" + $PrincipalInstance = Get-MgGroup -Filter "DisplayName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalValue = $PrincipalInstance.DisplayName + } + else + { + Write-Verbose -Message "Retrieving Principal by DisplayName {$Principal}" + $PrincipalInstance = Get-MgServicePrincipal -Filter "DisplayName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalValue = $PrincipalInstance.DisplayName + } + + Write-Verbose -Message "Found Principal {$PrincipalValue}" + $RoleDefinitionId = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id + Write-Verbose -Message "Retrieved role definition {$RoleDefinition} with ID {$RoleDefinitionId}" + + if ($null -eq $request) + { + Write-Verbose -Message "Retrieving the request by PrincipalId {$($PrincipalInstance.Id)}, RoleDefinitionId {$($RoleDefinitionId)} and DirectoryScopeId {$($DirectoryScopeId)}" + [Array] $requests = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -Filter "PrincipalId eq '$($PrincipalInstance.Id)' and RoleDefinitionId eq '$($RoleDefinitionId)' and DirectoryScopeId eq '$($DirectoryScopeId)'" + if ($requests.Length -eq 0) + { + return $nullResult + } + + $request = $requests[0] + } + + $schedules = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -Filter "PrincipalId eq '$($request.PrincipalId)'" + $schedule = $schedules | Where-Object -FilterScript { $_.RoleDefinitionId -eq $RoleDefinitionId } + if ($null -eq $schedule) + { + foreach ($instance in $schedules) + { + $roleDefinitionInfo = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $instance.RoleDefinitionId + if ($null -ne $roleDefinitionInfo -and $RoleDefinitionInfo.DisplayName -eq $RoleDefinition) + { + $schedule = $instance + break + } + } + } + + if ($null -eq $schedule -or $null -eq $request) + { + if ($null -eq $schedule) + { + Write-Verbose -Message "Could not retrieve the schedule for {$($request.PrincipalId)} & RoleDefinitionId {$RoleDefinitionId}" + } + if ($null -eq $request) + { + Write-Verbose -Message "Could not request the schedule for {$RoleDefinition}" + } + return $nullResult + } + + $ScheduleInfoValue = @{} + + if ($null -ne $schedule.ScheduleInfo.Expiration) + { + $expirationValue = @{ + duration = $schedule.ScheduleInfo.Expiration.Duration + type = $schedule.ScheduleInfo.Expiration.Type + } + if ($null -ne $schedule.ScheduleInfo.Expiration.EndDateTime) + { + $expirationValue.Add('endDateTime', $schedule.ScheduleInfo.Expiration.EndDateTime.ToString('yyyy-MM-ddThh:mm:ssZ')) + } + $ScheduleInfoValue.Add('expiration', $expirationValue) + } + if ($null -ne $schedule.ScheduleInfo.Recurrence) + { + $recurrenceValue = @{ + pattern = @{ + 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 = $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 $schedule.ScheduleInfo.StartDateTime) + { + $ScheduleInfoValue.Add('StartDateTime', $schedule.ScheduleInfo.StartDateTime.ToString('yyyy-MM-ddThh:mm:ssZ')) + } + + $results = @{ + Principal = $PrincipalValue + PrincipalType = $PrincipalType + RoleDefinition = $RoleDefinition + DirectoryScopeId = $request.DirectoryScopeId + AppScopeId = $request.AppScopeId + Action = $request.Action + Id = $request.Id + Justification = $request.Justification + IsValidationOnly = $request.IsValidationOnly + ScheduleInfo = $ScheduleInfoValue + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + Managedidentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + return $results + } + catch + { + Write-Verbose "Error: $_" + 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] + $Principal, + + [Parameter(Mandatory = $true)] + [System.String] + $RoleDefinition, + + [Parameter(Mandatory = $true)] + [ValidateSet('User', 'Group', 'ServicePrincipal')] + [System.String] + $PrincipalType, + + [Parameter()] + [System.String] + $Id, + + [Parameter(Mandatory = $true)] + [System.String] + $DirectoryScopeId, + + [Parameter()] + [System.String] + $AppScopeId, + + [Parameter()] + [ValidateSet('adminAssign', 'adminUpdate', 'adminRemove', 'selfActivate', 'selfDeactivate', 'adminExtend', 'adminRenew', 'selfExtend', 'selfRenew', 'unknownFutureValue')] + [System.String] + $Action, + + [Parameter()] + [System.String] + $Justification, + + [Parameter()] + [System.Boolean] + $IsValidationOnly, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $ScheduleInfo, + + [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, + + [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 + + $currentInstance = Get-TargetResource @PSBoundParameters + + $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + # CREATE + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + ##TODO - Replace by the New cmdlet for the resource + New-Cmdlet @SetParameters + } + # UPDATE + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Update/Set cmdlet for the resource + Set-cmdlet @SetParameters + } + # REMOVE + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Remove cmdlet for the resource + Remove-cmdlet @SetParameters + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Principal, + + [Parameter(Mandatory = $true)] + [System.String] + $RoleDefinition, + + [Parameter(Mandatory = $true)] + [ValidateSet('User', 'Group', 'ServicePrincipal')] + [System.String] + $PrincipalType, + + [Parameter()] + [System.String] + $Id, + + [Parameter(Mandatory = $true)] + [System.String] + $DirectoryScopeId, + + [Parameter()] + [System.String] + $AppScopeId, + + [Parameter()] + [ValidateSet('adminAssign', 'adminUpdate', 'adminRemove', 'selfActivate', 'selfDeactivate', 'adminExtend', 'adminRenew', 'selfExtend', 'selfRenew', 'unknownFutureValue')] + [System.String] + $Action, + + [Parameter()] + [System.String] + $Justification, + + [Parameter()] + [System.Boolean] + $IsValidationOnly, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $ScheduleInfo, + + [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, + + [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-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -All ` + -ErrorAction SilentlyContinue + + $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 + # Find the Principal Type + $principalType = 'User' + $userInfo = Get-MgUser -UserId $config.PrincipalId -ErrorAction SilentlyContinue + + if ($null -eq $userInfo) + { + $principalType = 'Group' + $groupInfo = Get-MgGroup -GroupId $config.PrincipalId -ErrorAction SilentlyContinue + if ($null -eq $groupInfo) + { + $principalType = 'ServicePrincipal' + $spnInfo = Get-MgServicePrincipal -ServicePrincipalId $config.PrincipalId -ErrorAction SilentlyContinue + if ($null -ne $spnInfo) + { + $PrincipalValue = $spnInfo.DisplayName + } + else + { + $PrincipalValue = $null + } + } + else + { + $PrincipalValue = $groupInfo.DisplayName + } + } + else + { + $PrincipalValue = $userInfo.UserPrincipalName + } + + if ($null -ne $PrincipalValue) + { + $RoleDefinitionId = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $config.RoleDefinitionId + $params = @{ + Id = $config.Id + Principal = $PrincipalValue + PrincipalType = $principalType + DirectoryScopeId = $config.DirectoryScopeId + RoleDefinition = $RoleDefinitionId.DisplayName + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + if ($Results.ScheduleInfo) + { + $complexMapping = @( + @{ + Name = 'expiration' + CimInstanceName = 'AADRoleEligibilityScheduleExpiration' + IsRequired = $False + } + @{ + Name = 'Recurrence' + CimInstanceName = 'AADRoleEligibilityScheduleRecurrence' + IsRequired = $False + } + @{ + Name = "range" + CimInstanceName = 'AADRoleEligibilityScheduleRecurrenceRange' + IsRequired = $False + } + @{ + Name = "pattern" + CimInstanceName = 'AADRoleEligibilityScheduleRecurrencePattern' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.ScheduleInfo ` + -CIMInstanceName 'AADRoleEligibilityScheduleInfo' ` + -ComplexTypeMapping $complexMapping + if ($complexTypeStringResult) + { + $Results.ScheduleInfo = $complexTypeStringResult + } + else + { + $Results.Remove('ScheduleInfo') | Out-Null + } + } + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + if ($Results.ScheduleInfo) + { + $isCIMArray = $false + if ($Results.ScheduleInfo.getType().Fullname -like '*[[\]]') + { + $isCIMArray = $true + } + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'ScheduleInfo' -IsCIMArray:$isCIMArray + } + $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_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.schema.mof new file mode 100644 index 0000000000..3072734229 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.schema.mof @@ -0,0 +1,67 @@ +[ClassVersion("1.0.0")] +class MSFT_AADRoleEligibilityScheduleRecurrenceRange +{ + [Required, Description("The date to stop applying the recurrence pattern. Depending on the recurrence pattern of the event, the last occurrence of the meeting may not be this date.")] String endDate; + [Write, Description("The number of times to repeat the event. Required and must be positive if type is numbered.")] UInt32 numberOfOccurrences; + [Write, Description("Time zone for the startDate and endDate properties.")] String recurrenceTimeZone; + [Required, Description("The date to start applying the recurrence pattern. The first occurrence of the meeting may be this date or later, depending on the recurrence pattern of the event. Must be the same value as the start property of the recurring event.")] String startDate; + [Required, Description("The recurrence range. The possible values are: endDate, noEnd, numbered."),ValueMap{"endDate","noEnd","numbered"}, Values{"endDate","noEnd","numbered"}] String type; +}; + +[ClassVersion("1.0.0")] +class MSFT_AADRoleEligibilityScheduleRecurrencePattern +{ + [Write, Description("The day of the month on which the event occurs.")] UInt32 dayOfMonth; + [Write, Description("A collection of the days of the week on which the event occurs. The possible values are: sunday, monday, tuesday, wednesday, thursday, friday, saturday"), ValueMap{"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}, Values{"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}] String daysOfWeek[]; + [Write, Description("The first day of the week."), ValueMap{"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}, Values{"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}] String firstDayOfWeek; + [Write, Description("Specifies on which instance of the allowed days specified in daysOfWeek the event occurs, counted from the first instance in the month. The possible values are: first, second, third, fourth, last."), ValueMap{"first","second","third","fourth","last"}, Values{"first","second","third","fourth","last"}] String index; + [Write, Description("The number of units between occurrences, where units can be in days, weeks, months, or years, depending on the type.")] UInt32 interval; + [Write, Description("The month in which the event occurs. This is a number from 1 to 12.")] UInt32 month; + [Write, Description("The recurrence pattern type: daily, weekly, absoluteMonthly, relativeMonthly, absoluteYearly, relativeYearly."), ValueMap{"daily","weekly","absoluteMonthly","relativeMonthly","absoluteYearly","relativeYearly"}, Values{"daily","weekly","absoluteMonthly","relativeMonthly","absoluteYearly","relativeYearly"}] String type; +}; + +[ClassVersion("1.0.0")] +class MSFT_AADRoleEligibilityScheduleRecurrence +{ + [Write, Description("The frequency of an event."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleRecurrencePattern")] String pattern; + [Write, Description("The duration of an event."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleRecurrenceRange")] String range; +}; + +[ClassVersion("1.0.0")] +class MSFT_AADRoleEligibilityScheduleExpiration +{ + [Write, Description("The requestor's desired duration of access represented in ISO 8601 format for durations. For example, PT3H refers to three hours. If specified in a request, endDateTime should not be present and the type property should be set to afterDuration.")] String duration; + [Write, Description("Timestamp of date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z.")] String endDateTime; + [Write, Description("The requestor's desired expiration pattern type. The possible values are: notSpecified, noExpiration, afterDateTime, afterDuration."), ValueMap{"notSpecified","noExpiration","afterDateTime","afterDuration"}, Values{"notSpecified","noExpiration","afterDateTime","afterDuration"}] String type; +}; + +[ClassVersion("1.0.0")] +class MSFT_AADRoleEligibilityScheduleInfo +{ + [Write, Description("When the eligible or active assignment expires."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleExpiration")] String expiration; + [Write, Description("The frequency of the eligible or active assignment. This property is currently unsupported in PIM."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleRecurrence")] String recurrence; + [Write, Description("When the eligible or active assignment becomes active.")] String startDateTime; +}; + +[ClassVersion("1.0.0.0"), FriendlyName("AADRoleEligibilitySchedule")] +class MSFT_AADRoleEligibilitySchedule : 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; + [Key, 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; + [Write, Description("Represents the type of operation on the role eligibility request.The possible values are: adminAssign, adminUpdate, adminRemove, selfActivate, selfDeactivate, adminExtend, adminRenew, selfExtend, selfRenew, unknownFutureValue."), ValueMap{"adminAssign","adminUpdate","adminRemove","selfActivate","selfDeactivate","adminExtend","adminRenew","selfExtend","selfRenew","unknownFutureValue"}, Values{"adminAssign","adminUpdate","adminRemove","selfActivate","selfDeactivate","adminExtend","adminRenew","selfExtend","selfRenew","unknownFutureValue"}] String Action; + [Write, Description("Determines whether the call is a validation or an actual call. Only set this property if you want to check whether an activation is subject to additional rules like MFA before actually submitting the request.")] Boolean IsValidationOnly; + [Write, Description("A message provided by users and administrators when create they create the unifiedRoleEligibilitySchedule object. Optional when action is adminRemove. Whether this property is required or optional is also dependent on the settings for the Azure AD role.")] String Justification; + [Write, Description("The period of the role eligibility. Optional when action is adminRemove. The period of eligibility is dependent on the settings of the Azure AD role."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleInfo")] String ScheduleInfo; + [Write, Description("Present ensures the instance 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 application to authenticate with."), 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; + [Write, Description("Access token used for authentication.")] String AccessTokens[]; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/readme.md new file mode 100644 index 0000000000..32e0e7fb27 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/readme.md @@ -0,0 +1,6 @@ + +# ResourceName + +## Description + +##TODO - Provide a short description of what the resource is set to configure. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/settings.json new file mode 100644 index 0000000000..edf14b05e4 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/settings.json @@ -0,0 +1,32 @@ +{ + "resourceName": "ResourceName", + "description": "Description of what the resource is about.", + "roles": { + "read": [ + "Role" + ], + "update": [ + "Role" + ] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [ + { + "name": "Permission for Monitoring and Export" + } + ], + "update": [ + { + "name": "Permission for deploying" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/1-Create.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/1-Create.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/2-Update.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/2-Update.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/3-Remove.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/3-Remove.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilitySchedule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilitySchedule.Tests.ps1 new file mode 100644 index 0000000000..780e0f343d --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilitySchedule.Tests.ps1 @@ -0,0 +1,178 @@ +[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" + } + + ##TODO - Mock any Remove/Set/New cmdlets + + # 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 should exist but it DOES NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return $null + Mock -CommandName Get-Cmdlet -MockWith { + return $null + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should create a new instance from the Set method' { + ##TODO - Replace the New-Cmdlet by the appropriate one + Set-TargetResource @testParams + Should -Invoke -CommandName New-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Absent' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + It 'Should return Values 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 remove the instance from the Set method' { + Set-TargetResource @testParams + ##TODO - Replace the Remove-Cmdlet by the appropriate one + Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return the desired values + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + 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 = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return a drift + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + It 'Should return Values 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 call the Set method' { + Set-TargetResource @testParams + ##TODO - Replace the Update-Cmdlet by the appropriate one + Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + 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 187bf9ae147e8efd8c2a026a1dcf7199619af073 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 15 Jan 2025 08:19:34 -0500 Subject: [PATCH 2/4] Fixes --- .../MSFT_AADRoleEligibilitySchedule.psm1 | 688 ------------------ ...MSFT_AADRoleEligibilitySchedule.schema.mof | 67 -- .../MSFT_AADRoleEligibilitySchedule/readme.md | 6 - .../settings.json | 32 - ...SFT_AADRoleEligibilityScheduleRequest.psm1 | 491 ++++--------- ...DRoleEligibilityScheduleRequest.schema.mof | 8 - .../AADRoleEligibilitySchedule/1-Create.ps1 | 26 - .../AADRoleEligibilitySchedule/2-Update.ps1 | 26 - .../AADRoleEligibilitySchedule/3-Remove.ps1 | 26 - ...65DSC.AADRoleEligibilitySchedule.Tests.ps1 | 178 ----- 10 files changed, 157 insertions(+), 1391 deletions(-) delete mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.psm1 delete mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.schema.mof delete mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/readme.md delete mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/settings.json delete mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/1-Create.ps1 delete mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/2-Update.ps1 delete mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/3-Remove.ps1 delete mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilitySchedule.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.psm1 deleted file mode 100644 index 647c13d327..0000000000 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.psm1 +++ /dev/null @@ -1,688 +0,0 @@ -function Get-TargetResource -{ - [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $Principal, - - [Parameter(Mandatory = $true)] - [System.String] - $RoleDefinition, - - [Parameter(Mandatory = $true)] - [ValidateSet('User', 'Group', 'ServicePrincipal')] - [System.String] - $PrincipalType, - - [Parameter()] - [System.String] - $Id, - - [Parameter(Mandatory = $true)] - [System.String] - $DirectoryScopeId, - - [Parameter()] - [System.String] - $AppScopeId, - - [Parameter()] - [ValidateSet('adminAssign', 'adminUpdate', 'adminRemove', 'selfActivate', 'selfDeactivate', 'adminExtend', 'adminRenew', 'selfExtend', 'selfRenew', 'unknownFutureValue')] - [System.String] - $Action, - - [Parameter()] - [System.String] - $Justification, - - [Parameter()] - [System.Boolean] - $IsValidationOnly, - - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] - $ScheduleInfo, - - [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, - - [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 - $nullResult.Ensure = 'Absent' - try - { - $request = $null - if (-not [System.String]::IsNullOrEmpty($Id)) - { - if ($null -ne $Script:exportedInstances -and $Script:ExportMode) - { - $request = $Script:exportedInstances | Where-Object -FilterScript { $_.Id -eq $Id } - } - else - { - Write-Verbose -Message "Getting Role Eligibility by Id {$Id}" - $request = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -UnifiedRoleEligibilityScheduleId $Id ` - -ErrorAction SilentlyContinue - } - } - - Write-Verbose -Message 'Getting Role Eligibility by PrincipalId and RoleDefinitionId' - $PrincipalValue = $null - if ($PrincipalType -eq 'User') - { - Write-Verbose -Message "Retrieving Principal by UserPrincipalName {$Principal}" - $PrincipalInstance = Get-MgUser -Filter "UserPrincipalName eq '$Principal'" -ErrorAction SilentlyContinue - $PrincipalValue = $PrincipalInstance.UserPrincipalName - } - elseif ($null -eq $PrincipalIdValue -and $PrincipalType -eq 'Group') - { - Write-Verbose -Message "Retrieving Principal by DisplayName {$Principal}" - $PrincipalInstance = Get-MgGroup -Filter "DisplayName eq '$Principal'" -ErrorAction SilentlyContinue - $PrincipalValue = $PrincipalInstance.DisplayName - } - else - { - Write-Verbose -Message "Retrieving Principal by DisplayName {$Principal}" - $PrincipalInstance = Get-MgServicePrincipal -Filter "DisplayName eq '$Principal'" -ErrorAction SilentlyContinue - $PrincipalValue = $PrincipalInstance.DisplayName - } - - Write-Verbose -Message "Found Principal {$PrincipalValue}" - $RoleDefinitionId = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id - Write-Verbose -Message "Retrieved role definition {$RoleDefinition} with ID {$RoleDefinitionId}" - - if ($null -eq $request) - { - Write-Verbose -Message "Retrieving the request by PrincipalId {$($PrincipalInstance.Id)}, RoleDefinitionId {$($RoleDefinitionId)} and DirectoryScopeId {$($DirectoryScopeId)}" - [Array] $requests = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -Filter "PrincipalId eq '$($PrincipalInstance.Id)' and RoleDefinitionId eq '$($RoleDefinitionId)' and DirectoryScopeId eq '$($DirectoryScopeId)'" - if ($requests.Length -eq 0) - { - return $nullResult - } - - $request = $requests[0] - } - - $schedules = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -Filter "PrincipalId eq '$($request.PrincipalId)'" - $schedule = $schedules | Where-Object -FilterScript { $_.RoleDefinitionId -eq $RoleDefinitionId } - if ($null -eq $schedule) - { - foreach ($instance in $schedules) - { - $roleDefinitionInfo = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $instance.RoleDefinitionId - if ($null -ne $roleDefinitionInfo -and $RoleDefinitionInfo.DisplayName -eq $RoleDefinition) - { - $schedule = $instance - break - } - } - } - - if ($null -eq $schedule -or $null -eq $request) - { - if ($null -eq $schedule) - { - Write-Verbose -Message "Could not retrieve the schedule for {$($request.PrincipalId)} & RoleDefinitionId {$RoleDefinitionId}" - } - if ($null -eq $request) - { - Write-Verbose -Message "Could not request the schedule for {$RoleDefinition}" - } - return $nullResult - } - - $ScheduleInfoValue = @{} - - if ($null -ne $schedule.ScheduleInfo.Expiration) - { - $expirationValue = @{ - duration = $schedule.ScheduleInfo.Expiration.Duration - type = $schedule.ScheduleInfo.Expiration.Type - } - if ($null -ne $schedule.ScheduleInfo.Expiration.EndDateTime) - { - $expirationValue.Add('endDateTime', $schedule.ScheduleInfo.Expiration.EndDateTime.ToString('yyyy-MM-ddThh:mm:ssZ')) - } - $ScheduleInfoValue.Add('expiration', $expirationValue) - } - if ($null -ne $schedule.ScheduleInfo.Recurrence) - { - $recurrenceValue = @{ - pattern = @{ - 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 = $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 $schedule.ScheduleInfo.StartDateTime) - { - $ScheduleInfoValue.Add('StartDateTime', $schedule.ScheduleInfo.StartDateTime.ToString('yyyy-MM-ddThh:mm:ssZ')) - } - - $results = @{ - Principal = $PrincipalValue - PrincipalType = $PrincipalType - RoleDefinition = $RoleDefinition - DirectoryScopeId = $request.DirectoryScopeId - AppScopeId = $request.AppScopeId - Action = $request.Action - Id = $request.Id - Justification = $request.Justification - IsValidationOnly = $request.IsValidationOnly - ScheduleInfo = $ScheduleInfoValue - Ensure = 'Present' - Credential = $Credential - ApplicationId = $ApplicationId - TenantId = $TenantId - ApplicationSecret = $ApplicationSecret - CertificateThumbprint = $CertificateThumbprint - Managedidentity = $ManagedIdentity.IsPresent - AccessTokens = $AccessTokens - } - return $results - } - catch - { - Write-Verbose "Error: $_" - 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] - $Principal, - - [Parameter(Mandatory = $true)] - [System.String] - $RoleDefinition, - - [Parameter(Mandatory = $true)] - [ValidateSet('User', 'Group', 'ServicePrincipal')] - [System.String] - $PrincipalType, - - [Parameter()] - [System.String] - $Id, - - [Parameter(Mandatory = $true)] - [System.String] - $DirectoryScopeId, - - [Parameter()] - [System.String] - $AppScopeId, - - [Parameter()] - [ValidateSet('adminAssign', 'adminUpdate', 'adminRemove', 'selfActivate', 'selfDeactivate', 'adminExtend', 'adminRenew', 'selfExtend', 'selfRenew', 'unknownFutureValue')] - [System.String] - $Action, - - [Parameter()] - [System.String] - $Justification, - - [Parameter()] - [System.Boolean] - $IsValidationOnly, - - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] - $ScheduleInfo, - - [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, - - [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 - - $currentInstance = Get-TargetResource @PSBoundParameters - - $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters - - # CREATE - if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') - { - ##TODO - Replace by the New cmdlet for the resource - New-Cmdlet @SetParameters - } - # UPDATE - elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') - { - ##TODO - Replace by the Update/Set cmdlet for the resource - Set-cmdlet @SetParameters - } - # REMOVE - elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') - { - ##TODO - Replace by the Remove cmdlet for the resource - Remove-cmdlet @SetParameters - } -} - -function Test-TargetResource -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $Principal, - - [Parameter(Mandatory = $true)] - [System.String] - $RoleDefinition, - - [Parameter(Mandatory = $true)] - [ValidateSet('User', 'Group', 'ServicePrincipal')] - [System.String] - $PrincipalType, - - [Parameter()] - [System.String] - $Id, - - [Parameter(Mandatory = $true)] - [System.String] - $DirectoryScopeId, - - [Parameter()] - [System.String] - $AppScopeId, - - [Parameter()] - [ValidateSet('adminAssign', 'adminUpdate', 'adminRemove', 'selfActivate', 'selfDeactivate', 'adminExtend', 'adminRenew', 'selfExtend', 'selfRenew', 'unknownFutureValue')] - [System.String] - $Action, - - [Parameter()] - [System.String] - $Justification, - - [Parameter()] - [System.Boolean] - $IsValidationOnly, - - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] - $ScheduleInfo, - - [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, - - [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-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -All ` - -ErrorAction SilentlyContinue - - $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 - # Find the Principal Type - $principalType = 'User' - $userInfo = Get-MgUser -UserId $config.PrincipalId -ErrorAction SilentlyContinue - - if ($null -eq $userInfo) - { - $principalType = 'Group' - $groupInfo = Get-MgGroup -GroupId $config.PrincipalId -ErrorAction SilentlyContinue - if ($null -eq $groupInfo) - { - $principalType = 'ServicePrincipal' - $spnInfo = Get-MgServicePrincipal -ServicePrincipalId $config.PrincipalId -ErrorAction SilentlyContinue - if ($null -ne $spnInfo) - { - $PrincipalValue = $spnInfo.DisplayName - } - else - { - $PrincipalValue = $null - } - } - else - { - $PrincipalValue = $groupInfo.DisplayName - } - } - else - { - $PrincipalValue = $userInfo.UserPrincipalName - } - - if ($null -ne $PrincipalValue) - { - $RoleDefinitionId = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $config.RoleDefinitionId - $params = @{ - Id = $config.Id - Principal = $PrincipalValue - PrincipalType = $principalType - DirectoryScopeId = $config.DirectoryScopeId - RoleDefinition = $RoleDefinitionId.DisplayName - Ensure = 'Present' - Credential = $Credential - ApplicationId = $ApplicationId - TenantId = $TenantId - ApplicationSecret = $ApplicationSecret - CertificateThumbprint = $CertificateThumbprint - ManagedIdentity = $ManagedIdentity.IsPresent - AccessTokens = $AccessTokens - } - } - - $Results = Get-TargetResource @Params - $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` - -Results $Results - - if ($Results.ScheduleInfo) - { - $complexMapping = @( - @{ - Name = 'expiration' - CimInstanceName = 'AADRoleEligibilityScheduleExpiration' - IsRequired = $False - } - @{ - Name = 'Recurrence' - CimInstanceName = 'AADRoleEligibilityScheduleRecurrence' - IsRequired = $False - } - @{ - Name = "range" - CimInstanceName = 'AADRoleEligibilityScheduleRecurrenceRange' - IsRequired = $False - } - @{ - Name = "pattern" - CimInstanceName = 'AADRoleEligibilityScheduleRecurrencePattern' - IsRequired = $False - } - ) - $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.ScheduleInfo ` - -CIMInstanceName 'AADRoleEligibilityScheduleInfo' ` - -ComplexTypeMapping $complexMapping - if ($complexTypeStringResult) - { - $Results.ScheduleInfo = $complexTypeStringResult - } - else - { - $Results.Remove('ScheduleInfo') | Out-Null - } - } - $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` - -ConnectionMode $ConnectionMode ` - -ModulePath $PSScriptRoot ` - -Results $Results ` - -Credential $Credential - if ($Results.ScheduleInfo) - { - $isCIMArray = $false - if ($Results.ScheduleInfo.getType().Fullname -like '*[[\]]') - { - $isCIMArray = $true - } - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` - -ParameterName 'ScheduleInfo' -IsCIMArray:$isCIMArray - } - $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_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.schema.mof deleted file mode 100644 index 3072734229..0000000000 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/MSFT_AADRoleEligibilitySchedule.schema.mof +++ /dev/null @@ -1,67 +0,0 @@ -[ClassVersion("1.0.0")] -class MSFT_AADRoleEligibilityScheduleRecurrenceRange -{ - [Required, Description("The date to stop applying the recurrence pattern. Depending on the recurrence pattern of the event, the last occurrence of the meeting may not be this date.")] String endDate; - [Write, Description("The number of times to repeat the event. Required and must be positive if type is numbered.")] UInt32 numberOfOccurrences; - [Write, Description("Time zone for the startDate and endDate properties.")] String recurrenceTimeZone; - [Required, Description("The date to start applying the recurrence pattern. The first occurrence of the meeting may be this date or later, depending on the recurrence pattern of the event. Must be the same value as the start property of the recurring event.")] String startDate; - [Required, Description("The recurrence range. The possible values are: endDate, noEnd, numbered."),ValueMap{"endDate","noEnd","numbered"}, Values{"endDate","noEnd","numbered"}] String type; -}; - -[ClassVersion("1.0.0")] -class MSFT_AADRoleEligibilityScheduleRecurrencePattern -{ - [Write, Description("The day of the month on which the event occurs.")] UInt32 dayOfMonth; - [Write, Description("A collection of the days of the week on which the event occurs. The possible values are: sunday, monday, tuesday, wednesday, thursday, friday, saturday"), ValueMap{"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}, Values{"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}] String daysOfWeek[]; - [Write, Description("The first day of the week."), ValueMap{"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}, Values{"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}] String firstDayOfWeek; - [Write, Description("Specifies on which instance of the allowed days specified in daysOfWeek the event occurs, counted from the first instance in the month. The possible values are: first, second, third, fourth, last."), ValueMap{"first","second","third","fourth","last"}, Values{"first","second","third","fourth","last"}] String index; - [Write, Description("The number of units between occurrences, where units can be in days, weeks, months, or years, depending on the type.")] UInt32 interval; - [Write, Description("The month in which the event occurs. This is a number from 1 to 12.")] UInt32 month; - [Write, Description("The recurrence pattern type: daily, weekly, absoluteMonthly, relativeMonthly, absoluteYearly, relativeYearly."), ValueMap{"daily","weekly","absoluteMonthly","relativeMonthly","absoluteYearly","relativeYearly"}, Values{"daily","weekly","absoluteMonthly","relativeMonthly","absoluteYearly","relativeYearly"}] String type; -}; - -[ClassVersion("1.0.0")] -class MSFT_AADRoleEligibilityScheduleRecurrence -{ - [Write, Description("The frequency of an event."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleRecurrencePattern")] String pattern; - [Write, Description("The duration of an event."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleRecurrenceRange")] String range; -}; - -[ClassVersion("1.0.0")] -class MSFT_AADRoleEligibilityScheduleExpiration -{ - [Write, Description("The requestor's desired duration of access represented in ISO 8601 format for durations. For example, PT3H refers to three hours. If specified in a request, endDateTime should not be present and the type property should be set to afterDuration.")] String duration; - [Write, Description("Timestamp of date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z.")] String endDateTime; - [Write, Description("The requestor's desired expiration pattern type. The possible values are: notSpecified, noExpiration, afterDateTime, afterDuration."), ValueMap{"notSpecified","noExpiration","afterDateTime","afterDuration"}, Values{"notSpecified","noExpiration","afterDateTime","afterDuration"}] String type; -}; - -[ClassVersion("1.0.0")] -class MSFT_AADRoleEligibilityScheduleInfo -{ - [Write, Description("When the eligible or active assignment expires."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleExpiration")] String expiration; - [Write, Description("The frequency of the eligible or active assignment. This property is currently unsupported in PIM."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleRecurrence")] String recurrence; - [Write, Description("When the eligible or active assignment becomes active.")] String startDateTime; -}; - -[ClassVersion("1.0.0.0"), FriendlyName("AADRoleEligibilitySchedule")] -class MSFT_AADRoleEligibilitySchedule : 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; - [Key, 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; - [Write, Description("Represents the type of operation on the role eligibility request.The possible values are: adminAssign, adminUpdate, adminRemove, selfActivate, selfDeactivate, adminExtend, adminRenew, selfExtend, selfRenew, unknownFutureValue."), ValueMap{"adminAssign","adminUpdate","adminRemove","selfActivate","selfDeactivate","adminExtend","adminRenew","selfExtend","selfRenew","unknownFutureValue"}, Values{"adminAssign","adminUpdate","adminRemove","selfActivate","selfDeactivate","adminExtend","adminRenew","selfExtend","selfRenew","unknownFutureValue"}] String Action; - [Write, Description("Determines whether the call is a validation or an actual call. Only set this property if you want to check whether an activation is subject to additional rules like MFA before actually submitting the request.")] Boolean IsValidationOnly; - [Write, Description("A message provided by users and administrators when create they create the unifiedRoleEligibilitySchedule object. Optional when action is adminRemove. Whether this property is required or optional is also dependent on the settings for the Azure AD role.")] String Justification; - [Write, Description("The period of the role eligibility. Optional when action is adminRemove. The period of eligibility is dependent on the settings of the Azure AD role."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleInfo")] String ScheduleInfo; - [Write, Description("Present ensures the instance 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 application to authenticate with."), 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; - [Write, Description("Access token used for authentication.")] String AccessTokens[]; -}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/readme.md deleted file mode 100644 index 32e0e7fb27..0000000000 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/readme.md +++ /dev/null @@ -1,6 +0,0 @@ - -# ResourceName - -## Description - -##TODO - Provide a short description of what the resource is set to configure. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/settings.json deleted file mode 100644 index edf14b05e4..0000000000 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilitySchedule/settings.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "resourceName": "ResourceName", - "description": "Description of what the resource is about.", - "roles": { - "read": [ - "Role" - ], - "update": [ - "Role" - ] - }, - "permissions": { - "graph": { - "delegated": { - "read": [], - "update": [] - }, - "application": { - "read": [ - { - "name": "Permission for Monitoring and Export" - } - ], - "update": [ - { - "name": "Permission for deploying" - } - ] - } - } - } -} diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 index 5091971e4f..68a19db077 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.psm1 @@ -46,10 +46,6 @@ [Microsoft.Management.Infrastructure.CimInstance] $ScheduleInfo, - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] - $TicketInfo, - [Parameter()] [System.String] [ValidateSet('Absent', 'Present')] @@ -83,8 +79,9 @@ [System.String[]] $AccessTokens ) - $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` - -InboundParameters $PSBoundParameters + + New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters | Out-Null #Ensure the proper dependencies are installed in the current environment. Confirm-M365DSCDependencies @@ -112,7 +109,7 @@ else { Write-Verbose -Message "Getting Role Eligibility by Id {$Id}" - $request = Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -UnifiedRoleEligibilityScheduleRequestId $Id ` + $request = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -UnifiedRoleEligibilityScheduleId $Id ` -ErrorAction SilentlyContinue } } @@ -145,7 +142,7 @@ if ($null -eq $request) { Write-Verbose -Message "Retrieving the request by PrincipalId {$($PrincipalInstance.Id)}, RoleDefinitionId {$($RoleDefinitionId)} and DirectoryScopeId {$($DirectoryScopeId)}" - [Array] $requests = Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -Filter "PrincipalId eq '$($PrincipalInstance.Id)' and RoleDefinitionId eq '$($RoleDefinitionId)' and DirectoryScopeId eq '$($DirectoryScopeId)'" + [Array] $requests = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -Filter "PrincipalId eq '$($PrincipalInstance.Id)' and RoleDefinitionId eq '$($RoleDefinitionId)' and DirectoryScopeId eq '$($DirectoryScopeId)'" if ($requests.Length -eq 0) { return $nullResult @@ -223,15 +220,6 @@ $ScheduleInfoValue.Add('StartDateTime', $schedule.ScheduleInfo.StartDateTime.ToString('yyyy-MM-ddThh:mm:ssZ')) } - $ticketInfoValue = $null - if ($null -ne $request.TicketInfo) - { - $ticketInfoValue = @{ - ticketNumber = $request.TicketInfo.TicketNumber - ticketSystem = $request.TicketInfo.TicketSystem - } - } - $results = @{ Principal = $PrincipalValue PrincipalType = $PrincipalType @@ -243,7 +231,6 @@ Justification = $request.Justification IsValidationOnly = $request.IsValidationOnly ScheduleInfo = $ScheduleInfoValue - TicketInfo = $ticketInfoValue Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId @@ -315,10 +302,6 @@ function Set-TargetResource [Microsoft.Management.Infrastructure.CimInstance] $ScheduleInfo, - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] - $TicketInfo, - [Parameter()] [System.String] [ValidateSet('Absent', 'Present')] @@ -352,16 +335,6 @@ function Set-TargetResource [System.String[]] $AccessTokens ) - try - { - $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` - -InboundParameters $PSBoundParameters ` - - } - catch - { - Write-Verbose -Message $_ - } #Ensure the proper dependencies are installed in the current environment. Confirm-M365DSCDependencies @@ -377,128 +350,114 @@ function Set-TargetResource $currentInstance = Get-TargetResource @PSBoundParameters - $PSBoundParameters.Remove('Ensure') | Out-Null - $PSBoundParameters.Remove('Credential') | Out-Null - $PSBoundParameters.Remove('ApplicationId') | Out-Null - $PSBoundParameters.Remove('ApplicationSecret') | Out-Null - $PSBoundParameters.Remove('TenantId') | Out-Null - $PSBoundParameters.Remove('CertificateThumbprint') | Out-Null - $PSBoundParameters.Remove('ManagedIdentity') | Out-Null - $PSBoundParameters.Remove('Verbose') | Out-Null - $PSBoundParameters.Remove('AccessTokens') | Out-Null - - $ParametersOps = ([Hashtable]$PSBoundParameters).clone() - + Write-Verbose -Message "Retrieving Principal Id from Set-TargetResource" + $PrincipalId = $null if ($PrincipalType -eq 'User') { - [Array]$PrincipalIdValue = (Get-MgUser -Filter "UserPrincipalName eq '$Principal'").Id + Write-Verbose -Message "Retrieving Principal by UserPrincipalName {$Principal}" + $PrincipalInstance = Get-MgUser -Filter "UserPrincipalName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalId = $PrincipalInstance.Id } - elseif ($PrincipalType -eq 'Group') + elseif ($null -eq $PrincipalIdValue -and $PrincipalType -eq 'Group') { - [Array]$PrincipalIdValue = (Get-MgGroup -Filter "DisplayName eq '$Principal'").Id + Write-Verbose -Message "Retrieving Principal by DisplayName {$Principal}" + $PrincipalInstance = Get-MgGroup -Filter "DisplayName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalId = $PrincipalInstance.Id } - elseif ($PrincipalType -eq 'ServicePrincipal') + else { - [Array]$PrincipalIdValue = (Get-MgServicePrincipal -Filter "DisplayName eq '$Principal'").Id + Write-Verbose -Message "Retrieving Principal by DisplayName {$Principal}" + $PrincipalInstance = Get-MgServicePrincipal -Filter "DisplayName eq '$Principal'" -ErrorAction SilentlyContinue + $PrincipalId = $PrincipalInstance.Id } - if ($null -eq $PrincipalIdValue) - { - throw "Couldn't find Principal {$PrincipalId} of type {$PrincipalType}" + Write-Verbose -Message "Retrieving ROleDefinitionId from Set-TargetResource" + $RoleDefinitionId = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id + + $instanceParams = @{ + directoryScopeId = $DirectoryScopeId + principalId = $PrincipalId + roleDefinitionId = $RoleDefinitionId + scheduleInfo = @{ + expiration = @{ + type = $ScheduleInfo.Expiration.Type + duration = $ScheduleInfo.Expiration.Duration + endDateTime = $ScheduleInfo.Expiration.EndDateTime + } + startDateTime = $ScheduleInfo.StartDateTime + } } - elseif ($PrincipalIdValue.Length -gt 1) + + if (-not [System.String]::IsNullOrEmpty($AppScopeId)) { - throw "Multiple Principal with ID {$PrincipalId} of type {$PrincipalType} were found. Cannot create schedule." + $instanceParams.Add('appScopeId', $AppScopeId) } - $ParametersOps.Add('PrincipalId', $PrincipalIdValue[0]) - $ParametersOps.Remove('Principal') | Out-Null - - $RoleDefinitionIdValue = (Get-MgBetaRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$RoleDefinition'").Id - $ParametersOps.Add('RoleDefinitionId', $RoleDefinitionIdValue) - $ParametersOps.Remove('RoleDefinition') | Out-Null - if ($null -ne $ScheduleInfo) + if ($null -eq $instanceParams.ScheduleInfo.Expiration.Duration) { - $ScheduleInfoValue = @{} + $instanceParams.ScheduleInfo.Expiration.Remove('duration') | Out-Null + } - if ($ScheduleInfo.StartDateTime) - { - $ScheduleInfoValue.Add('startDateTime', $ScheduleInfo.StartDateTime) + $RecurrenceInfo = @{} + $foundRecurrenceItem = $false + if ($null -ne $ScheduleInfo.Recurrence.Pattern.Type) + { + $Pattern = @{ + dayOfMonth = $ScheduleInfo.Recurrence.Pattern.DayOfMonth + daysOfWeek = $ScheduleInfo.Recurrence.Pattern.DaysOfWeek + firstDayOfWeek = $ScheduleInfo.Recurrence.Pattern.FirstDayOfWeek + index = $ScheduleInfo.Recurrence.Pattern.Index + month = $ScheduleInfo.Recurrence.Pattern.Month + type = $ScheduleInfo.Recurrence.Pattern.Type } - - if ($ScheduleInfo.Expiration) - { - $expirationValue = @{ - endDateTime = $ScheduleInfo.Expiration.endDateTime - type = $ScheduleInfo.Expiration.type - } - if ($ScheduleInfo.Expiration.duration) - { - $expirationValue.Add('duration', $ScheduleInfo.Expiration.duration) - } - $ScheduleInfoValue.Add('Expiration', $expirationValue) + $RecurrenceInfo.Add('pattern', $Pattern) + $foundRecurrenceItem = $true + } + if ($null -ne $ScheduleInfo.Recurrence.Range.Type) + { + $Range = @{ + endDate = $ScheduleInfo.Recurrence.Range.EndDate + numberOfOccurrences = $ScheduleInfo.Recurrence.Range.NumberOfOccurrences + recurrenceTimeZone = $ScheduleInfo.Recurrence.Range.RecurrenceTimeZone + startDate = $ScheduleInfo.Recurrence.Range.StartDate + type = $ScheduleInfo.Recurrence.Range.Type } + $RecurrenceInfo.Add('range', $Range) + $foundRecurrenceItem = $true + } + if ($foundRecurrenceItem) + { + $instanceParams.Add('recurrence', $RecurrenceInfo) + } - if ($ScheduleInfo.Recurrence) - { - $Found = $false - $recurrenceValue = @{} - - if ($ScheduleInfo.Recurrence.Pattern) - { - $Found = $true - $patternValue = @{ - dayOfMonth = $ScheduleInfo.Recurrence.Pattern.dayOfMonth - daysOfWeek = $ScheduleInfo.Recurrence.Pattern.daysOfWeek - firstDayOfWeek = $ScheduleInfo.Recurrence.Pattern.firstDayOfWeek - index = $ScheduleInfo.Recurrence.Pattern.index - interval = $ScheduleInfo.Recurrence.Pattern.interval - month = $ScheduleInfo.Recurrence.Pattern.month - type = $ScheduleInfo.Recurrence.Pattern.type - } - $recurrenceValue.Add('Pattern', $patternValue) - } - if ($ScheduleInfo.Recurrence.Range) - { - $Found = $true - $rangeValue = @{ - endDate = $ScheduleInfo.Recurrence.Range.endDate - numberOfOccurrences = $ScheduleInfo.Recurrence.Range.numberOfOccurrences - recurrenceTimeZone = $ScheduleInfo.Recurrence.Range.recurrenceTimeZone - startDate = $ScheduleInfo.Recurrence.Range.startDate - type = $ScheduleInfo.Recurrence.Range.type - } - $recurrenceValue.Add('Range', $rangeValue) - } - if ($Found) - { - $ScheduleInfoValue.Add('Recurrence', $recurrenceValue) - } - } - Write-Verbose -Message "ScheduleInfo: $(Convert-M365DscHashtableToString -Hashtable $ScheduleInfoValue)" - $ParametersOps.ScheduleInfo = $ScheduleInfoValue + if ([System.String]::IsNullOrEmpty($instanceParams.scheduleInfo.expiration.endDateTime)) + { + $instanceParams.scheduleInfo.expiration.Remove('endDateTime') | Out-Null } - $ParametersOps.Remove('PrincipalType') | Out-Null + + # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { - Write-Verbose -Message "Creating a Role Assignment Schedule Request for principal {$Principal} and role {$RoleDefinition}" - $ParametersOps.Remove('Id') | Out-Null - Write-Verbose -Message "Values: $(Convert-M365DscHashtableToString -Hashtable $ParametersOps)" - New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @ParametersOps + $instanceParams.Add('action', 'AdminAssign') + $instanceParams.Add('justification', 'AdminAssign by Microsoft365DSC') + Write-Verbose -Message "Creating new role eligibility Schedule with parameters:`r`n$(ConvertTo-Json $instanceParams -Depth 10)" + New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @instanceParams -Verbose } + # UPDATE elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { - Write-Verbose -Message "Updating the Role Assignment Schedule Request for principal {$Principal} and role {$RoleDefinition}" - $ParametersOps.Remove('Id') | Out-Null - $ParametersOps.Action = 'AdminUpdate' - New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @ParametersOps + $instanceParams.Add('action', 'AdminUpdate') + $instanceParams.Add('justification', 'AdminUpdate by Microsoft365DSC') + Write-Verbose -Message "Updating role eligibility Schedule with parameters:`r`n$(ConvertTo-Json $instanceParams -Depth 10)" + New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @instanceParams } + # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { - Write-Verbose -Message "Removing the Role Assignment Schedule Request for principal {$Principal} and role {$RoleDefinition}" - $ParametersOps.Remove('Id') | Out-Null - $ParametersOps.Action = 'AdminRemove' - New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @ParametersOps + $instanceParams.Add('action', 'AdminRemove') + $instanceParams.Add('justification', 'AdminRemove by Microsoft365DSC') + Write-Verbose -Message "Removing role eligibility Schedule with parameters:`r`n$(ConvertTo-Json $instanceParams -Depth 10)" + New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest @instanceParams } } @@ -550,10 +509,6 @@ function Test-TargetResource [Microsoft.Management.Infrastructure.CimInstance] $ScheduleInfo, - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] - $TicketInfo, - [Parameter()] [System.String] [ValidateSet('Absent', 'Present')] @@ -600,11 +555,9 @@ function Test-TargetResource Add-M365DSCTelemetryEvent -Data $data #endregion - Write-Verbose -Message "Testing configuration of the Azure AD Role Eligibility Schedule Request for user {$Principal} and role {$RoleDefinition}" - $CurrentValues = Get-TargetResource @PSBoundParameters - $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() - $ValuesToCheck.Remove('Action') | Out-Null + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + if ($null -ne $CurrentValues.ScheduleInfo -and $null -ne $ValuesToCheck.ScheduleInfo) { # Compare ScheduleInfo.Expiration @@ -646,11 +599,11 @@ function Test-TargetResource return $false } } + $ValuesToCheck.Remove('ScheduleInfo') | Out-Null Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - $ValuesToCheck.Remove('ScheduleInfo') | Out-Null $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` -DesiredValues $PSBoundParameters ` @@ -714,16 +667,8 @@ function Export-TargetResource try { $Script:ExportMode = $true - #region resource generator code - $schedules = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -All -ErrorAction Stop - [array] $Script:exportedInstances = @() - [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 + [array] $Script:exportedInstances = Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -All ` + -ErrorAction SilentlyContinue $i = 1 $dscContent = '' @@ -735,28 +680,27 @@ function Export-TargetResource { Write-Host "`r`n" -NoNewline } - foreach ($request in $Script:exportedInstances) + foreach ($config in $Script:exportedInstances) { if ($null -ne $Global:M365DSCExportResourceInstancesCount) { $Global:M365DSCExportResourceInstancesCount++ } - $displayedKey = $request.Id + $displayedKey = $config.Id Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline - # Find the Principal Type $principalType = 'User' - $userInfo = Get-MgUser -UserId $request.PrincipalId -ErrorAction SilentlyContinue + $userInfo = Get-MgUser -UserId $config.PrincipalId -ErrorAction SilentlyContinue if ($null -eq $userInfo) { $principalType = 'Group' - $groupInfo = Get-MgGroup -GroupId $request.PrincipalId -ErrorAction SilentlyContinue + $groupInfo = Get-MgGroup -GroupId $config.PrincipalId -ErrorAction SilentlyContinue if ($null -eq $groupInfo) { $principalType = 'ServicePrincipal' - $spnInfo = Get-MgServicePrincipal -ServicePrincipalId $request.PrincipalId -ErrorAction SilentlyContinue + $spnInfo = Get-MgServicePrincipal -ServicePrincipalId $config.PrincipalId -ErrorAction SilentlyContinue if ($null -ne $spnInfo) { $PrincipalValue = $spnInfo.DisplayName @@ -778,12 +722,12 @@ function Export-TargetResource if ($null -ne $PrincipalValue) { - $RoleDefinitionId = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $request.RoleDefinitionId + $RoleDefinitionId = Get-MgBetaRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $config.RoleDefinitionId $params = @{ - Id = $request.Id + Id = $config.Id Principal = $PrincipalValue PrincipalType = $principalType - DirectoryScopeId = $request.DirectoryScopeId + DirectoryScopeId = $config.DirectoryScopeId RoleDefinition = $RoleDefinitionId.DisplayName Ensure = 'Present' Credential = $Credential @@ -794,46 +738,66 @@ function Export-TargetResource ManagedIdentity = $ManagedIdentity.IsPresent AccessTokens = $AccessTokens } + } - $Results = Get-TargetResource @Params + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results - $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` - -Results $Results - try - { - if ($null -ne $results.ScheduleInfo) - { - $Results.ScheduleInfo = Get-M365DSCAzureADEligibilityRequestScheduleInfoAsString -ScheduleInfo $Results.ScheduleInfo + if ($Results.ScheduleInfo) + { + $complexMapping = @( + @{ + Name = 'expiration' + CimInstanceName = 'AADRoleEligibilityScheduleRequestScheduleExpiration' + IsRequired = $False } - } - catch - { - Write-Verbose -Message "Error converting Schedule: $_" - } - if ($Results.TicketInfo) + @{ + Name = 'Recurrence' + CimInstanceName = 'AADRoleEligibilityScheduleRequestScheduleRecurrence' + IsRequired = $False + } + @{ + Name = "range" + CimInstanceName = 'AADRoleEligibilityScheduleRequestScheduleRecurrenceRange' + IsRequired = $False + } + @{ + Name = "pattern" + CimInstanceName = 'AADRoleEligibilityScheduleRequestScheduleRecurrencePattern' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.ScheduleInfo ` + -CIMInstanceName 'AADRoleEligibilityScheduleRequestSchedule' ` + -ComplexTypeMapping $complexMapping + if ($complexTypeStringResult) { - $Results.TicketInfo = Get-M365DSCAzureADEligibilityRequestTicketInfoAsString -TicketInfo $Results.TicketInfo + $Results.ScheduleInfo = $complexTypeStringResult } - $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` - -ConnectionMode $ConnectionMode ` - -ModulePath $PSScriptRoot ` - -Results $Results ` - -Credential $Credential - if ($null -ne $Results.ScheduleInfo) + else { - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` - -ParameterName 'ScheduleInfo' + $Results.Remove('ScheduleInfo') | Out-Null } - if ($null -ne $Results.TicketInfo) + } + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + if ($Results.ScheduleInfo) + { + $isCIMArray = $false + if ($Results.ScheduleInfo.getType().Fullname -like '*[[\]]') { - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` - -ParameterName 'TicketInfo' + $isCIMArray = $true } - - $dscContent += $currentDSCBlock - Save-M365DSCPartialExport -Content $currentDSCBlock ` - -FileName $Global:PartialExportFileName + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'ScheduleInfo' -IsCIMArray:$isCIMArray } + $dscContent += $currentDSCBlock + Save-M365DSCPartialExport -Content $currentDSCBlock ` + -FileName $Global:PartialExportFileName $i++ Write-Host $Global:M365DSCEmojiGreenCheckMark } @@ -841,157 +805,16 @@ function Export-TargetResource } catch { - if ($_.ErrorDetails.Message -like '*The tenant needs an AAD Premium*' -or ` - $_.ErrorDetails.MEssage -like '*[AadPremiumLicenseRequired]*') - { - Write-Host "`r`n $($Global:M365DSCEmojiYellowCircle) Tenant does not meet license requirement to extract this component." - } - else - { - Write-Verbose -Message "Exception: $($_.Exception.Message)" - Write-Host $Global:M365DSCEmojiRedX - New-M365DSCLogEntry -Message 'Error during Export:' ` - -Exception $_ ` - -Source $($MyInvocation.MyCommand.Source) ` - -TenantId $TenantId ` - -Credential $Credential - } - - return '' - } -} - -function Get-M365DSCAzureADEligibilityRequestTicketInfoAsString -{ - [CmdletBinding()] - [OutputType([System.String])] - param( - [Parameter(Mandatory = $true)] - [System.Collections.Hashtable] - $TicketInfo - ) - - if ($TicketInfo.TicketNumber -or $TicketInfo.TicketSystem) - { - $StringContent = "MSFT_AADRoleEligibilityScheduleRequestTicketInfo {`r`n" - $StringContent += " ticketNumber = '$($TicketInfo.TicketNumber)'`r`n" - $StringContent += " ticketSystem = '$($TicketInfo.TicketSystem)'`r`n" - $StringContent += " }`r`n" - return $StringContent - } - else - { - return $null - } -} - -function Get-M365DSCAzureADEligibilityRequestScheduleInfoAsString -{ - [CmdletBinding()] - [OutputType([System.String])] - param( - [Parameter(Mandatory = $true)] - [System.Collections.Hashtable] - $ScheduleInfo - ) - - $Found = $false - $StringContent = "MSFT_AADRoleEligibilityScheduleRequestSchedule {`r`n" - if ($ScheduleInfo.StartDateTime) - { - $StringContent += " startDateTime = '$($ScheduleInfo.StartDateTime)'`r`n" - } - if ($ScheduleInfo.Expiration.Duration -or $ScheduleInfo.Expiration.EndDateTime -or $ScheduleInfo.Expiration.Type) - { - $Found = $true - $StringContent += " expiration = MSFT_AADRoleEligibilityScheduleRequestScheduleExpiration`r`n" - $StringContent += " {`r`n" - if ($ScheduleInfo.Expiration.Duration) - { - $StringContent += " duration = '$($ScheduleInfo.Expiration.Duration)'`r`n" - } - if ($ScheduleInfo.Expiration.EndDateTime) - { - $StringContent += " endDateTime = '$($ScheduleInfo.Expiration.EndDateTime.ToString())'`r`n" - } - if ($ScheduleInfo.Expiration.Type) - { - $StringContent += " type = '$($ScheduleInfo.Expiration.Type)'`r`n" - } - $StringContent += " }`r`n" - } - if ($ScheduleInfo.Recurrence.Pattern.DayOfMonth -or $ScheduleInfo.Recurrence.Pattern.DaysOfWeek -or ` - $ScheduleInfo.Recurrence.Pattern.firstDayOfWeek -or $ScheduleInfo.Recurrence.Pattern.Index -or ` - $ScheduleInfo.Recurrence.Pattern.Interval -or $ScheduleInfo.Recurrence.Pattern.Month -or ` - $ScheduleInfo.Recurrence.Pattern.Type -or $ScheduleInfo.Recurrence.Range.EndDate -or $ScheduleInfo.Recurrence.Range.numberOfOccurrences -or ` - $ScheduleInfo.Recurrence.Range.recurrenceTimeZone -or $ScheduleInfo.Recurrence.Range.startDate -or ` - $ScheduleInfo.Recurrence.Range.type) - { - $StringContent += " recurrence = MSFT_AADRoleEligibilityScheduleRequestScheduleRecurrence`r`n" - $StringContent += " {`r`n" + Write-Host $Global:M365DSCEmojiRedX - if ($ScheduleInfo.Recurrence.Pattern.DayOfMonth -or $ScheduleInfo.Recurrence.Pattern.DaysOfWeek -or ` - $ScheduleInfo.Recurrence.Pattern.firstDayOfWeek -or $ScheduleInfo.Recurrence.Pattern.Index -or ` - $ScheduleInfo.Recurrence.Pattern.Interval -or $ScheduleInfo.Recurrence.Pattern.Month -or ` - $ScheduleInfo.Recurrence.Pattern.Type) - { - $Found = $true - $StringContent += " pattern = MSFT_AADRoleEligibilityScheduleRequestScheduleRecurrencePattern`r`n" - $StringContent += " {`r`n" - if ($ScheduleInfo.Recurrence.Pattern.DayOfMonth) - { - $StringContent += " dayOfMonth = $($ScheduleInfo.Recurrence.Pattern.DayOfMonth)`r`n" - } - if ($ScheduleInfo.Recurrence.Pattern.DaysOfWeek) - { - $StringContent += " daysOfWeek = @($($ScheduleInfo.Recurrence.Pattern.DaysOfWeek -join ','))`r`n" - } - if ($ScheduleInfo.Recurrence.Pattern.firstDayOfWeek) - { - $StringContent += " firstDayOfWeek = '$($ScheduleInfo.Recurrence.Pattern.firstDayOfWeek)'`r`n" - } - if ($ScheduleInfo.Recurrence.Pattern.Index) - { - $StringContent += " index = '$($ScheduleInfo.Recurrence.Pattern.Index)'`r`n" - } - if ($ScheduleInfo.Recurrence.Pattern.Interval) - { - $StringContent += " interval = $($ScheduleInfo.Recurrence.Pattern.Interval.ToString())`r`n" - } - if ($ScheduleInfo.Recurrence.Pattern.Month) - { - $StringContent += " month = $($ScheduleInfo.Recurrence.Pattern.Month.ToString())`r`n" - } - if ($ScheduleInfo.Recurrence.Pattern.Type) - { - $StringContent += " type = '$($ScheduleInfo.Recurrence.Pattern.Type)'`r`n" - } - $StringContent += " }`r`n" - } - if ($ScheduleInfo.Recurrence.Range.EndDate -or $ScheduleInfo.Recurrence.Range.numberOfOccurrences -or ` - $ScheduleInfo.Recurrence.Range.recurrenceTimeZone -or $ScheduleInfo.Recurrence.Range.startDate -or ` - $ScheduleInfo.Recurrence.Range.type) - { - $Found = $true - $StringContent += " range = MSFT_AADRoleEligibilityScheduleRequestScheduleRange`r`n" - $StringContent += " {`r`n" - $StringContent += " endDate = '$($ScheduleInfo.Recurrence.Range.EndDate)'`r`n" - $StringContent += " numberOfOccurrences = $($ScheduleInfo.Recurrence.Range.numberOfOccurrences)`r`n" - $StringContent += " recurrenceTimeZone = '$($ScheduleInfo.Recurrence.Range.recurrenceTimeZone)'`r`n" - $StringContent += " startDate = '$($ScheduleInfo.Recurrence.Range.startDate)'`r`n" - $StringContent += " type = '$($ScheduleInfo.Recurrence.Range.type)'`r`n" - $StringContent += " }`r`n" - } - - $StringContent += " }`r`n" - } - $StringContent += " }`r`n" + New-M365DSCLogEntry -Message 'Error during Export:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential - if ($Found) - { - return $StringContent + return '' } - return $null } Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.schema.mof index 7e81112f40..788c55c594 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADRoleEligibilityScheduleRequest/MSFT_AADRoleEligibilityScheduleRequest.schema.mof @@ -43,13 +43,6 @@ class MSFT_AADRoleEligibilityScheduleRequestSchedule [Write, Description("When the eligible or active assignment becomes active.")] String startDateTime; }; -[ClassVersion("1.0.0")] -class MSFT_AADRoleEligibilityScheduleRequestTicketInfo -{ - [Write, Description("The ticket number.")] String ticketNumber; - [Write, Description("The description of the ticket system.")] String ticketSystem; -}; - [ClassVersion("1.0.0.0"), FriendlyName("AADRoleEligibilityScheduleRequest")] class MSFT_AADRoleEligibilityScheduleRequest : OMI_BaseResource { @@ -63,7 +56,6 @@ class MSFT_AADRoleEligibilityScheduleRequest : OMI_BaseResource [Write, Description("Determines whether the call is a validation or an actual call. Only set this property if you want to check whether an activation is subject to additional rules like MFA before actually submitting the request.")] Boolean IsValidationOnly; [Write, Description("A message provided by users and administrators when create they create the unifiedRoleEligibilityScheduleRequest object. Optional when action is adminRemove. Whether this property is required or optional is also dependent on the settings for the Azure AD role.")] String Justification; [Write, Description("The period of the role eligibility. Optional when action is adminRemove. The period of eligibility is dependent on the settings of the Azure AD role."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleRequestSchedule")] String ScheduleInfo; - [Write, Description("Ticket details linked to the role eligibility request including details of the ticket number and ticket system."), EmbeddedInstance("MSFT_AADRoleEligibilityScheduleRequestTicketInfo")] String TicketInfo; [Write, Description("Present ensures the instance 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; diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/1-Create.ps1 deleted file mode 100644 index b516274848..0000000000 --- a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/1-Create.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -<# -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 - { - - } -} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/2-Update.ps1 deleted file mode 100644 index b516274848..0000000000 --- a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/2-Update.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -<# -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 - { - - } -} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/3-Remove.ps1 deleted file mode 100644 index b516274848..0000000000 --- a/Modules/Microsoft365DSC/Examples/Resources/AADRoleEligibilitySchedule/3-Remove.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -<# -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 - { - - } -} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilitySchedule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilitySchedule.Tests.ps1 deleted file mode 100644 index 780e0f343d..0000000000 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilitySchedule.Tests.ps1 +++ /dev/null @@ -1,178 +0,0 @@ -[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" - } - - ##TODO - Mock any Remove/Set/New cmdlets - - # 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 should exist but it DOES NOT" -Fixture { - BeforeAll { - $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; - } - - ##TODO - Mock the Get-Cmdlet to return $null - Mock -CommandName Get-Cmdlet -MockWith { - return $null - } - } - It 'Should return Values from the Get method' { - (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' - } - It 'Should return false from the Test method' { - Test-TargetResource @testParams | Should -Be $false - } - - It 'Should create a new instance from the Set method' { - ##TODO - Replace the New-Cmdlet by the appropriate one - Set-TargetResource @testParams - Should -Invoke -CommandName New-Cmdlet -Exactly 1 - } - } - - Context -Name "The instance exists but it SHOULD NOT" -Fixture { - BeforeAll { - $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Absent' - Credential = $Credential; - } - - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { - return @{ - - } - } - } - It 'Should return Values 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 remove the instance from the Set method' { - Set-TargetResource @testParams - ##TODO - Replace the Remove-Cmdlet by the appropriate one - Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 - } - } - - Context -Name "The instance exists and values are already in the desired state" -Fixture { - BeforeAll { - $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; - } - - ##TODO - Mock the Get-Cmdlet to return the desired values - Mock -CommandName Get-Cmdlet -MockWith { - return @{ - - } - } - } - - 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 = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; - } - - ##TODO - Mock the Get-Cmdlet to return a drift - Mock -CommandName Get-Cmdlet -MockWith { - return @{ - - } - } - } - - It 'Should return Values 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 call the Set method' { - Set-TargetResource @testParams - ##TODO - Replace the Update-Cmdlet by the appropriate one - Should -Invoke -CommandName Update-Cmdlet -Exactly 1 - } - } - - Context -Name 'ReverseDSC Tests' -Fixture { - BeforeAll { - $Global:CurrentModeIsExport = $true - $Global:PartialExportFileName = "$(New-Guid).partial.ps1" - $testParams = @{ - Credential = $Credential; - } - - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { - return @{ - - } - } - } - 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 44664407a53c0f8e0319921580481d736bf3fbca Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Thu, 23 Jan 2025 15:57:29 -0500 Subject: [PATCH 3/4] Fixes --- CHANGELOG.md | 145 +++++++++++++++++- ...MSFT_IntuneAppProtectionPolicyAndroid.psm1 | 38 ++++- .../Microsoft365DSC/Modules/M365DSCUtil.psm1 | 13 ++ 3 files changed, 190 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26e967f969..866a1e0f29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,150 @@ # UNRELEASED +* AADRoleEligibilityScheduleRequest + * Fixed overall logic to prevent errors complaining about existing permissions. +* FabricAdminTenantSettings + * Fix titles that have a zero length whitespace character. +* IntuneAppProtectionPolicyAndroid + * Fixes an error retrieving the group id for assignment which resulted + in a 500 error when creating or updating an instance. +* O365SearchAndIntelligenceConfigurations + * Added support for Meeting Insights settings. + * Added support for Service Principal authentication. + +# 1.25.122.1 + +* AADConditionalAccessPolicy + * Fixes CA policy deployment errors when deploying policies based for workload identities. + * Fixed DisableResilienceDefaults result + * Add DisableResilienceDefaults false szenario +* AADDeviceRegistrationPolicy + * Fixes an error when trying to disable AAD join. +* AADGroupsNamingPolicy + * Use correct parameter `DesiredValues` given to `Test-M365DSCParameterState`, + contrary to `EXOTenantAllowBlockListItems` these resources are not affected + but we still should use the correct parameter +* AADRoleSetting + * Fixed issue where missing settings object for a role caused errors. + FIXES [#5602](https://github.com/microsoft/Microsoft365DSC/issues/5602) +* AADServicePrincipal + * FIXES [#5549](https://github.com/microsoft/Microsoft365DSC/issues/5549) +* EXOAvailabilityConfig + * Removed dependency on Microsoft Graph to retrieve user information. +* EXODistributionGroup + * Removed dependency on Microsoft Graph to retrieve user information. +* EXOHostedContentFilterPolicy + * Use correct parameter `DesiredValues` given to `Test-M365DSCParameterState`, + contrary to `EXOTenantAllowBlockListItems` these resources are not affected + but we still should use the correct parameter +* EXOTenantAllowBlockListItems + * Fixed `Test-TargetResource` by using the correct parameter `DesiredValues` + given to `Test-M365DSCParameterState` +* EXOTransportRule + * Fix type of `SenderInRecipientList` in schema +* FabricAdminTenantSettings + * Added support for the AllowGetOneLakeUDK, AllowMountDfCreation, AllowOneLakeUDK, + ArtifactOrgAppPreview properties. + * Fix values that have a zero length whitespace character. +* IntuneAppProtectionPolicyiOS + * Fixes [#5589] https://github.com/microsoft/Microsoft365DSC/issues/5589 +* SCAutoSensitivityLabelPolicy + * Use correct parameter `DesiredValues` given to `Test-M365DSCParameterState`, + contrary to `EXOTenantAllowBlockListItems` these resources are not affected + but we still should use the correct parameter +* SCLabelPolicy + * Use correct parameter `DesiredValues` given to `Test-M365DSCParameterState`, + contrary to `EXOTenantAllowBlockListItems` these resources are not affected + but we still should use the correct parameter +* SCSecurityFilter + * Use correct parameter `DesiredValues` given to `Test-M365DSCParameterState`, + contrary to `EXOTenantAllowBlockListItems` these resources are not affected + but we still should use the correct parameter +* SPOSPOBrowserIdleSignout + * Corrected export types where the schema expected a String, but received a different type + FIXES [#5648](https://github.com/microsoft/Microsoft365DSC/issues/5648) +* SPOSharingSettings + * Corrected export types where the schema expected a String, but received a different type + FIXES [#5648](https://github.com/microsoft/Microsoft365DSC/issues/5648) +* M365DSCReport + * Fix missing delimiter when called without the parameter. + FIXES [#5634](https://github.com/microsoft/Microsoft365DSC/issues/5634) + * Add configuration validation to inform about comparisons against empty or invalid configurations. + FIXES [#5658](https://github.com/microsoft/Microsoft365DSC/issues/5658) +* M365DSCTelemetryEngine + * Report LCM details only if running as administrator. +* M365DSCUtil + * In `Test-M365DSCParameterState` try to replace the line endings before + making the comparison otherwise it may fail as it did for a few resources + FIXES [#5648](https://github.com/microsoft/Microsoft365DSC/issues/5648) +* MISC + * Export Performance Improvements + Implements the changes described in [#5615](https://github.com/microsoft/Microsoft365DSC/issues/5615) + Improved resource caching behavior across Intune resources. + +# 1.25.115.1 + +* AADAuthenticationRequirement + * Filtered guests from the export, to prevent errors during export + FIXES [#5625](https://github.com/microsoft/Microsoft365DSC/issues/5625) +* AADCustomAuthenticationExtension + * Fixes an issue extracting instances due to the OrganizationName being null. +* AADGroup + * Only get Members & GroupAsMembers when a static group is defined. +* AADGroupEligibilitySchedule + * New resource for Privileged Identity Management (PIM) for Groups +* AADNamingLocationPolicy + * Improved logging and fixed issue that caused creation of duplicate + locations with same name. +* EXOSmtpDaneInbound + * Initial release +* IntuneAccountProtectionLocalAdministratorPasswordSolutionPolicy + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneAccountProtectionPolicyWindows10 + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneAntivirusPolicyLinux + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneAntivirusPolicyMacOS + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneAntivirusPolicyWindows10SettingCatalog + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneAppAndBrowserIsolationPolicyWindows10 + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneDeviceControlPolicyWindows10 + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneDiskEncryptionMacOS + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneDiskEncryptionWindows10 + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneEndpointDetectionAndResponsePolicyLinux + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneEndpointDetectionAndResponsePolicyMacOS + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneEndpointDetectionAndResponsePolicyWindows10 + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneFirewallPolicyWindows10 + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneSettingCatalogASRRulesPolicyWindows10 + * Fixed creation of policy while it was found by name, now it updates existing policies correctly. +* IntuneVPNConfigurationPolicyAndroidWork + * Initial release +* M365DSCRuleEvaluation + * Making RuleDefinition a key of the resource to allow multiple + rules for the same resource type to be provided in the same config. + * Adding a new RuleName parameter to make it easier to understand + the results in the event logs. +* TeamsGroupPolicyAssignment + * FIXES [[#5527](https://github.com/microsoft/Microsoft365DSC/issues/5527)] +* DEPENDENCIES + * Updated Microsoft.PowerApps.Administration.PowerShell to version 2.0.203. + * Updated MSCloudLoginAssistant to version 1.1.34. + +# 1.25.108.1 + +* AADAuthenticationRequirement + * Changed Export logic to extract instances from all users. +* AADConditionalAccessPolicy + * Add disableResilienceDefaults * AADOrganizationCertificateBasedAuthConfiguration * Fixed the primary key of the resource. FIXES [#5523](https://github.com/microsoft/Microsoft365DSC/issues/5523) @@ -12,7 +156,6 @@ * MISC * DEFENDER * Added support for the UseBasicParsing paramter for REST calls. - * AADApplication * Added support for Oauth2PermissionScopes. * Fixes comparison issue for permissions. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneAppProtectionPolicyAndroid/MSFT_IntuneAppProtectionPolicyAndroid.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneAppProtectionPolicyAndroid/MSFT_IntuneAppProtectionPolicyAndroid.psm1 index f9b14c892d..0bf28a5056 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneAppProtectionPolicyAndroid/MSFT_IntuneAppProtectionPolicyAndroid.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneAppProtectionPolicyAndroid/MSFT_IntuneAppProtectionPolicyAndroid.psm1 @@ -285,16 +285,24 @@ function Get-TargetResource { foreach ($assignment in $policyInfo.Assignments) { + $groupInfo = Get-MgGroup -GroupId $assignment.Target.AdditionalProperties.groupId -ErrorAction SilentlyContinue + $groupValue = $assignment.Target.AdditionalProperties.groupId + if ($null -ne $groupInfo) + { + $groupValue = $groupInfo.DisplayName + } + switch ($assignment.Target.AdditionalProperties.'@odata.type') { '#microsoft.graph.groupAssignmentTarget' { - $assignmentsArray += $assignment.Target.AdditionalProperties.groupId + + $assignmentsArray += $groupValue } '#microsoft.graph.exclusionGroupAssignmentTarget' { - $exclusionArray += $assignment.Target.AdditionalProperties.groupId + $exclusionArray += $groupValue } } } @@ -350,6 +358,7 @@ function Get-TargetResource } catch { + Write-Verbose -Message $_ if ($_.Exception.Message -eq 'Multiple Policies with same displayname identified - Module currently only functions with unique names') { throw $_ @@ -651,7 +660,13 @@ function Set-TargetResource $PSBoundParameters.Assignments | ForEach-Object { if ($_ -ne $null) { - $assignmentsArray += set-JSONstring -id $_ -type 'Assignments' + $groupInfo = Get-MgGroup -Filter "DisplayName eq '$_'" + $idValue = $_ + if (-not [System.String]::IsNullOrEmpty($groupInfo)) + { + $idValue = $groupInfo.Id + } + $assignmentsArray += set-JSONstring -id $idValue -type 'Assignments' } } $configstring += ( 'Assignments' + ":`r`n" + ($PSBoundParameters.Assignments | Out-String) + "`r`n" ) @@ -710,7 +725,7 @@ function Set-TargetResource $setParams.add('AndroidManagedAppProtectionId', $currentPolicy.id) Update-MgBetaDeviceAppManagementAndroidManagedAppProtection @setParams - Write-Verbose -Message 'Setting Group Assignments...' + Write-Verbose -Message "Setting Group Assignments with values:`r`n$(ConvertTo-Json $assignmentsArray -Depth 10)" Set-MgBetaDeviceAppManagementTargetedManagedAppConfiguration -TargetedManagedAppConfigurationId $setParams.AndroidManagedAppProtectionId -Assignments $assignmentsArray } @@ -1016,7 +1031,20 @@ function Test-TargetResource # handle complex parameters - manually for now if ($PSBoundParameters.keys -contains 'Assignments' ) { - $targetvalues.add('Assignments', $psboundparameters.Assignments) + $assignmentsValue = @() + foreach ($assignment in $Assignments) + { + $groupInfo = Get-MgGroup -GroupId $assignment -ErrorAction SilentlyContinue + if ($null -ne $groupInfo) + { + $assignmentsValue += $groupInfo.DisplayName + } + else + { + $assignmentsValue += $assignment + } + } + $targetvalues.add('Assignments', $assignmentsValue) } Write-Verbose -Message 'Starting Exluded Groups Check' diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index 76e01728c3..d29075f36e 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -662,6 +662,19 @@ function Test-M365DSCParameterState if ($null -ne $IncludedDrifts -and $IncludedDrifts.Keys.Count -gt 0) { $DriftedParameters = $IncludedDrifts + foreach ($existingDrift in $IncludedDrifts) + { + $propertyName = $existingDrift.Keys[0] + $value = $existingDrift."$propertyName" + $start = $value.IndexOf('') + $currentValue = $value.Substring(0, $start).Replace('', '') + $desiredValue = $value.Substring($start+15, ($value.Length)-($start+15)).Replace('', '').Replace('', '') + $DriftObject.DriftInfo.Add($propertyName, @{ + PropertyName = $propertyName + CurrentValue = $currentValue + DesiredValue = $desiredValue + }) + } $returnValue = $false } From 14c9156e6413008213fa96eb77ac899721d23b10 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Fri, 24 Jan 2025 08:52:08 -0500 Subject: [PATCH 4/4] Fixing Unit Tests --- ...5DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 index 6f84a2bcd2..a39b4805ab 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADRoleEligibilityScheduleRequest.Tests.ps1 @@ -48,14 +48,16 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName Get-MgBetaRoleManagementDirectoryRoleDefinition -MockWith { return @{ - DisplayName = 'Teams Communications Administrator' - Id = '12345' + DisplayName = 'Teams Communications Administrator' + Id = '12345' + DirectoryScopeId = '/' } } Mock -CommandName Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -MockWith { return @{ Id = '12345-12345-12345-12345-12345' RoleDefinitionId = "12345" + DirectoryScopeId = '/' } } @@ -89,6 +91,11 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -MockWith { return $null } + + + Mock -CommandName Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule -MockWith { + return $null + } } It 'Should return Values from the Get method' { (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' @@ -263,7 +270,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { It 'Should call the Set to Update the instance' { Set-TargetResource @testParams - Should -Invoke -CommandName Get-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -Exactly 1 + Should -Invoke -CommandName New-MgBetaRoleManagementDirectoryRoleEligibilityScheduleRequest -Exactly 1 } } Context -Name 'ReverseDSC Tests' -Fixture {