Functions/Public/Add-MsGApplicationAPIPermission.ps1
|
function Add-MsGApplicationAPIPermission { <# .SYNOPSIS Adds specified API permission to application. .DESCRIPTION This function adds specified API permissions to an app registration. Both application and delegated permissions can be assigned for any resource application. .PARAMETER DisplayName Display Name of the app registration. .PARAMETER AppId App/Client Id of the app registration. .PARAMETER ObjectId Object Id of the app registration. .PARAMETER Permission List of permissions to add to the app registration. .PARAMETER Type Type of permissions to add to the app registration. .PARAMETER AdminConsent Specifies whether admin consent is provided to the permissions. .PARAMETER ResourceAppId App/Client Id of the resource application that the permissions are assigned from. .PARAMETER Force Bypasses the confirmation check. .PARAMETER Headers Authentication Headers to connect to Microsoft Graph. .PARAMETER Jwt Jwt to connect to Microsoft Graph. .EXAMPLE PS> Add-MsGApplicationAPIPermission -DisplayName TestApp-01 -Permission User.Read.All -Type Application -AdminConsent -Force This adds the User.Read.All Application API Permission for Microsoft Graph to the TestApp-01 app registration, allowing it to read user data in the tenant. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Name')] param( [parameter(Mandatory = $true, ParameterSetName = 'Name', Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][string]$DisplayName, [parameter(Mandatory = $true, ParameterSetName = 'AppId', Position = 1)][string]$AppId, [parameter(Mandatory = $true, ParameterSetName = 'ObjId', Position = 2)][string]$ObjectId, [parameter(Mandatory = $true, Position = 3)][string[]]$Permission, [parameter(Mandatory = $false, Position = 4)][ValidateSet('Application', 'Delegated')][string]$Type = 'Application', [parameter(Mandatory = $false, Position = 5)][switch]$AdminConsent, [parameter(Mandatory = $false)][string]$ResourceAppId = '00000003-0000-0000-c000-000000000000', [parameter(Mandatory = $false)][switch]$Force, [parameter(Mandatory = $false)][hashtable]$Headers, [parameter(Mandatory = $false)][string]$Jwt ) BEGIN { # Session Checks if ([string]::IsNullOrEmpty($Headers) -and [string]::IsNullOrEmpty($Jwt)) { if ($null -ne $myGraphToken) { Write-Verbose "Setting Jwt to Global: $myGraphToken" $Jwt = $myGraphToken } } if ([string]::IsNullOrEmpty($Headers) -and -not([string]::IsNullOrEmpty($Jwt))) { $Headers = @{ 'Authorization' = 'Bearer {0}' -f $Jwt 'Content-Type' = 'application/json' } } if ([string]::IsNullOrEmpty($Headers)) { $errorMessage = '{0} : Unable to run command. Headers are empty.' -f $MyInvocation.MyCommand.Name throw $errorMessage } # end Session Checks # Retrieve Resource Permissions try { $resourceApp = Get-MsGServicePrincipal -AppId $ResourceAppId -Headers $Headers -ErrorAction 'Stop' $applicationPermissionTable = $resourceApp | Select-Object -ExpandProperty 'appRoles' | Group-Object 'value' -AsHashTable -AsString $delegatedPermissionTable = $resourceApp | Select-Object -ExpandProperty 'oauth2PermissionScopes' | Group-Object 'value' -AsHashTable -AsString } catch { $errorMessage = Get-MsGErrorMessage $_ $errorException = '{0} : Unable to retrieve resource tables. Error: {1}' -f $MyInvocation.MyCommand.Name, $errorMessage throw $errorException } # end region } PROCESS { # Retrieve Application from Entra ID try { switch ($PSCmdlet.ParameterSetName) { "Name" { $appEndpoint = "applications?`$filter=displayName eq '{0}'" -f $DisplayName } "AppId" { $appEndpoint = "applications?`$filter=appId eq '{0}'" -f $AppId } "ObjId" { $appEndpoint = "applications/{0}" -f $ObjectId } } $application = Invoke-MsGRequest -Method Get -Endpoint $appEndpoint -Headers $Headers -ErrorAction 'Stop' if ([string]::IsNullOrEmpty($application)) { $errorMessage = 'Application not found.' throw $errorMessage } } catch { $errorMessage = Get-MsGErrorMessage $_ $errorException = '{0} : Unable to retrieve application from Entra ID. Error: {1}' -f $MyInvocation.MyCommand.Name, $errorMessage throw $errorException } # end region # Retrieve resource access. $appRequiredResourceAccess = $application.requiredResourceAccess $updatedRequiredResourceAccess = New-Object -TypeName System.Collections.Generic.List[psobject] # If the app already has resources tied to it, make updates. if (-not([string]::IsNullOrEmpty($appRequiredResourceAccess))) { foreach ($requiredResourceAccess in $appRequiredResourceAccess) { if ($requiredResourceAccess.resourceAppId -eq $resourceApp.appId) { # Specific resource is to be added. Iterate through permissions. $ResourceAccess = $requiredResourceAccess.resourceAccess foreach ($SpecifiedPermission in $Permission) { $PermissionId = $null $PermissionType = $null $NewResourceAccess = $null # Retrieve Properties switch ($Type) { "Application" { $PermissionId = $applicationPermissionTable[$SpecifiedPermission].Id $PermissionType = 'Role' } "Delegated" { $PermissionId = $delegatedPermissionTable[$SpecifiedPermission].Id $PermissionType = 'Scope' } } # Check Permission Id if ([string]::IsNullOrEmpty($PermissionId)) { $warningMessage = 'Unable to retrieve the id for the {0} permission. This will not be added to the application.' -f $SpecifiedPermission Write-Warning $warningMessage continue } # Add new permission to resource access object $NewResourceAccess = [pscustomobject]@{ 'id' = $PermissionId 'type' = $PermissionType } $ResourceAccess += $NewResourceAccess } # Add to required resource access. Group resourceAccess to remove duplicates. $newRequiredResourceAccess = [PSCustomObject]@{ 'resourceAppId' = $requiredResourceAccess.resourceAppId 'resourceAccess' = $ResourceAccess | Group-Object Id, Type | ForEach-Object { $_.Group[0] } } $updatedRequiredResourceAccess.Add($newRequiredResourceAccess) } else { # Specific resource is not being changed. Adding to update body and continuing forward. $updatedRequiredResourceAccess.Add($requiredResourceAccess) } } } else { # Otherwise will need to add the new required resources manually. $ResourceAccess = New-Object -TypeName System.Collections.Generic.List[psobject] foreach ($SpecifiedPermission in $Permission) { $PermissionId = $null $PermissionType = $null $NewResourceAccess = $null # Retrieve Properties switch ($Type) { "Application" { $PermissionId = $applicationPermissionTable[$SpecifiedPermission].Id $PermissionType = 'Role' } "Delegated" { $PermissionId = $delegatedPermissionTable[$SpecifiedPermission].Id $PermissionType = 'Scope' } } # Check Permission Id if ([string]::IsNullOrEmpty($PermissionId)) { $warningMessage = 'Unable to retrieve the id for the {0} permission. This will not be added to the application.' -f $SpecifiedPermission Write-Warning $warningMessage continue } # Add new permission to resource access object $NewResourceAccess = [pscustomobject]@{ 'id' = $PermissionId 'type' = $PermissionType } $ResourceAccess.Add($NewResourceAccess) } $newRequiredResourceAccess = [PSCustomObject]@{ 'resourceAppId' = $resourceApp.appId 'resourceAccess' = [psobject[]]($ResourceAccess | Group-Object Id, Type | ForEach-Object { $_.Group[0] }) } $updatedRequiredResourceAccess.Add($newRequiredResourceAccess) } # end region # Upload new resource access. try { $endpoint = 'applications/{0}' -f $application.id $body = @{ 'requiredResourceAccess' = $updatedRequiredResourceAccess } Invoke-MsGRequest -Method Patch -Endpoint $endpoint -Body $body -Headers $Headers -ErrorAction 'Stop' > $null } catch { $errorMessage = Get-MsGErrorMessage $_ $errorException = '{0} : Unable to add the {1} API permissions for the following to the application in Entra ID: {2}. Error: {3}' -f $MyInvocation.MyCommand.Name, $Type, ($Permission -join ', '), $errorMessage throw $errorException } # end region # Admin Consent Actions if ($AdminConsent) { # Retrieve Service Principal try { $servicePrincipal = Get-MsGServicePrincipal -AppId $application.appId -Headers $Headers -ErrorAction 'Stop' } catch { $errorMessage = Get-MsGErrorMessage $_ $errorException = '{0} : Unable to retrieve service principal from Entra ID. Error: {1}' -f $MyInvocation.MyCommand.Name, $errorMessage throw $errorException } # end region # Apply admin consent if ($Force.IsPresent -or ($PSCmdlet.ShouldContinue("Provide admin consent to the following permissions on the '$($servicePrincipal.displayName)' app: $($Permission -join ', ').", "Are you sure you want to proceed?"))) { try { Add-MsGServicePrincipalAPIPermission -ObjectId $servicePrincipal.id -Permission $Permission -Type $Type -ResourceAppId $resourceApp.appId -Force -Headers $Headers -ErrorAction 'Stop' } catch { $errorMessage = Get-MsGErrorMessage $_ $errorException = '{0} : Unable to consent to permissions in Entra ID. Error: {1}' -f $MyInvocation.MyCommand.Name, $errorMessage throw $errorException } } # end region } # end region } } |