deploy/scripts/aad-register.ps1 (792 lines of code) (raw):

<# .SYNOPSIS Registers required applications. .DESCRIPTION Registers the required applications in AAD and returns an object containing the information. .PARAMETER Name The Name prefix under which to register the applications. .PARAMETER TenantId The Azure Active Directory tenant to use. .PARAMETER Context A previously created az context (optional). .PARAMETER Credentials Credentials to use to log in (optional). .PARAMETER ReplyUrl A reply url to add to the web application (optional). #> param( [Parameter(Mandatory = $true)] [string] $Name, [object] $Context, [string] $TenantId, [string] $Output, [string] $ReplyUrl, [string] $EnvironmentName = "AzureCloud" ) <#.Description Perform login - uses profile file if exists - returns account #> Function Select-Context() { [OutputType([Microsoft.Azure.Commands.Profile.Models.Core.PSAzureContext])] Param( [string] $environmentName ) $rootDir = Get-RootFolder $script:ScriptDir $contextFile = Join-Path $rootDir ".user" if ((Test-Path $contextFile) -and [string]::IsNullOrEmpty($script:TenantId)) { # Migrate .user file into root (next to .env) if (!(Test-Path $contextFile)) { $oldFile = Join-Path $script:ScriptDir ".user" if (Test-Path $oldFile) { Move-Item -Path $oldFile -Destination $contextFile } } $azProfile = Import-AzContext -Path $contextFile $context = $azProfile.Context if ($context) { Write-Host "Using saved context (Account $($context.Account), Tenant $($context.Tenant.Id))" return $context } } $tenantIdArg = @{} if (![string]::IsNullOrEmpty($script:tenantId)) { $tenantIdArg = @{ TenantId = $script:tenantId } } # Login and get context try { $azProfile = Connect-AzAccount ` -Environment $environmentName @tenantIdArg -AuthScope AadGraph ` -WarningAction Stop $reply = Read-Host -Prompt "Save credentials in .user file? [y/n]" if ($reply -match "[yY]") { Save-AzContext -Path $contextFile -Profile $connection } $context = $azProfile.Context Write-Host "Using context (Account $($context.Account), Tenant $($context.Tenant.Id))" return $context } catch { try { # for the case where prompting web browser is not applicable, we will # prompt for DeviceAuthentication login. $azProfile = Connect-AzAccount -AuthScope AadGraph ` -UseDeviceAuthentication ` -Environment $environmentName @tenantIdArg ` -ErrorAction Stop $reply = Read-Host -Prompt "Save credentials in .user file? [y/n]" if ($reply -match "[yY]") { Save-AzContext -Path $contextFile -Profile $connection } $context = $azProfile.Context Write-Host "Using context (Account $($context.Account), Tenant $($context.Tenant.Id))" return $context } catch{ throw "The login to the Azure account was not successful." } } } <#.Description find the top most folder with solution in it #> Function Get-RootFolder() { param( $startDir ) $cur = $startDir while (![string]::IsNullOrEmpty($cur)) { if (Test-Path -Path (Join-Path $cur "Industrial-IoT.sln") -PathType Leaf) { return $cur } $cur = Split-Path $cur } return $startDir } <#.Description Grant consent to to the app with id = $azureAppId #> Function Add-AdminConsentGrant() { Param( [string] $azureAppId ) $context = $script:Context if (!$context) { throw "Not authorized to give admin consent for $($azureAppId)." } $url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$($azureAppId)/Consent?onBehalfOfAll=true" for ($i = 0; $i -lt 10; $i++) { # Try 10 times * 3 s = 30 s $token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate( ` $context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", ` $null, "74658136-14ec-4630-ad9b-26e160ff0fc6") if (!$token) { throw "Failed to get auth token for $($context.Tenant.Id)." } try { $header = @{ "Authorization" = "Bearer $($token.AccessToken)" "X-Requested-With" = "XMLHttpRequest" "x-ms-client-request-id" = [guid]::NewGuid() "x-ms-correlation-id" = [guid]::NewGuid() } Invoke-RestMethod -Uri $url -Method POST -Headers $header # Success return } catch { Write-Verbose "$($_.Exception.Message) at $($url) - Retrying..." Start-Sleep -s 3 } } throw "Failed to grant consent for $($azureAppId)." } <#.Description Create an application key See https://www.sabin.io/blog/adding-an-azure-active-directory-application-and-key-using-powershell/ #> Function CreateAppKey([DateTime] $fromDate, [double] $durationInMonths) { $key = New-Object Microsoft.Graph.PowerShell.Models.MicrosoftGraphPasswordCredential $key.StartDateTime = $fromDate $key.EndDateTime = $fromDate.AddMonths($durationInMonths) $key.KeyId = (New-Guid).ToString() $key.DisplayName = "app secret" return $key } <#.Description Adds the requiredAccesses (expressed as a pipe separated string) to the requiredAccess structure The exposed permissions are in the $exposedPermissions collection, and the type of permission (Scope | Role) is described in $permissionType #> Function AddResourcePermission( $requiredAccess, $exposedPermissions, [string]$requiredAccesses, [string]$permissionType) { foreach($permission in $requiredAccesses.Trim().Split("|")) { foreach($exposedPermission in $exposedPermissions) { if ($exposedPermission.Value -eq $permission) { $resourceAccess = New-Object Microsoft.Graph.PowerShell.Models.MicrosoftGraphResourceAccess $resourceAccess.Type = $permissionType # Scope = Delegated permissions | Role = Application permissions $resourceAccess.Id = $exposedPermission.Id # Read directory data $requiredAccess.ResourceAccess += $resourceAccess } } } } <#.Description Example: GetRequiredPermissions "Microsoft Graph" "Graph.Read|User.Read" See also: http://stackoverflow.com/questions/42164581/how-to-configure-a-new-azure-ad-application-through-powershell #> Function GetRequiredPermissions( [string] $applicationDisplayName, [string] $requiredDelegatedPermissions, [string] $requiredApplicationPermissions, $servicePrincipal) { # If we are passed the service principal we use it directly, otherwise we find it # from the display name (which might not be unique) if ($servicePrincipal) { $sp = $servicePrincipal } else { $sp = Get-MgServicePrincipal -Filter "DisplayName eq '$applicationDisplayName'" } $appid = $sp.AppId $requiredAccess = New-Object Microsoft.Graph.PowerShell.Models.MicrosoftGraphRequiredResourceAccess $requiredAccess.ResourceAppId = $appid $requiredAccess.ResourceAccess = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphResourceAccess] # $sp.Oauth2Permissions | Select Id,AdminConsentDisplayName,Value: To see the list # of all the Delegated permissions for the application: if ($requiredDelegatedPermissions) { AddResourcePermission $requiredAccess -exposedPermissions $sp.Oauth2PermissionScopes ` -requiredAccesses $requiredDelegatedPermissions -permissionType "Scope" } # $sp.AppRoles | Select Id,AdminConsentDisplayName,Value: To see the list of all the # Application permissions for the application if ($requiredApplicationPermissions) { AddResourcePermission $requiredAccess -exposedPermissions $sp.AppRoles ` -requiredAccesses $requiredApplicationPermissions -permissionType "Role" } return $requiredAccess } <#.Description This function creates a new Azure AD scope (OAuth2Permission) with default and provided values #> Function CreateScope( [string] $value, [string] $userConsentDisplayName, [string] $userConsentDescription, [string] $adminConsentDisplayName, [string] $adminConsentDescription) { $scope = New-Object Microsoft.Graph.PowerShell.Models.MicrosoftGraphPermissionScope $scope.Id = New-Guid $scope.Value = $value $scope.UserConsentDisplayName = $userConsentDisplayName $scope.UserConsentDescription = $userConsentDescription $scope.AdminConsentDisplayName = $adminConsentDisplayName $scope.AdminConsentDescription = $adminConsentDescription $scope.IsEnabled = $true $scope.Type = "User" return $scope } <#.Description This function creates a new Azure AD AppRole with default and provided values #> Function CreateAppRole( [string] $types, [string] $name, [string] $value, [string] $description) { $appRole = New-Object Microsoft.Graph.PowerShell.Models.MicrosoftGraphAppRole $appRole.AllowedMemberTypes = New-Object System.Collections.Generic.List[string] $typesArr = $types.Split(',') foreach($type in $typesArr) { $appRole.AllowedMemberTypes += $type; } $appRole.DisplayName = $name $appRole.Id = New-Guid $appRole.IsEnabled = $true $appRole.Description = $description $appRole.Value = $value; return $appRole } <#.Description This function takes a string as input and creates an instance of an Optional claim object #> Function CreateOptionalClaim([string] $name) { <#.Description This function creates a new Azure AD optional claims with default and provided values #> $appClaim = New-Object ` Microsoft.Graph.PowerShell.Models.MicrosoftGraphOptionalClaim $appClaim.AdditionalProperties = New-Object System.Collections.Generic.List[string] $appClaim.Source = $null $appClaim.Essential = $false $appClaim.Name = $name return $appClaim } <#.Description This function takes a string as input and creates an instance of an preauthorized application #> Function CreatePreAuthorizedApplication([string] $appId, [string[]] $delegatedPermissionIds) { <#.Description This function creates a new preauthorized application #> $application = New-Object ` Microsoft.Graph.PowerShell.Models.MicrosoftGraphPreAuthorizedApplication $application.AppId = $appId $application.DelegatedPermissionIds = $delegatedPermissionIds return $application } <#.Description Get configuration object for service, web and client applications #> Function Connect-MicrosoftGraph() { param( [Microsoft.Azure.Commands.Profile.Models.Core.PSAzureContext] $context ) $tenantId = $context.Tenant.Id if (![string]::IsNullOrEmpty($script:TenantId)) { $tenantId = $script:TenantId } try { $token = Get-AzAccessToken -ResourceTypeName MSGraph if ($token) { try { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] $secureToken = ConvertTo-SecureString -AsPlainText -Force -String $token.Token Connect-MgGraph -AccessToken $secureToken | Out-Null } catch { # Fall back to Connect-MgGraph 1.x behavior and pass access token as plain text Connect-MgGraph -AccessToken $token.Token | Out-Null } return } } catch { if ($script:interactive) { throw } Write-Warning $_.Exception.Message } Connect-MgGraph -Scopes "User.Read.All Organization.Read.All Application.Write.All" ` -Environment $context.Environment.Name ` -TenantId $tenantId | Out-Null } <#.Description Get configuration object for service, web and client applications #> Function ConfigureApplications() { param( [string] $applicationName, [string] $accessToken ) $context = Get-MgContext # Get the user running the script $currentUserPrincipalName = $context.Account if (!$currentUserPrincipalName) { $currentUserPrincipalName = $script:Context.Account.Id } $tenantId = $context.TenantId if (!$tenantId) { $tenantId = $script:Context.Tenant.Id } $authority = $script:Context.Environment.ActiveDirectoryAuthority # get the tenant we signed in to $tenant = Get-MgOrganization # $tenantDisplayName = $tenant.DisplayName $defaultTenant = ($tenant.VerifiedDomains | Where-Object { $_.IsDefault -eq $True }) $tenantName = $defaultTenant.Name if ($currentUserPrincipalName) { # Try to get current user try { try { $user = Get-MgUser -Filter "UserPrincipalName eq '$($currentUserPrincipalName)'" -ErrorAction Stop } catch { $user = (Get-MgUser -Search "UserPrincipalName:$currentUserPrincipalName" -ErrorAction Stop)[0] } } catch { Write-Warning "Getting user principal for $($creds.Account.Id) failed due to $($_.Exception.Message)." } if (!$user) { # Handle guests $accountId = $currentUserPrincipalName.Replace("@", "_") $user = (Get-MgUser -Filter "startswith(userPrincipalName, '$($accountId)')") } } # Get or create native client application $clientDisplayName = $applicationName + "-client" $clientAadApplication = Get-MgApplication -Filter "DisplayName eq '$clientDisplayName'" if (!$clientAadApplication) { $clientAadApplication = New-MgApplication -DisplayName $clientDisplayName Write-Host "Created new client application '$($clientDisplayName)' in Tenant '$($tenantName)'." } else { Write-Host "Updating client application '$($clientDisplayName)' in Tenant '$($tenantName)'." } $currentAppId = $clientAadApplication.AppId $currentAppObjectId = $clientAadApplication.Id Update-MgApplication -ApplicationId $currentAppObjectId -PublicClient ` @{ RedirectUris = @( "urn:ietf:wg:oauth:2.0:oob" "https://localhost" "http://localhost" ) } ` -SignInAudience AzureADMyOrg -IsFallbackPublicClient Write-Host " Added redirect uris." $owner = Get-MgApplicationOwner -ApplicationId $currentAppObjectId if ($user -and (-not $owner)) { try { New-MgApplicationOwnerByRef -ApplicationId $currentAppObjectId -BodyParameter ` @{"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($user.Id)"} Write-Host " Added '$($user.UserPrincipalName)' as owner." } catch { Write-Warning "Adding $($user.UserPrincipalName) as owner failed due to $($_.Exception.Message)." } } # create the service principal of the newly created application $currentServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$($currentAppId)'" if (!$currentServicePrincipal) { $currentServicePrincipal = New-MgServicePrincipal -AppId $currentAppId ` -Tags { WindowsAzureActiveDirectoryIntegratedApp } Write-Host " Added new service principal." } # # Create web application # $webDisplayName = $applicationName + "-web" $webAadApplication = Get-MgApplication -Filter "DisplayName eq '$webDisplayName'" if (!$webAadApplication) { $webAadApplication = New-MgApplication -DisplayName $webDisplayName Write-Host "Created new web client application '$($webDisplayName)' in Tenant '$($tenantName)'." } else { Write-Host "Updating web client application '$($webDisplayName)' in Tenant '$($tenantName)'." } $currentAppId = $webAadApplication.AppId $currentAppObjectId = $webAadApplication.Id $replyUrls = @("https://localhost:44321/signin-oidc") if (![string]::IsNullOrEmpty($script:ReplyUrl)) { $replyUrls = @($script:ReplyUrl, "urn:ietf:wg:oauth:2.0:oob") } Update-MgApplication -ApplicationId $currentAppObjectId -Web ` @{ RedirectUris = $replyUrls ImplicitGrantSettings = @{ EnableAccessTokenIssuance = $True EnableIdTokenIssuance = $True } } ` -SignInAudience AzureADMyOrg Write-Host " Added redirect uris." $owner = Get-MgApplicationOwner -ApplicationId $currentAppObjectId if ($user -and (-not $owner)) { try { New-MgApplicationOwnerByRef -ApplicationId $currentAppObjectId -BodyParameter ` @{"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($user.Id)"} Write-Host " Added '$($user.UserPrincipalName)' as owner." } catch { Write-Warning "Adding $($user.UserPrincipalName) as owner failed due to $($_.Exception.Message)." } } # Get a 24 months application key for the web application $fromDate = [DateTime]::Now; $key = CreateAppKey -fromDate $fromDate -durationInMonths 24 $webAppSecret = Add-MgApplicationPassword -ApplicationId $currentAppObjectId -PasswordCredential $key Write-Host " Added a secret." # create the service principal of the newly created application $currentServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$($currentAppId)'" if (!$currentServicePrincipal) { $currentServicePrincipal = New-MgServicePrincipal -AppId $currentAppId ` -Tags { WindowsAzureActiveDirectoryIntegratedApp } Write-Host " Added new service principal." } # # Create service application # $serviceDisplayName = $applicationName + "-service" $serviceAadApplication = Get-MgApplication -Filter "DisplayName eq '$serviceDisplayName'" if (!$serviceAadApplication) { $serviceAadApplication = New-MgApplication -DisplayName $serviceDisplayName Write-Host "Created new service application '$($serviceDisplayName)' in Tenant '$($tenantName)'." } else { Write-Host "Updating service application '$($serviceDisplayName)' in Tenant '$($tenantName)'." } $currentAppId = $serviceAadApplication.AppId $currentAppObjectId = $serviceAadApplication.Id $tenantName = (Get-MgApplication -ApplicationId $currentAppObjectId).PublisherDomain Update-MgApplication -ApplicationId $currentAppObjectId -Web ` @{ HomePageUrl = "https://localhost" } ` -Api ` @{ RequestedAccessTokenVersion = 2 } ` -SignInAudience AzureADMyOrg -IdentifierUris ` @( "api://$currentAppId" "https://$tenantName/$serviceDisplayName" ) Write-Host " Added identifiers." $owner = Get-MgApplicationOwner -ApplicationId $currentAppObjectId if ($user -and (-not $owner)) { try { New-MgApplicationOwnerByRef -ApplicationId $currentAppObjectId -BodyParameter ` @{"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($user.Id)"} Write-Host " Added '$($user.UserPrincipalName)' as owner." } catch { Write-Warning "Adding $($user.UserPrincipalName) as owner failed due to $($_.Exception.Message)." } } # Get a 24 months application key for the service application (is this needed?) $fromDate = [DateTime]::Now; $key = CreateAppKey -fromDate $fromDate -durationInMonths 24 $serviceSecret = Add-MgApplicationPassword -ApplicationId $currentAppObjectId -PasswordCredential $key Write-Host " Added a secret." # Add optional Claims $optionalClaims = New-Object Microsoft.Graph.PowerShell.Models.MicrosoftGraphOptionalClaims $optionalClaims.AccessToken = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphOptionalClaim] $optionalClaims.IdToken = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphOptionalClaim] $optionalClaims.Saml2Token = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphOptionalClaim] $newClaim = CreateOptionalClaim -name "idtyp" $optionalClaims.AccessToken += ($newClaim) Update-MgApplication -ApplicationId $currentAppObjectId -OptionalClaims $optionalClaims Write-Host " Added optional claims." # Add app roles and permissions $allAppRoles = $serviceAadApplication.AppRoles if ($allAppRoles.Count -eq 0) { $appRoles = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphAppRole] $newRole = CreateAppRole -current $serviceAadApplication.AppRoles -name "Readers" ` -value "Reader" -types "User" ` -description "Readers have the ability to read entitities." $appRoles.Add($newRole) $newRole = CreateAppRole -current $serviceAadApplication.AppRoles -name "Writers" ` -value "Writer" -types "User" ` -description "Writers have the ability to call and write entities." $appRoles.Add($newRole) $newRole = CreateAppRole -current $serviceAadApplication.AppRoles -name "Administrators" ` -value "Admin" -types "Application,User" ` -description "Admins can access read, write and advanced features." $appRoles.Add($newRole) Update-MgApplication -ApplicationId $currentAppObjectId -AppRoles $appRoles Write-Host " Added api roles." } # add/update scopes $scopes = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphPermissionScope] $existingScopes = $serviceAadApplication.Api.Oauth2PermissionScopes $scope = $existingScopes | Where-Object { $_.Value -eq "User_impersonation" } if ($scope) { $scopes.Add($scope) if ($existingScopes.Count -eq 1) { $existingScopes = @() } # disable the scope # $scope.IsEnabled = $false # Update-MgApplication -ApplicationId $currentAppObjectId -Api @{Oauth2PermissionScopes = @($scopes)} # clear the scope # Update-MgApplication -ApplicationId $currentAppObjectId -Api @{Oauth2PermissionScopes = @()} # $scopes = ` #New-Object System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphPermissionScope] } if ($existingScopes.Count -eq 0) { $scope = CreateScope -value Read ` -userConsentDisplayName "Read entities using Industrial IoT" ` -userConsentDescription "Allow the app to read entities in the Industrial IoT platform." ` -adminConsentDisplayName "Read entities using Industrial IoT" ` -adminConsentDescription "Allow the app to read entities using Industrial IoT" $scopes.Add($scope) $scope = CreateScope -value ReadWrite ` -userConsentDisplayName "Read and Write entities using Industrial IoT" ` -userConsentDescription "Allow the app to read and write entities in the Industrial IoT platform." ` -adminConsentDisplayName "Read and Write entities using Industrial IoT" ` -adminConsentDescription "Allow the app to read and write entities using Industrial IoT" $scopes.Add($scope) Update-MgApplication -ApplicationId $currentAppObjectId ` -Api @{Oauth2PermissionScopes = @($scopes)} Write-Host " Added api scopes." } $knownApplications = New-Object System.Collections.Generic.List[System.String] $knownApplications.Add($clientAadApplication.AppId) $knownApplications.Add($webAadApplication.AppId) $knownApplications.Add("04b07795-8ddb-461a-bbee-02f9e1bf7b46") $knownApplications.Add("872cd9fa-d31f-45e0-9eab-6e460a02d1f1") Update-MgApplication -ApplicationId $currentAppObjectId ` -Api @{KnownClientApplications = @($knownApplications)} Write-Host " Added known applications." # Add 1) Azure CLI and 2) Visual Studio to allow log onto the platform with them as clients $preauthApplications = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphPreAuthorizedApplication] $serviceAadApplication = Get-MgApplication -ApplicationId $currentAppObjectId $existingScopes = $serviceAadApplication.Api.Oauth2PermissionScopes $delegatedPermissionIds = $($existingScopes | Select-Object -ExpandProperty Id) $preauthApplication = CreatePreAuthorizedApplication ` -appId "04b07795-8ddb-461a-bbee-02f9e1bf7b46" -delegatedPermissionIds @($delegatedPermissionIds) $preauthApplications.Add($preauthApplication) $preauthApplication = CreatePreAuthorizedApplication ` -appId "872cd9fa-d31f-45e0-9eab-6e460a02d1f1" -delegatedPermissionIds @($delegatedPermissionIds) $preauthApplications.Add($preauthApplication) Update-MgApplication -ApplicationId $currentAppObjectId ` -Api @{PreAuthorizedApplications = @($preauthApplications)} Write-Host " Added pre-authorized applications." # Add required permissions to graph $requiredResourcesAccess = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphRequiredResourceAccess] $requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" ` -requiredDelegatedPermissions "User.Read" $requiredResourcesAccess.Add($requiredPermissions) Update-MgApplication -ApplicationId $currentAppObjectId ` -RequiredResourceAccess $requiredResourcesAccess # create the service principal of the newly created application $currentServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$($currentAppId)'" if (!$currentServicePrincipal) { $currentServicePrincipal = New-MgServicePrincipal -AppId $currentAppId ` -Tags { WindowsAzureActiveDirectoryIntegratedApp } Write-Host " Added service principal." } # Add current user as Writer, Approver and Administrator for service application if ($user) { $allAppRoles = $currentServicePrincipal.AppRoles $app_role_name = "Readers" $app_role = $allAppRoles | Where-Object { $_.DisplayName -eq $app_role_name } if ($app_role) { New-MgUserAppRoleAssignment -UserId $user.Id -PrincipalId $user.Id ` -ResourceId $currentServicePrincipal.Id -AppRoleID $app_role.Id ` -ErrorAction SilentlyContinue | Out-Null Write-Host " Assigned Reader role to $($user.UserPrincipalName)." } $app_role_name = "Writers" $app_role = $allAppRoles | Where-Object { $_.DisplayName -eq $app_role_name } if ($app_role) { New-MgUserAppRoleAssignment -UserId $user.Id -PrincipalId $user.Id ` -ResourceId $currentServicePrincipal.Id -AppRoleID $app_role.Id ` -ErrorAction SilentlyContinue | Out-Null Write-Host " Assigned Writer role to $($user.UserPrincipalName)." } $app_role_name = "Administrators" $app_role = $allAppRoles | Where-Object { $_.DisplayName -eq $app_role_name } if ($app_role) { New-MgUserAppRoleAssignment -UserId $user.Id -PrincipalId $user.Id ` -ResourceId $currentServicePrincipal.Id -AppRoleID $app_role.Id ` -ErrorAction SilentlyContinue | Out-Null Write-Host " Assigned Admin role to $($user.UserPrincipalName)." } } # # Update client application to add and if possible grant required permissions. # $currentAppId = $clientAadApplication.AppId $currentAppObjectId = $clientAadApplication.Id $requiredResourcesAccess = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphRequiredResourceAccess] #$requiredPermissions = GetRequiredPermissions -applicationDisplayName $serviceDisplayName ` # -requiredDelegatedPermissions "user_impersonation" #$requiredResourcesAccess.Add($requiredPermissions) #$requiredPermissions = GetRequiredPermissions -applicationDisplayName $serviceDisplayName ` # -requiredDelegatedPermissions "Read" #$requiredResourcesAccess.Add($requiredPermissions) $requiredPermissions = GetRequiredPermissions -applicationDisplayName $serviceDisplayName ` -requiredDelegatedPermissions "ReadWrite" $requiredResourcesAccess.Add($requiredPermissions) $requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" ` -requiredDelegatedPermissions "User.Read" $requiredResourcesAccess.Add($requiredPermissions) Update-MgApplication -ApplicationId $currentAppObjectId ` -RequiredResourceAccess $requiredResourcesAccess Write-Host "Client application '$($clientDisplayName)' updated with required resource access." # Grant permissions to client try { Add-AdminConsentGrant -azureAppId $currentAppId | Out-Null Write-Host " Admin consent granted to native client application." } catch { Write-Host $_.Exception.Message Write-Host "You must grant admin consent for application '$($clientDisplayName)' manually inside Azure Active Directory." } # # Update web application to add and if possible grant required permissions. # $currentAppId = $webAadApplication.AppId $currentAppObjectId = $webAadApplication.Id $requiredResourcesAccess = New-Object ` System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphRequiredResourceAccess] #$requiredPermissions = GetRequiredPermissions -applicationDisplayName $serviceDisplayName ` # -requiredDelegatedPermissions "user_impersonation" #$requiredResourcesAccess.Add($requiredPermissions) #$requiredPermissions = GetRequiredPermissions -applicationDisplayName $serviceDisplayName ` # -requiredDelegatedPermissions "Read" #$requiredResourcesAccess.Add($requiredPermissions) # Also require admin as app role to run end to end tests. TODO: Split out into seperate client app $requiredPermissions = GetRequiredPermissions -applicationDisplayName $serviceDisplayName ` -requiredDelegatedPermissions "ReadWrite" -requiredApplicationPermissions "Admin" $requiredResourcesAccess.Add($requiredPermissions) $requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" ` -requiredDelegatedPermissions "User.Read" $requiredResourcesAccess.Add($requiredPermissions) Update-MgApplication -ApplicationId $currentAppObjectId ` -RequiredResourceAccess $requiredResourcesAccess Write-Host "Web application '$($webDisplayName)' updated with required resource access." # Grant permissions to web app try { Add-AdminConsentGrant -azureAppId $webAadApplication.AppId | Out-Null Write-Host " Admin consent granted to web application." } catch { Write-Host $_.Exception.Message Write-Host "You must grant admin consent for application '$($webDisplayName)' manually inside Azure Active Directory." } return [pscustomobject] @{ TenantId = $tenantId Authority = $authority Audience = $serviceAadApplication.IdentifierUris[0].ToString() ServiceId = $serviceAadApplication.AppId ServicePrincipalId = $serviceAadApplication.Id ServiceSecret = $serviceSecret.SecretText ServiceDisplayName = $serviceDisplayName ClientId = $clientAadApplication.AppId ClientPrincipalId = $clientAadApplication.Id ClientDisplayName = $clientDisplayName WebAppId = $webAadApplication.AppId WebAppPrincipalId = $webAadApplication.Id WebAppSecret = $webAppSecret.SecretText WebAppDisplayName = $webDisplayName UserPrincipalId = $user.Id } } #******************************************************************************************************* # Script body #******************************************************************************************************* $ErrorActionPreference = 'Stop' $script:ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path # Import modules Import-Module Az.Accounts try { Import-Module Microsoft.Graph.Authentication -MinimumVersion 2.0.0 Import-Module Microsoft.Graph.Identity.DirectoryManagement -MinimumVersion 2.0.0 Import-Module Microsoft.Graph.Applications -MinimumVersion 2.0.0 Import-Module Microsoft.Graph.Groups -MinimumVersion 2.0.0 Import-Module Microsoft.Graph.Users -MinimumVersion 2.0.0 } catch { $ex = $_ Write-Host Write-Host "An error occurred: $($ex.Exception.Message)" Write-Host Write-Host "Ensure you have installed the Microsoft.Graph cmdlets:" Write-Host "1) Run PowerShell as an administrator" Write-Host "2) In the PowerShell window, type: Install-Module Microsoft.Graph" Write-Host throw $ex } if (!$script:Context) { $script:Context = Select-Context -environmentName $script:EnvironmentName $script:interactive = $true } else { Write-Host "Using passed context (Account $($script:Context.Account), Tenant $($script:Context.Tenant.Id))" $script:interactive = $false } try { Connect-MicrosoftGraph -context $context } catch { Write-Warning "Failed to sign into Microsoft Graph for $($script:Name): $($_.Exception.Message)" return $null } try { $aadConfig = ConfigureApplications -applicationName $script:Name -context $context $aadConfigJson = $aadConfig | ConvertTo-Json if ($isCloudShell) { Write-Host "aadConfig:" Write-Host $aadConfigJson } else { Write-Verbose $aadConfigJson } if ($script:Output) { $aadConfigJson | Out-File $script:Output return } return $aadConfig } catch { Write-Warning "Failed to register applications for $($script:Name): $($_.Exception.Message)" return $null } finally { Disconnect-MgGraph | Out-Null }