diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d136196b5..c5eeb43cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ * Initial release. * AADLifecycleWorkflowSettings * Initial release. +* AADServicePrincipal + * Adding Delegated Permission Classification Property * ADOPermissionGroupSettings * Initial release. * EXOATPBuiltInProtectionRule diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 index a32fd0d623..5640892910 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 @@ -32,6 +32,10 @@ function Get-TargetResource [System.Boolean] $AppRoleAssignmentRequired, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $DelegatedPermissionClassifications, + [Parameter()] [System.String] $ErrorUrl, @@ -212,32 +216,43 @@ function Get-TargetResource } } + [Array]$complexDelegatedPermissionClassifications = @() + $permissionClassifications = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals(appId='$AppId')/delegatedPermissionClassifications" -Method Get + foreach ($permissionClassification in $permissionClassifications.Value){ + $hashtable = @{ + classification = $permissionClassification.Classification + permissionName = $permissionClassification.permissionName + } + $complexDelegatedPermissionClassifications += $hashtable + } + $result = @{ - AppId = $AADServicePrincipal.AppId - AppRoleAssignedTo = $AppRoleAssignedToValues - ObjectID = $AADServicePrincipal.Id - DisplayName = $AADServicePrincipal.DisplayName - AlternativeNames = $AADServicePrincipal.AlternativeNames - AccountEnabled = [boolean]$AADServicePrincipal.AccountEnabled - AppRoleAssignmentRequired = $AADServicePrincipal.AppRoleAssignmentRequired - ErrorUrl = $AADServicePrincipal.ErrorUrl - Homepage = $AADServicePrincipal.Homepage - LogoutUrl = $AADServicePrincipal.LogoutUrl - Owners = $ownersValues - PublisherName = $AADServicePrincipal.PublisherName - ReplyURLs = $AADServicePrincipal.ReplyURLs - SamlMetadataURL = $AADServicePrincipal.SamlMetadataURL - ServicePrincipalNames = $AADServicePrincipal.ServicePrincipalNames - ServicePrincipalType = $AADServicePrincipal.ServicePrincipalType - Tags = $AADServicePrincipal.Tags - Ensure = 'Present' - Credential = $Credential - ApplicationId = $ApplicationId - ApplicationSecret = $ApplicationSecret - TenantId = $TenantId - CertificateThumbprint = $CertificateThumbprint - Managedidentity = $ManagedIdentity.IsPresent - AccessTokens = $AccessTokens + AppId = $AADServicePrincipal.AppId + AppRoleAssignedTo = $AppRoleAssignedToValues + ObjectID = $AADServicePrincipal.Id + DisplayName = $AADServicePrincipal.DisplayName + AlternativeNames = $AADServicePrincipal.AlternativeNames + AccountEnabled = [boolean]$AADServicePrincipal.AccountEnabled + AppRoleAssignmentRequired = $AADServicePrincipal.AppRoleAssignmentRequired + DelegatedPermissionClassifications = [Array]$complexDelegatedPermissionClassifications + ErrorUrl = $AADServicePrincipal.ErrorUrl + Homepage = $AADServicePrincipal.Homepage + LogoutUrl = $AADServicePrincipal.LogoutUrl + Owners = $ownersValues + PublisherName = $AADServicePrincipal.PublisherName + ReplyURLs = $AADServicePrincipal.ReplyURLs + SamlMetadataURL = $AADServicePrincipal.SamlMetadataURL + ServicePrincipalNames = $AADServicePrincipal.ServicePrincipalNames + ServicePrincipalType = $AADServicePrincipal.ServicePrincipalType + Tags = $AADServicePrincipal.Tags + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + ApplicationSecret = $ApplicationSecret + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + Managedidentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens } Write-Verbose -Message "Get-TargetResource Result: `n $(Convert-M365DscHashtableToString -Hashtable $result)" return $result @@ -289,6 +304,10 @@ function Set-TargetResource [System.Boolean] $AppRoleAssignmentRequired, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $DelegatedPermissionClassifications, + [Parameter()] [System.String] $ErrorUrl, @@ -398,6 +417,8 @@ function Set-TargetResource { $currentParameters.AppRoleAssignedTo = $AppRoleAssignedToValue } + # removing Delegated permission classifications from this new call, as adding below separately + $currentParameters.Remove('DelegatedPermissionClassifications') | Out-Null $ObjectGuid = [System.Guid]::empty if (-not [System.Guid]::TryParse($AppId, [System.Management.Automation.PSReference]$ObjectGuid)) { @@ -419,6 +440,17 @@ function Set-TargetResource Write-Verbose -Message "Adding new owner {$owner}" $newOwner = New-MgServicePrincipalOwnerByRef -ServicePrincipalId $newSP.Id -BodyParameter $body } + + #adding delegated permissions classifications + if($null -ne $DelegatedPermissionClassifications){ + foreach ($permissionClassification in $DelegatedPermissionClassifications){ + $params = @{ + classification = $permissionClassification.Classification + permissionName = $permissionClassification.permissionName + } + Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals(appId='$($currentParameters.AppId)')/delegatedPermissionClassifications" -Method Post -Body $params + } + } } # ServicePrincipal should exist and will be configured to desired state elseif ($Ensure -eq 'Present' -and $currentAADServicePrincipal.Ensure -eq 'Present') @@ -434,6 +466,7 @@ function Set-TargetResource Write-Verbose -Message "ServicePrincipalID: $($currentAADServicePrincipal.ObjectID)" $currentParameters.Remove('AppRoleAssignedTo') | Out-Null $currentParameters.Remove('Owners') | Out-Null + $currentParameters.Remove('DelegatedPermissionClassifications') | Out-Null Update-MgServicePrincipal -ServicePrincipalId $currentAADServicePrincipal.ObjectID @currentParameters if ($AppRoleAssignedTo) @@ -546,6 +579,26 @@ function Set-TargetResource -DirectoryObjectId $userInfo.Id | Out-Null } } + + Write-Verbose -Message "Checking if DelegatedPermissionClassifications need to be updated..." + + if ($null -ne $DelegatedPermissionClassifications) + { + # removing old perm classifications + $permissionClassificationList = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals(appId='$($currentParameters.AppId)')/delegatedPermissionClassifications" -Method Get + foreach($permissionClassification in $permissionClassificationList.Value){ + Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals(appId='$($currentParameters.AppId)')/delegatedPermissionClassifications/$($permissionClassification.Id)" -Method Delete + } + + # adding new perm classifications + foreach ($permissionClassification in $DelegatedPermissionClassifications){ + $params = @{ + classification = $permissionClassification.Classification + permissionName = $permissionClassification.permissionName + } + Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals(appId='$($currentParameters.AppId)')/delegatedPermissionClassifications" -Method Post -Body $params + } + } } # ServicePrincipal exists but should not elseif ($Ensure -eq 'Absent' -and $currentAADServicePrincipal.Ensure -eq 'Present') @@ -589,6 +642,10 @@ function Test-TargetResource [System.Boolean] $AppRoleAssignmentRequired, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $DelegatedPermissionClassifications, + [Parameter()] [System.String] $ErrorUrl, @@ -677,21 +734,48 @@ function Test-TargetResource Write-Verbose -Message 'Testing configuration of Azure AD ServicePrincipal' + $testTargetResource = $true $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + + #Compare Cim instances + foreach ($key in $PSBoundParameters.Keys) + { + $source = $PSBoundParameters.$key + $target = $CurrentValues.$key + if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') + { + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-not $testResult) + { + $testTargetResource = $false + } + else { + $ValuesToCheck.Remove($key) | Out-Null + } + } + } Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" - $ValuesToCheck = $PSBoundParameters - $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys ` + -IncludedDrifts $driftedParams + + if(-not $TestResult) + { + $testTargetResource = $false + } - Write-Verbose -Message "Test-TargetResource returned $TestResult" + Write-Verbose -Message "Test-TargetResource returned $testTargetResource" - return $TestResult + return $testTargetResource } function Export-TargetResource @@ -785,6 +869,10 @@ function Export-TargetResource { $Results.AppRoleAssignedTo = Get-M365DSCAzureADServicePrincipalAssignmentAsString -Assignments $Results.AppRoleAssignedTo } + if ($Results.DelegatedPermissionClassifications.Count -gt 0) + { + $Results.DelegatedPermissionClassifications = Get-M365DSCAzureADServicePrincipalDelegatedPermissionClassifications -PermissionClassifications $Results.DelegatedPermissionClassifications + } $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` -ConnectionMode $ConnectionMode ` -ModulePath $PSScriptRoot ` @@ -795,6 +883,11 @@ function Export-TargetResource $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` -ParameterName 'AppRoleAssignedTo' } + if ($null -ne $Results.DelegatedPermissionClassifications) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'DelegatedPermissionClassifications' + } $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName @@ -841,4 +934,26 @@ function Get-M365DSCAzureADServicePrincipalAssignmentAsString return $StringContent } +function Get-M365DSCAzureADServicePrincipalDelegatedPermissionClassifications +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory = $true)] + [System.Collections.ArrayList] + $PermissionClassifications + ) + + $StringContent = "@(`r`n" + foreach ($permissionClassification in $PermissionClassifications) + { + $StringContent += " MSFT_AADServicePrincipalDelegatedPermissionClassification {`r`n" + $StringContent += " Classification = '" + $PermissionClassification.Classification + "'`r`n" + $StringContent += " PermissionName = '" + $PermissionClassification.PermissionName + "'`r`n" + $StringContent += " }`r`n" + } + $StringContent += ' )' + return $StringContent +} + Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof index 3d4c507c12..717b2e569f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof @@ -5,6 +5,13 @@ class MSFT_AADServicePrincipalRoleAssignment [Write, Description("Unique identity representing the principal.")] String Identity; }; +[ClassVersion("1.0.0")] +class MSFT_AADServicePrincipalDelegatedPermissionClassification +{ + [Write, Description("Classification of the delegated permission"), ValueMap{"low", "medium", "high"}, Values{"low", "medium", "high"}] String Classification; + [Write, Description("Name of the permission")] String PermissionName; +}; + [ClassVersion("1.0.0.0"), FriendlyName("AADServicePrincipal")] class MSFT_AADServicePrincipal : OMI_BaseResource { @@ -25,6 +32,8 @@ class MSFT_AADServicePrincipal : OMI_BaseResource [Write, Description("Specifies an array of service principal names. Based on the identifierURIs collection, plus the application's appId property, these URIs are used to reference an application's service principal.")] String ServicePrincipalNames[]; [Write, Description("The type of the service principal.")] String ServicePrincipalType; [Write, Description("Tags linked to this service principal.Note that if you intend for this service principal to show up in the All Applications list in the admin portal, you need to set this value to {WindowsAzureActiveDirectoryIntegratedApp}")] String Tags[]; + [Write, Description("The permission classifications for delegated permissions exposed by the app that this service principal represents."), EmbeddedInstance("MSFT_AADServicePrincipalDelegatedPermissionClassification")] String DelegatedPermissionClassifications[]; + [Write, Description("Specify if the Azure AD App should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [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;