public/set-scanPermissions.ps1
Function set-scanPermissions{ <# Author = "Jos Lieben (jos@lieben.nu)" CompanyName = "Lieben Consultancy" Copyright = "https://www.lieben.nu/liebensraum/commercial-use/" Parameters: -appId: the appId of the application (SPN or managed identity) you want to assign permissions to -appName: the name of the application (SPN or managed identity) you want to assign permissions to. If it doesn't exist, it will be created as application + SPN -switchToSPNAuth: use this if you intend to immediate use the newly created SPN on this machine to run a scan, this will force re-auth and will configure the relevant settings with set-M365PermissionConfig #> Param( [string]$appId, [string]$appName, [Switch]$switchToSPNAuth ) $global:octo.userConfig.LCClientId = "2886cc8e-5190-4fb4-9525-61c9b83aa70e" #use onboarding appId if(!$appId -and !$appName){ Throw "You need to provide either an appId or an appName" } #ensure we have an onboarding token, which can in turn provision a new or configure an existing SPN to run scans try{ $global:octo.LCRefreshToken = get-AuthorizationCode -clientId $global:octo.userConfig.LCClientId -skipVersionCheck $global:octo.userConfig.authMode = "Delegated" $global:octo.connection = "Connected" }catch{ Write-Error $_ -ErrorAction Continue Throw "Failed to authorize for onboarding, cannot continue" } if(!$appId){ $spn = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals?`$Filter=displayName eq '$appName'" -Method GET -ComplexFilter }else{ $spn = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals?`$filter=appId eq '$appId'" -Method GET -ComplexFilter } if(!$spn -and $appName){ Write-LogMessage -message "$appName not detected, creating..." $desiredState = @{ "displayName" = $appName } try { $app = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/applications" -Body ($desiredState | ConvertTo-Json) -Method POST Write-LogMessage -message "$appName created, waiting 10 seconds..." Start-Sleep -s 10 $spn = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals?`$Filter=appId eq '$($app.appId)'" -Method GET if(!$spn){ $desiredState = @{ "appId" = $app.appId } try { $spn = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals" -Body ($desiredState | ConvertTo-Json) -Method POST Write-LogMessage -message "SPN added to $($app.displayName), waiting 10 seconds..." Start-Sleep -s 10 } catch { Throw $_ } } } catch { Throw $_ } } Write-LogMessage -message "SPN $($spn.displayName) detected, checking permissions..." $appRoles = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals/$($spn.id)/appRoleAssignments" -Method GET $requiredRoles = @( @{ "resource" = "00000003-0000-0ff1-ce00-000000000000" #Sharepoint Online "id" = "678536fe-1083-478a-9c59-b99265e6b0d3" #Sites.FullControl.All } @{ "resource" = "00000002-0000-0ff1-ce00-000000000000" #Exchange Online "id" = "dc50a0fb-09a3-484d-be87-e023b12c6440" #Exchange.ManageAsApp } @{ "resource" = "00000002-0000-0ff1-ce00-000000000000" #Exchange Online "id" = "dc890d15-9560-4a4c-9b7f-a736ec74ec40" #full_access_as_app } @{ "resource" = "00000003-0000-0000-c000-000000000000" #Graph API "id" = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" #Directory.Read.All } @{ "resource" = "00000003-0000-0000-c000-000000000000" #Graph API "id" = "c74fd47d-ed3c-45c3-9a9e-b8676de685d2" #EntitlementManagement.Read.All } @{ "resource" = "00000003-0000-0000-c000-000000000000" #Graph API "id" = "fee28b28-e1f3-4841-818e-2704dc62245f" #RoleEligibilitySchedule.ReadWrite.Directory } @{ "resource" = "00000003-0000-0000-c000-000000000000" #Graph API "id" = "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8" #RoleManagement.ReadWrite.Directory } @{ "resource" = "00000003-0000-0000-c000-000000000000" #Graph API "id" = "a82116e5-55eb-4c41-a434-62fe8a61c773" #Sites.FullControl.All } @{ "resource" = "00000003-0000-0000-c000-000000000000" #Graph API "id" = "a9e09520-8ed4-4cde-838e-4fdea192c227" #CloudPC.Read.All } ) foreach ($role in $requiredRoles) { $existingPermission = $null; $existingPermission = $appRoles | Where-Object { $_.appRoleId -eq $role.id} if (!$existingPermission) { try { $targetSpn = $null; $targetSpn = (New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals(appId='$($role.resource)')" -Method GET -NoRetry) }catch { $targetSpn = $null } if (!$targetSpn) { Write-LogMessage -message "Required SPN $($role.resource) not detected, creating..." $desiredState = @{ "appId" = $role.resource } try { $targetSpn = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals" -Body ($desiredState | ConvertTo-Json) -Method POST Write-LogMessage -message "SPN registered, waiting 10 seconds..." Start-Sleep -s 10 } catch { Write-Error $_ -ErrorAction Continue $targetSpn = $null } }else { Write-LogMessage -message "Detected required SPN $($role.resource)" } $body = @{ principalId = $spn.Id resourceId = $targetSpn.id appRoleId = $role.id } try { Write-LogMessage -message "Adding approle $($role.id)..." New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals/$($targetSpn.id)/appRoleAssignments" -Body ($body | ConvertTo-Json -Depth 15) -Method POST Write-LogMessage -message "Added approle $($role.id) :)" }catch { Write-Error $_ -ErrorAction Continue } } } $gaRole = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/roleManagement/directory/roleDefinitions?`$filter=DisplayName eq 'Global Administrator'&`$select=rolePermissions" -Method GET if (!$gaRole) { Throw "Global admin role not found!" } $userRoles = New-GraphQuery -Uri "$($global:octo.graphUrl)/v1.0/servicePrincipals/$($spn.Id)/transitiveMemberOf" -Method GET | Where-Object { $_.'@odata.type' -eq "#microsoft.graph.directoryRole" } if (!$userRoles -or $userRoles.roleTemplateId -notcontains $gaRole.id) { Write-LogMessage -message "assigning GA role..." $desiredState = @{ '@odata.type' = "#microsoft.graph.unifiedRoleAssignment" roleDefinitionId = $gaRole.id principalId = $spn.Id directoryScopeId = "/" } $null = New-GraphQuery -Uri "$($global:octo.graphUrl)/beta/roleManagement/directory/roleAssignments" -Body ($desiredState | ConvertTo-Json) -Method POST Write-LogMessage -message "GA role assigned" }else { Write-LogMessage -message "SPN role is already configured correctly" } try{ new-SpnAuthCert -tenantId $spn.appOwnerOrganizationId -importToEntraAppId $app.Id }catch{ Write-Error $_ -ErrorAction Continue Throw "Cannot continue, exiting..." } #"logout" so we can re-authenticate with the new SPN $global:octo.connection = "Pending" if($switchToSPNAuth){ set-M365PermissionsConfig -authMode ServicePrincipal -LCTenantId $spn.appOwnerOrganizationId -LCClientId $spn.appId connect-M365 -ServicePrincipal } } |