183 lines
12 KiB
PowerShell
183 lines
12 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Grants Microsoft Graph permissions for an auditor.
|
|
.DESCRIPTION
|
|
This function grants the specified Microsoft Graph permissions to a user, allowing the user to perform audits. It connects to Microsoft Graph, checks if a service principal exists for the client application, creates it if it does not exist, and then grants the specified permissions. Finally, it assigns the app to the user.
|
|
.PARAMETER UserPrincipalNameForConsent
|
|
The UPN or ID of the user to grant consent for.
|
|
.PARAMETER SkipGraphConnection
|
|
If specified, skips connecting to Microsoft Graph.
|
|
.PARAMETER DoNotDisconnect
|
|
If specified, does not disconnect from Microsoft Graph after granting consent.
|
|
.PARAMETER SkipModuleCheck
|
|
If specified, skips the check for the Microsoft.Graph module.
|
|
.PARAMETER SuppressRevertOutput
|
|
If specified, suppresses the output of the revert commands.
|
|
.EXAMPLE
|
|
Grant-M365SecurityAuditConsent -UserPrincipalNameForConsent user@example.com
|
|
|
|
Grants Microsoft Graph permissions to user@example.com for the client application with the specified Application ID.
|
|
.EXAMPLE
|
|
Grant-M365SecurityAuditConsent -UserPrincipalNameForConsent user@example.com -SkipGraphConnection
|
|
|
|
Grants Microsoft Graph permissions to user@example.com, skipping the connection to Microsoft Graph.
|
|
.NOTES
|
|
This function requires the Microsoft.Graph module version 2.4.0 or higher.
|
|
.LINK
|
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Grant-M365SecurityAuditConsent
|
|
#>
|
|
function Grant-M365SecurityAuditConsent {
|
|
[CmdletBinding(
|
|
SupportsShouldProcess = $true,
|
|
ConfirmImpact = 'High'
|
|
)]
|
|
[OutputType([void])]
|
|
param (
|
|
[Parameter(
|
|
Mandatory = $true,
|
|
Position = 0,
|
|
ValueFromPipeline = $true,
|
|
ValueFromPipelineByPropertyName = $true,
|
|
HelpMessage = 'Specify the UPN of the user to grant consent for.'
|
|
)]
|
|
[ValidatePattern('^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')]
|
|
[String]$UserPrincipalNameForConsent,
|
|
[Parameter(
|
|
Mandatory = $false,
|
|
HelpMessage = 'Skip connecting to Microsoft Graph.'
|
|
)]
|
|
[switch]$SkipGraphConnection,
|
|
[Parameter(
|
|
Mandatory = $false,
|
|
HelpMessage = 'Skip the check for the Microsoft.Graph module.'
|
|
)]
|
|
[switch]$SkipModuleCheck,
|
|
[Parameter(
|
|
Mandatory = $false,
|
|
HelpMessage = 'Suppress the output of the revert commands.'
|
|
)]
|
|
[switch]$SuppressRevertOutput,
|
|
[Parameter(
|
|
Mandatory = $false,
|
|
HelpMessage = 'Do not disconnect from Microsoft Graph after granting consent.'
|
|
)]
|
|
[switch]$DoNotDisconnect
|
|
)
|
|
begin {
|
|
if (!($SkipModuleCheck)) {
|
|
Assert-ModuleAvailability -ModuleName Microsoft.Graph -RequiredVersion "2.4.0"
|
|
}
|
|
# Adjusted from: https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/grant-consent-single-user?pivots=msgraph-powershell
|
|
# Needed: A user account with a Privileged Role Administrator, Application Administrator, or Cloud Application Administrator
|
|
# The app for which consent is being granted.
|
|
$clientAppId = "14d82eec-204b-4c2f-b7e8-296a70dab67e" # Microsoft Graph PowerShell
|
|
# The API to which access will be granted. Microsoft Graph PowerShell makes API
|
|
# requests to the Microsoft Graph API, so we'll use that here.
|
|
$resourceAppId = "00000003-0000-0000-c000-000000000000" # Microsoft Graph API
|
|
# The permissions to grant.
|
|
$permissions = @("Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All")
|
|
# The user on behalf of whom access will be granted. The app will be able to access
|
|
# the API on behalf of this user.
|
|
$userUpnOrId = $UserPrincipalNameForConsent
|
|
}
|
|
process {
|
|
try {
|
|
if (-not $SkipGraphConnection -and $PSCmdlet.ShouldProcess("Scopes: User.ReadBasic.All, Application.ReadWrite.All, DelegatedPermissionGrant.ReadWrite.All, AppRoleAssignment.ReadWrite.All", "Connect-MgGraph")) {
|
|
# Step 0. Connect to Microsoft Graph PowerShell. We need User.ReadBasic.All to get
|
|
# users' IDs, Application.ReadWrite.All to list and create service principals,
|
|
# DelegatedPermissionGrant.ReadWrite.All to create delegated permission grants,
|
|
# and AppRoleAssignment.ReadWrite.All to assign an app role.
|
|
# WARNING: These are high-privilege permissions!
|
|
Write-Host "Connecting to Microsoft Graph with scopes: User.ReadBasic.All, Application.ReadWrite.All, DelegatedPermissionGrant.ReadWrite.All, AppRoleAssignment.ReadWrite.All" -ForegroundColor Yellow
|
|
Connect-MgGraph -Scopes ("User.ReadBasic.All Application.ReadWrite.All " + "DelegatedPermissionGrant.ReadWrite.All " + "AppRoleAssignment.ReadWrite.All") -NoWelcome
|
|
$context = Get-MgContext
|
|
Write-Host "Connected to Microsoft Graph with user: $(($context.Account)) with the authtype `"$($context.AuthType)`" for the `"$($context.Environment)`" environment." -ForegroundColor Green
|
|
}
|
|
}
|
|
catch {
|
|
throw "Connection execution aborted: $_"
|
|
break
|
|
}
|
|
try {
|
|
if ($PSCmdlet.ShouldProcess("Create Microsoft Graph API service princial if not found", "New-MgServicePrincipal")) {
|
|
# Step 1. Check if a service principal exists for the client application.
|
|
# If one doesn't exist, create it.
|
|
$clientSp = Get-MgServicePrincipal -Filter "appId eq '$($clientAppId)'" -ErrorAction SilentlyContinue
|
|
if (-not $clientSp) {
|
|
Write-Host "Client service principal not found. Creating one." -ForegroundColor Yellow
|
|
$clientSp = New-MgServicePrincipal -AppId $clientAppId
|
|
}
|
|
$user = Get-MgUser -UserId $userUpnOrId
|
|
if (!($user)) {
|
|
throw "User with UPN or ID `"$userUpnOrId`" not found."
|
|
}
|
|
Write-Verbose "User: $($user.UserPrincipalName) Found!"
|
|
$resourceSp = Get-MgServicePrincipal -Filter "appId eq '$($resourceAppId)'"
|
|
$scopeToGrant = $permissions -join " "
|
|
$existingGrant = Get-MgOauth2PermissionGrant -Filter "clientId eq '$($clientSp.Id)' and principalId eq '$($user.Id)' and resourceId eq '$($resourceSp.Id)'"
|
|
}
|
|
if (-not $existingGrant -and $PSCmdlet.ShouldProcess("User: $userUpnOrId for Microsoft Graph PowerShell Scopes: $($permissions -join ', ')", "New-MgOauth2PermissionGrant: Granting Consent")) {
|
|
# Step 2. Create a delegated permission that grants the client app access to the
|
|
# API, on behalf of the user.
|
|
$grant = New-MgOauth2PermissionGrant -ResourceId $resourceSp.Id -Scope $scopeToGrant -ClientId $clientSp.Id -ConsentType "Principal" -PrincipalId $user.Id
|
|
Write-Host "Consent granted to user $($user.UserPrincipalName) for Microsoft Graph API with scopes: $((($grant.Scope) -split ' ') -join ', ')" -ForegroundColor Green
|
|
}
|
|
if ($existingGrant -and $PSCmdlet.ShouldProcess("Update existing Microsoft Graph permissions for user $userUpnOrId", "Update-MgOauth2PermissionGrant")) {
|
|
# Step 2. Update the existing permission grant with the new scopes.
|
|
Write-Host "Updating existing permission grant for user $($user.UserPrincipalName)." -ForegroundColor Yellow
|
|
$updatedGrant = Update-MgOauth2PermissionGrant -PermissionGrantId $existingGrant.Id -Scope $scopeToGrant -Confirm:$false
|
|
Write-Host "Updated permission grant with ID $($updatedGrant.Id) for scopes: $scopeToGrant" -ForegroundColor Green
|
|
}
|
|
if ($PSCmdlet.ShouldProcess("Assigning app to user $userUpnOrId", "New-MgServicePrincipalAppRoleAssignedTo")) {
|
|
# Step 3. Assign the app to the user. This ensures that the user can sign in if assignment
|
|
# is required, and ensures that the app shows up under the user's My Apps portal.
|
|
if ($clientSp.AppRoles | Where-Object { $_.AllowedMemberTypes -contains "User" }) {
|
|
Write-Warning "A default app role assignment cannot be created because the client application exposes user-assignable app roles. You must assign the user a specific app role for the app to be listed in the user's My Apps access panel."
|
|
}
|
|
else {
|
|
# The app role ID 00000000-0000-0000-0000-000000000000 is the default app role
|
|
# indicating that the app is assigned to the user, but not for any specific
|
|
# app role.
|
|
$assignment = New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $clientSp.Id -ResourceId $clientSp.Id -PrincipalId $user.Id -AppRoleId "00000000-0000-0000-0000-000000000000"
|
|
# $assignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $assignment.ResourceId -All -WhatIf
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
throw "An error occurred while granting consent:`n$_"
|
|
}
|
|
finally {
|
|
if (!($DoNotDisconnect) -and $PSCmdlet.ShouldProcess("Disconnect from Microsoft Graph", "Disconnect")) {
|
|
# Clean up sessions
|
|
Write-Host "Disconnecting from Microsoft Graph." -ForegroundColor Yellow
|
|
Disconnect-MgGraph | Out-Null
|
|
}
|
|
}
|
|
}
|
|
end {
|
|
if (-not $SuppressRevertOutput -and $PSCmdlet.ShouldProcess("Instructions to undo this change", "Generate Revert Commands")) {
|
|
<#
|
|
# Instructions to revert the changes made by this script
|
|
$resourceAppId = "00000003-0000-0000-c000-000000000000"
|
|
$clientAppId = "14d82eec-204b-4c2f-b7e8-296a70dab67e"
|
|
# Get the user object
|
|
#$user = Get-MgUser -UserId "user@example.com"
|
|
$resourceSp = Get-MgServicePrincipal -Filter "appId eq '$($resourceAppId)'"
|
|
# Get the service principal using $clientAppId
|
|
$clientSp = Get-MgServicePrincipal -Filter "appId eq '$($clientAppId)'"
|
|
$existingGrant = Get-MgOauth2PermissionGrant -Filter "clientId eq '$($clientSp.Id)' and principalId eq '$($user.Id)' and resourceId eq '$($resourceSp.Id)'"
|
|
# Get all app role assignments for the service principal
|
|
$appRoleAssignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $clientSp.Id -All
|
|
# At index of desired user assignment
|
|
Remove-MgServicePrincipalAppRoleAssignedTo -AppRoleAssignmentId $appRoleAssignments[1].Id -ServicePrincipalId $clientSp.Id
|
|
Remove-MgOAuth2PermissionGrant -OAuth2PermissionGrantId $existingGrant.Id
|
|
#>
|
|
Write-Host "App assigned to user $($assignment.PrincipalDisplayName) for $($assignment.ResourceDisplayName) at $($assignment.CreatedDateTime)." -ForegroundColor Green
|
|
Write-Host "If you made a mistake and would like to remove the assignement for `"$($user.UserPrincipalName)`", you can run the following:`n" -ForegroundColor Yellow
|
|
Write-Host "Connect-MgGraph -Scopes (`"User.ReadBasic.All Application.ReadWrite.All `" + `"DelegatedPermissionGrant.ReadWrite.All `" + `"AppRoleAssignment.ReadWrite.All`")" -ForegroundColor Cyan
|
|
Write-Host "Remove-MgServicePrincipalAppRoleAssignedTo -AppRoleAssignmentId `"$($assignment.Id)`" -ServicePrincipalId `"$($assignment.ResourceId)`"" -ForegroundColor Cyan
|
|
Write-Host "Remove-MgOAuth2PermissionGrant -OAuth2PermissionGrantId `"$($grant.Id)`"" -ForegroundColor Cyan
|
|
}
|
|
}
|
|
}
|