Modified Identity Scripts to reflect automation credentials, pass ARM Application ID, and onboard multiple directories to ARM at the same time
This commit is contained in:
Родитель
8cea0d4262
Коммит
1ecc194244
|
@ -126,14 +126,14 @@ function Initialize-AzureRmEnvironment([string]$EnvironmentName, [string] $Resou
|
|||
$directoryTenantId = (New-Object uri(Invoke-RestMethod "$($endpoints.authentication.loginEndpoint.TrimEnd('/'))/$DirectoryTenantName/.well-known/openid-configuration").token_endpoint).AbsolutePath.Split('/')[1]
|
||||
|
||||
$azureEnvironmentParams = @{
|
||||
Name = $EnvironmentName
|
||||
ActiveDirectoryEndpoint = $endpoints.authentication.loginEndpoint.TrimEnd('/') + "/"
|
||||
Name = $EnvironmentName
|
||||
ActiveDirectoryEndpoint = $endpoints.authentication.loginEndpoint.TrimEnd('/') + "/"
|
||||
ActiveDirectoryServiceEndpointResourceId = $endpoints.authentication.audiences[0]
|
||||
AdTenant = $directoryTenantId
|
||||
ResourceManagerEndpoint = $ResourceManagerEndpoint
|
||||
GalleryEndpoint = $endpoints.galleryEndpoint
|
||||
GraphEndpoint = $endpoints.graphEndpoint
|
||||
GraphAudience = $endpoints.graphEndpoint
|
||||
AdTenant = $directoryTenantId
|
||||
ResourceManagerEndpoint = $ResourceManagerEndpoint
|
||||
GalleryEndpoint = $endpoints.galleryEndpoint
|
||||
GraphEndpoint = $endpoints.graphEndpoint
|
||||
GraphAudience = $endpoints.graphEndpoint
|
||||
}
|
||||
|
||||
Remove-AzureRmEnvironment -Name $EnvironmentName -Force -ErrorAction Ignore | Out-Null
|
||||
|
@ -157,30 +157,35 @@ function Resolve-AzureEnvironment([Microsoft.Azure.Commands.Profile.Models.PSAzu
|
|||
return $azureEnvironment
|
||||
}
|
||||
|
||||
function Initialize-AzureRmUserAccount([Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment]$azureEnvironment, [string] $SubscriptionName, [string] $SubscriptionId) {
|
||||
# Prompts the user for interactive login flow
|
||||
$azureAccount = Add-AzureRmAccount -EnvironmentName $azureEnvironment.Name -TenantId $azureEnvironment.AdTenant
|
||||
function Initialize-AzureRmUserAccount([Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment]$azureEnvironment, [string] $SubscriptionName, [string] $SubscriptionId, [pscredential] $AutomationCredential) {
|
||||
|
||||
if ($SubscriptionName) {
|
||||
$params = @{
|
||||
EnvironmentName = $azureEnvironment.Name
|
||||
TenantId = $azureEnvironment.AdTenant
|
||||
}
|
||||
|
||||
if ($AutomationCredential)
|
||||
{
|
||||
$params += @{ Credential = $AutomationCredential }
|
||||
}
|
||||
|
||||
# Prompts the user for interactive login flow if automation credential is not specified
|
||||
$azureAccount = Add-AzureRmAccount @params
|
||||
|
||||
if ($SubscriptionName)
|
||||
{
|
||||
Select-AzureRmSubscription -SubscriptionName $SubscriptionName | Out-Null
|
||||
}
|
||||
elseif ($SubscriptionId) {
|
||||
elseif ($SubscriptionId)
|
||||
{
|
||||
Select-AzureRmSubscription -SubscriptionId $SubscriptionId | Out-Null
|
||||
}
|
||||
|
||||
return $azureAccount
|
||||
}
|
||||
|
||||
function Get-IdentityApplicationData {
|
||||
# Import and read application data
|
||||
Write-Host "Loading identity application data..."
|
||||
$xmlData = [xml](Get-ChildItem -Path C:\EceStore -Recurse -Force -File | Sort Length | Select -Last 1 | Get-Content | Out-String)
|
||||
$xmlIdentityApplications = $xmlData.SelectNodes('//IdentityApplication')
|
||||
|
||||
return $xmlIdentityApplications
|
||||
}
|
||||
|
||||
function Resolve-GraphEnvironment([Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment]$azureEnvironment) {
|
||||
function Resolve-GraphEnvironment([Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment]$azureEnvironment)
|
||||
{
|
||||
$graphEnvironment = switch ($azureEnvironment.ActiveDirectoryAuthority) {
|
||||
'https://login.microsoftonline.com/' { 'AzureCloud' }
|
||||
'https://login.chinacloudapi.cn/' { 'AzureChinaCloud' }
|
||||
|
@ -193,9 +198,20 @@ function Resolve-GraphEnvironment([Microsoft.Azure.Commands.Profile.Models.PSAzu
|
|||
return $graphEnvironment
|
||||
}
|
||||
|
||||
function Get-AzureRmUserRefreshToken([Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment]$azureEnvironment, [string]$directoryTenantId) {
|
||||
# Prompts the user for interactive login flow
|
||||
$azureAccount = Add-AzureRmAccount -EnvironmentName $azureEnvironment.Name -TenantId $directoryTenantId
|
||||
function Get-AzureRmUserRefreshToken([Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment]$azureEnvironment, [string]$directoryTenantId, [pscredential]$AutomationCredential)
|
||||
{
|
||||
$params = @{
|
||||
EnvironmentName = $azureEnvironment.Name
|
||||
TenantId = $directoryTenantId
|
||||
}
|
||||
|
||||
if ($AutomationCredential)
|
||||
{
|
||||
$params += @{ Credential = $AutomationCredential }
|
||||
}
|
||||
|
||||
# Prompts the user for interactive login flow if automation credential is not specified
|
||||
$azureAccount = Add-AzureRmAccount @params
|
||||
|
||||
# Retrieve the refresh token
|
||||
$tokens = [Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache]::DefaultShared.ReadItems()
|
||||
|
@ -203,12 +219,15 @@ function Get-AzureRmUserRefreshToken([Microsoft.Azure.Commands.Profile.Models.PS
|
|||
Where Resource -EQ $azureEnvironment.ActiveDirectoryServiceEndpointResourceId |
|
||||
Where IsMultipleResourceRefreshToken -EQ $true |
|
||||
Where DisplayableId -EQ $azureAccount.Context.Account.Id |
|
||||
Select -ExpandProperty RefreshToken |
|
||||
Sort ExpiresOn |
|
||||
Select -Last 1 -ExpandProperty RefreshToken |
|
||||
ConvertTo-SecureString -AsPlainText -Force
|
||||
|
||||
return $refreshToken
|
||||
}
|
||||
|
||||
# Exposed Functions
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Adds a Guest Directory Tenant to Azure Stack.
|
||||
|
@ -236,10 +255,15 @@ function Register-GuestDirectoryTenantToAzureStack {
|
|||
[ValidateNotNullOrEmpty()]
|
||||
[string] $DirectoryTenantName,
|
||||
|
||||
# The name of the guest Directory Tenant which is to be onboarded.
|
||||
# The names of the guest Directory Tenants which are to be onboarded.
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $GuestDirectoryTenantName,
|
||||
[string[]] $GuestDirectoryTenantName,
|
||||
|
||||
# The location of your Azure Stack deployment.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $Location,
|
||||
|
||||
# The identifier of the Administrator Subscription. If not specified, the script will attempt to use the set default subscription.
|
||||
[ValidateNotNull()]
|
||||
|
@ -253,9 +277,10 @@ function Register-GuestDirectoryTenantToAzureStack {
|
|||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ResourceGroupName = 'system',
|
||||
|
||||
# Optional: A credential used to authenticate with Azure Stack. Must support a non-interactive authentication flow. If not provided, the script will prompt for user credentials.
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $Location = 'local'
|
||||
[ValidateNotNull()]
|
||||
[pscredential] $AutomationCredential = $null
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$VerbosePreference = 'Continue'
|
||||
|
@ -265,134 +290,24 @@ function Register-GuestDirectoryTenantToAzureStack {
|
|||
|
||||
# Initialize the Azure PowerShell module to communicate with Azure Stack. Will prompt user for credentials.
|
||||
$azureEnvironment = Initialize-AzureRmEnvironment -EnvironmentName 'AzureStackAdmin' -ResourceManagerEndpoint $AdminResourceManagerEndpoint -DirectoryTenantName $DirectoryTenantName
|
||||
$azureAccount = Initialize-AzureRmUserAccount -azureEnvironment $azureEnvironment -SubscriptionName $SubscriptionName -SubscriptionId $SubscriptionId
|
||||
$azureAccount = Initialize-AzureRmUserAccount -azureEnvironment $azureEnvironment -SubscriptionName $SubscriptionName -SubscriptionId $SubscriptionId -AutomationCredential $AutomationCredential
|
||||
|
||||
# resolve the guest directory tenant ID from the name
|
||||
$guestDirectoryTenantId = (New-Object uri(Invoke-RestMethod "$($azureEnvironment.ActiveDirectoryAuthority.TrimEnd('/'))/$GuestDirectoryTenantName/.well-known/openid-configuration").token_endpoint).AbsolutePath.Split('/')[1]
|
||||
|
||||
# Add (or update) the new directory tenant to the Azure Stack deployment
|
||||
$params = @{
|
||||
ApiVersion = '2015-11-01' # needed if using "latest" / later version of Azure Powershell
|
||||
ResourceType = "Microsoft.Subscriptions.Admin/directoryTenants"
|
||||
ResourceGroupName = $ResourceGroupName
|
||||
ResourceName = $GuestDirectoryTenantName
|
||||
Location = $Location
|
||||
Properties = @{ tenantId = $guestDirectoryTenantId }
|
||||
}
|
||||
$directoryTenant = New-AzureRmResource @params -Force -Verbose -ErrorAction Stop
|
||||
Write-Verbose -Message "Directory Tenant onboarded: $(ConvertTo-Json $directoryTenant)" -Verbose
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Publishes the list of applications to the Azure Stack ARM.
|
||||
.DESCRIPTION
|
||||
|
||||
.EXAMPLE
|
||||
$adminARMEndpoint = "https://adminmanagement.local.azurestack.external"
|
||||
$azureStackDirectoryTenant = "<homeDirectoryTenant>.onmicrosoft.com"
|
||||
$guestDirectoryTenantToBeOnboarded = "<guestDirectoryTenant>.onmicrosoft.com"
|
||||
|
||||
Publish-AzureStackApplicationsToARM -AdminResourceManagerEndpoint $adminARMEndpoint -DirectoryTenantName $azureStackDirectoryTenant
|
||||
#>
|
||||
function Publish-AzureStackApplicationsToARM {
|
||||
[CmdletBinding()]
|
||||
param
|
||||
(
|
||||
# The endpoint of the Azure Stack Resource Manager service.
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNull()]
|
||||
[ValidateScript( {$_.Scheme -eq [System.Uri]::UriSchemeHttps})]
|
||||
[uri] $AdminResourceManagerEndpoint,
|
||||
|
||||
# The name of the home Directory Tenant in which the Azure Stack Administrator subscription resides.
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $DirectoryTenantName,
|
||||
|
||||
# The identifier of the Administrator Subscription. If not specified, the script will attempt to use the set default subscription.
|
||||
[Parameter()]
|
||||
[ValidateNotNull()]
|
||||
[string] $SubscriptionId = $null,
|
||||
|
||||
# The display name of the Administrator Subscription. If not specified, the script will attempt to use the set default subscription.
|
||||
[Parameter()]
|
||||
[ValidateNotNull()]
|
||||
[string] $SubscriptionName = $null,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ResourceGroupName = 'system',
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $Location = 'local',
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidateScript( {Test-Path -Path $_ -PathType Container -ErrorAction Stop})]
|
||||
[string] $InfrastructureSharePath = '\\SU1FileServer\SU1_Infrastructure_1'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$VerbosePreference = 'Continue'
|
||||
|
||||
# Install-Module AzureRm -RequiredVersion '1.2.8'
|
||||
Import-Module 'AzureRm.Profile' -Force -Verbose:$false 4> $null
|
||||
Write-Warning "This script is intended to work only with the initial TP3 release of Azure Stack and will be deprecated."
|
||||
|
||||
# Initialize the Azure PowerShell module to communicate with Azure Stack. Will prompt user for credentials.
|
||||
$azureEnvironment = Initialize-AzureRmEnvironment -EnvironmentName 'AzureStackAdmin' -ResourceManagerEndpoint $AdminResourceManagerEndpoint -DirectoryTenantName $DirectoryTenantName
|
||||
$azureAccount = Initialize-AzureRmUserAccount -azureEnvironment $azureEnvironment -SubscriptionName $SubscriptionName -SubscriptionId $SubscriptionId
|
||||
|
||||
# Register each identity application for future onboarding.
|
||||
$xmlIdentityApplications = Get-IdentityApplicationData
|
||||
foreach ($xmlIdentityApplication in $xmlIdentityApplications) {
|
||||
$applicationData = Get-Content -Path ($xmlIdentityApplication.ConfigPath.Replace('{Infrastructure}', $InfrastructureSharePath)) | Out-String | ConvertFrom-Json
|
||||
|
||||
# Note - 'Admin' applications do not need to be registered for replication into a new directory tenant
|
||||
if ($xmlIdentityApplication.Name.StartsWith('Admin', [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
Write-Warning "Skipping registration of Admin application: $('{0}.{1}' -f $xmlIdentityApplication.Name, $xmlIdentityApplication.DisplayName)"
|
||||
continue
|
||||
}
|
||||
|
||||
# Advertise any necessary OAuth2PermissionGrants for the application
|
||||
$oauth2PermissionGrants = @()
|
||||
foreach ($applicationFriendlyName in $xmlIdentityApplication.OAuth2PermissionGrants.FirstPartyApplication.FriendlyName) {
|
||||
$oauth2PermissionGrants += [pscustomobject]@{
|
||||
Resource = $applicationData.ApplicationInfo.appId
|
||||
Client = $applicationData.GraphInfo.Applications."$applicationFriendlyName".Id
|
||||
ConsentType = 'AllPrincipals'
|
||||
Scope = 'user_impersonation'
|
||||
}
|
||||
}
|
||||
foreach ($directoryTenantName in $GuestDirectoryTenantName)
|
||||
{
|
||||
# Resolve the guest directory tenant ID from the name
|
||||
$directoryTenantId = (New-Object uri(Invoke-RestMethod "$($azureEnvironment.ActiveDirectoryAuthority.TrimEnd('/'))/$directoryTenantName/.well-known/openid-configuration").token_endpoint).AbsolutePath.Split('/')[1]
|
||||
|
||||
# Add (or update) the new directory tenant to the Azure Stack deployment
|
||||
$params = @{
|
||||
ApiVersion = '2015-11-01' # needed if using "latest" / later version of Azure Powershell
|
||||
ResourceType = "Microsoft.Subscriptions.Providers/applicationRegistrations"
|
||||
ApiVersion = '2015-11-01'
|
||||
ResourceType = "Microsoft.Subscriptions.Admin/directoryTenants"
|
||||
ResourceGroupName = $ResourceGroupName
|
||||
ResourceName = '{0}.{1}' -f $xmlIdentityApplication.Name, $xmlIdentityApplication.DisplayName
|
||||
Location = $Location
|
||||
Properties = @{
|
||||
"objectId" = $applicationData.ApplicationInfo.objectId
|
||||
"appId" = $applicationData.ApplicationInfo.appId
|
||||
"oauth2PermissionGrants" = $oauth2PermissionGrants
|
||||
"directoryRoles" = @()
|
||||
"tags" = @()
|
||||
}
|
||||
ResourceName = $directoryTenantName
|
||||
Location = $Location
|
||||
Properties = @{ tenantId = $directoryTenantId }
|
||||
}
|
||||
|
||||
# Advertise 'ReadDirectoryData' workaround for applications which require this permission of type 'Role'
|
||||
if ($xmlIdentityApplication.AADPermissions.ApplicationPermission.Name -icontains 'ReadDirectoryData') {
|
||||
$params.Properties.directoryRoles = @('Directory Readers')
|
||||
}
|
||||
|
||||
# Advertise any specified tags required for application integration scenarios
|
||||
if ($xmlIdentityApplication.tags) {
|
||||
$params.Properties.tags += $xmlIdentityApplication.tags
|
||||
}
|
||||
|
||||
$registeredApplication = New-AzureRmResource @params -Force -Verbose -ErrorAction Stop
|
||||
Write-Verbose -Message "Identity application registered: $(ConvertTo-Json $registeredApplication)" -Verbose
|
||||
$directoryTenant = New-AzureRmResource @params -Force -Verbose -ErrorAction Stop
|
||||
Write-Verbose -Message "Directory Tenant onboarded: $(ConvertTo-Json $directoryTenant)" -Verbose
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -421,7 +336,17 @@ function Register-AzureStackWithMyDirectoryTenant {
|
|||
# The name of the directory tenant being onboarded.
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $DirectoryTenantName
|
||||
[string] $DirectoryTenantName,
|
||||
|
||||
# Optional: The identifier (GUID) of the Resource Manager application. Pass this parameter to skip the need to complete the guest signup flow via the portal.
|
||||
[Parameter(Mandatory=$false)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ResourceManagerApplicationId,
|
||||
|
||||
# Optional: A credential used to authenticate with Azure Stack. Must support a non-interactive authentication flow. If not provided, the script will prompt for user credentials.
|
||||
[Parameter()]
|
||||
[ValidateNotNull()]
|
||||
[pscredential] $AutomationCredential = $null
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
@ -429,17 +354,28 @@ function Register-AzureStackWithMyDirectoryTenant {
|
|||
|
||||
# Install-Module AzureRm -RequiredVersion '1.2.8'
|
||||
Import-Module 'AzureRm.Profile' -Force -Verbose:$false 4> $null
|
||||
Import-Module "$PSScriptRoot\GraphAPI\GraphAPI.psm1" -Force -Verbose:$false 4> $null
|
||||
Import-Module "$PSScriptRoot\GraphAPI\GraphAPI.psm1" -Force -Verbose:$false 4> $null
|
||||
|
||||
# Initialize the Azure PowerShell module to communicate with the Azure Resource Manager corresponding to their home Graph Service. Will prompt user for credentials.
|
||||
$azureStackEnvironment = Initialize-AzureRmEnvironment -EnvironmentName 'AzureStack' -ResourceManagerEndpoint $TenantResourceManagerEndpoint -DirectoryTenantName $DirectoryTenantName
|
||||
$azureEnvironment = Resolve-AzureEnvironment $azureStackEnvironment
|
||||
$refreshToken = Get-AzureRmUserRefreshToken $azureEnvironment $azureStackEnvironment.AdTenant
|
||||
$refreshToken = Get-AzureRmUserRefreshToken -azureEnvironment $azureEnvironment -directoryTenantId $azureStackEnvironment.AdTenant -AutomationCredential $AutomationCredential
|
||||
|
||||
# Initialize the Graph PowerShell module to communicate with the correct graph service
|
||||
$graphEnvironment = Resolve-GraphEnvironment $azureEnvironment
|
||||
Initialize-GraphEnvironment -Environment $graphEnvironment -DirectoryTenantId $DirectoryTenantName -RefreshToken $refreshToken
|
||||
|
||||
# Initialize the service principal for the Azure Stack Resource Manager application (allows us to acquire a token to ARM). If not specified, the sign-up flow must be completed via the Azure Stack portal first.
|
||||
if ($ResourceManagerApplicationId)
|
||||
{
|
||||
$resourceManagerServicePrincipal = Initialize-GraphApplicationServicePrincipal -ApplicationId $ResourceManagerApplicationId
|
||||
}
|
||||
|
||||
# Authorize the Azure Powershell module to act as a client to call the Azure Stack Resource Manager in the onboarding directory tenant
|
||||
Initialize-GraphOAuth2PermissionGrant -ClientApplicationId (Get-GraphEnvironmentInfo).Applications.PowerShell.Id -ResourceApplicationIdentifierUri $azureStackEnvironment.ActiveDirectoryServiceEndpointResourceId
|
||||
Write-Host "Delaying for 15 seconds to allow the permission for Azure PowerShell to be initialized..."
|
||||
Start-Sleep -Seconds 15
|
||||
|
||||
# Authorize the Azure Powershell module to act as a client to call the Azure Stack Resource Manager in the onboarded tenant
|
||||
Initialize-GraphOAuth2PermissionGrant -ClientApplicationId (Get-GraphEnvironmentInfo).Applications.PowerShell.Id -ResourceApplicationIdentifierUri $azureStackEnvironment.ActiveDirectoryServiceEndpointResourceId
|
||||
|
||||
|
@ -452,27 +388,63 @@ function Register-AzureStackWithMyDirectoryTenant {
|
|||
}
|
||||
$applicationRegistrations = Invoke-RestMethod @applicationRegistrationParams | Select -ExpandProperty value
|
||||
|
||||
# Initialize each registered application in the onboarding directory tenant
|
||||
foreach ($applicationRegistration in $applicationRegistrations) {
|
||||
# Initialize the service principal for the registered application, updating any tags as necessary
|
||||
# Identify which permissions have already been granted to each registered application and which additional permissions need consent
|
||||
$permissions = @()
|
||||
foreach ($applicationRegistration in $applicationRegistrations)
|
||||
{
|
||||
# Initialize the service principal for the registered application
|
||||
$applicationServicePrincipal = Initialize-GraphApplicationServicePrincipal -ApplicationId $applicationRegistration.appId
|
||||
if ($applicationRegistration.tags) {
|
||||
|
||||
# Initialize the necessary tags for the registered application
|
||||
if ($applicationRegistration.tags)
|
||||
{
|
||||
Update-GraphApplicationServicePrincipalTags -ApplicationId $applicationRegistration.appId -Tags $applicationRegistration.tags
|
||||
}
|
||||
|
||||
# Initialize the necessary oauth2PermissionGrants for the registered application
|
||||
foreach ($oauth2PermissionGrant in $applicationRegistration.oauth2PermissionGrants) {
|
||||
$oauth2PermissionGrantParams = @{
|
||||
ClientApplicationId = $oauth2PermissionGrant.client
|
||||
ResourceApplicationId = $oauth2PermissionGrant.resource
|
||||
Scope = $oauth2PermissionGrant.scope
|
||||
# Lookup the permission consent status for the application permissions (either to or from) that the registered application requires
|
||||
foreach($appRoleAssignment in $applicationRegistration.appRoleAssignments)
|
||||
{
|
||||
$params = @{
|
||||
ClientApplicationId = $appRoleAssignment.client
|
||||
ResourceApplicationId = $appRoleAssignment.resource
|
||||
PermissionType = 'Application'
|
||||
PermissionId = $appRoleAssignment.roleId
|
||||
}
|
||||
Initialize-GraphOAuth2PermissionGrant @oauth2PermissionGrantParams
|
||||
$permissions += New-GraphPermissionDescription @params -LookupConsentStatus
|
||||
}
|
||||
|
||||
# Initialize the necessary directory role membership(s) for the registered application
|
||||
foreach ($directoryRole in $applicationRegistration.directoryRoles) {
|
||||
Initialize-GraphDirectoryRoleMembership -ApplicationId $applicationRegistration.appId -RoleDisplayName $directoryRole
|
||||
# Lookup the permission consent status for the delegated permissions (either to or from) that the registered application requires
|
||||
foreach($oauth2PermissionGrant in $applicationRegistration.oauth2PermissionGrants)
|
||||
{
|
||||
$resourceApplicationServicePrincipal = Initialize-GraphApplicationServicePrincipal -ApplicationId $oauth2PermissionGrant.resource
|
||||
foreach ($scope in $oauth2PermissionGrant.scope.Split(' '))
|
||||
{
|
||||
$params = @{
|
||||
ClientApplicationId = $oauth2PermissionGrant.client
|
||||
ResourceApplicationServicePrincipal = $resourceApplicationServicePrincipal
|
||||
PermissionType = 'Delegated'
|
||||
PermissionId = ($resourceApplicationServicePrincipal.oauth2Permissions | Where value -EQ $scope).id
|
||||
}
|
||||
$permissions += New-GraphPermissionDescription @params -LookupConsentStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Show the user a display of the required permissions
|
||||
$permissions | Show-GraphApplicationPermissionDescriptions
|
||||
|
||||
if ($permissions | Where isConsented -EQ $false | Select -First 1)
|
||||
{
|
||||
# Grant the required permissions to the corresponding applications
|
||||
$permissions | Where isConsented -EQ $false | Grant-GraphApplicationPermission
|
||||
}
|
||||
|
||||
Write-Host "`r`nAll permissions required for registered Azure Stack applications or scenarios have been granted!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function @(
|
||||
"Register-AzureStackWithMyDirectoryTenant",
|
||||
"Register-GuestDirectoryTenantToAzureStack",
|
||||
"Get-DirectoryTenantIdentifier",
|
||||
"New-ADGraphServicePrincipal"
|
||||
)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# See LICENSE.txt in the project root for license information.
|
||||
# {FileName} {Version} {DateTime}
|
||||
# {BuildRepo} {BuildBranch} {BuildType}-{BuildArchitecture}
|
||||
# (Manually updated from Solution Deploy repository)
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
|
@ -21,6 +23,11 @@ function Initialize-GraphEnvironment
|
|||
[ValidateNotNull()]
|
||||
[pscredential] $UserCredential = $null,
|
||||
|
||||
# Indicates that the script should prompt the user to input a credential with which to acquire an access token targeting Graph.
|
||||
[Parameter(ParameterSetName='Credential_AAD')]
|
||||
[Parameter(ParameterSetName='Credential_ADFS')]
|
||||
[switch] $PromptForUserCredential,
|
||||
|
||||
# The refresh token to use to acquire an access token targeting Graph.
|
||||
[Parameter(ParameterSetName='RefreshToken_AAD')]
|
||||
[Parameter(ParameterSetName='RefreshToken_ADFS')]
|
||||
|
@ -63,6 +70,11 @@ function Initialize-GraphEnvironment
|
|||
Write-Warning "Parameters for ADFS have been specified; please note that only a subset of Graph APIs are available to be used in conjuction with ADFS."
|
||||
}
|
||||
|
||||
if ($PromptForUserCredential)
|
||||
{
|
||||
$UserCredential = Get-Credential -Message "Please provide a credential used to access Graph. Must support non-interactive authentication flows."
|
||||
}
|
||||
|
||||
if ($UserCredential)
|
||||
{
|
||||
Write-Verbose "Initializing the module to use Graph environment '$Environment' for user '$($UserCredential.UserName)' in directory tenant '$DirectoryTenantId'." -Verbose
|
||||
|
@ -77,7 +89,7 @@ function Initialize-GraphEnvironment
|
|||
}
|
||||
else
|
||||
{
|
||||
Write-Warning "A user credential, refresh token, or service principal info was not provided. Graph API calls cannot be made until one is provided. Please run 'Initialize-GraphEnvironment' again with valid credentials."
|
||||
Write-Warning "A user credential, refresh token, or service principal info was not provided. Graph API calls cannot be made until one is provided. Please run 'Initialize-GraphEnvironment' again with valid credentials."
|
||||
}
|
||||
|
||||
$graphEnvironmentTemplate = @{}
|
||||
|
@ -212,12 +224,23 @@ function Initialize-GraphEnvironment
|
|||
}
|
||||
|
||||
AadPermissions = [HashTable]@{
|
||||
AccessDirectoryAsSignedInUser = "a42657d6-7f20-40e3-b6f0-cee03008a62a"
|
||||
EnableSignOnAndReadUserProfiles = "311a71cc-e848-46a1-bdf8-97ff7156d8e6"
|
||||
ReadAllGroups = "6234d376-f627-4f0f-90e0-dff25c5211a3"
|
||||
ReadAllUsersBasicProfile = "cba73afc-7f69-4d86-8450-4978e04ecd1a"
|
||||
ReadAllUsersFullProfile = "c582532d-9d9e-43bd-a97c-2667a28ce295"
|
||||
ReadDirectoryData = "5778995a-e1bf-45b8-affa-663a9f3f4d04"
|
||||
AccessDirectoryAsSignedInUser = "a42657d6-7f20-40e3-b6f0-cee03008a62a"
|
||||
EnableSignOnAndReadUserProfiles = "311a71cc-e848-46a1-bdf8-97ff7156d8e6"
|
||||
ReadAllGroups = "6234d376-f627-4f0f-90e0-dff25c5211a3"
|
||||
ReadAllUsersBasicProfile = "cba73afc-7f69-4d86-8450-4978e04ecd1a"
|
||||
ReadAllUsersFullProfile = "c582532d-9d9e-43bd-a97c-2667a28ce295"
|
||||
ReadDirectoryData = "5778995a-e1bf-45b8-affa-663a9f3f4d04"
|
||||
ManageAppsThatThisAppCreatesOrOwns = "824c81eb-e3f8-4ee6-8f6d-de7f50d565b7"
|
||||
}
|
||||
|
||||
AadPermissionScopes = [HashTable]@{
|
||||
AccessDirectoryAsSignedInUser = "Directory.AccessAsUser.All"
|
||||
EnableSignOnAndReadUserProfiles = "User.Read"
|
||||
ReadAllGroups = "Group.Read.All"
|
||||
ReadAllUsersBasicProfile = "User.ReadBasic.All"
|
||||
ReadAllUsersFullProfile = "User.Read.All"
|
||||
ReadDirectoryData = "Directory.Read.All"
|
||||
ManageAppsThatThisAppCreatesOrOwns = "Application.ReadWrite.OwnedBy"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,7 +326,7 @@ function Assert-GraphConnection
|
|||
# Trace the message to verbose stream as well in case error is not traced in same file as other verbose logs
|
||||
$traceMessage = "An error occurred while trying to verify connection to the graph endpoint '$($Script:GraphEnvironment.OpenIdMetadata)': $_`r`n`r`nAdditional details: $traceResponse"
|
||||
Write-Verbose "ERROR: $traceMessage"
|
||||
|
||||
|
||||
throw New-Object System.InvalidOperationException($traceMessage)
|
||||
}
|
||||
}
|
||||
|
@ -735,7 +758,7 @@ function Find-GraphApplication
|
|||
)
|
||||
|
||||
$filter = if ($DisplayName) {"displayName eq '$DisplayName'"} elseif($AppUri) {"identifierUris/any(i:i eq '$AppUri')"} else {"appId eq '$AppId'"}
|
||||
$response = Invoke-GraphApi -ApiPath "applications()" -QueryParameters @{ '$filter' = $filter } -ErrorAction Stop
|
||||
$response = Invoke-GraphApi -ApiPath "applications" -QueryParameters @{ '$filter' = $filter } -ErrorAction Stop
|
||||
Write-Output $response.value
|
||||
}
|
||||
|
||||
|
@ -745,20 +768,25 @@ function Find-GraphApplication
|
|||
#>
|
||||
function Get-GraphApplication
|
||||
{
|
||||
[CmdletBinding()]
|
||||
[CmdletBinding(DefaultParameterSetName='ByUri')]
|
||||
[OutputType([pscustomobject])]
|
||||
param
|
||||
(
|
||||
# The application identifier URI.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ByUri')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $AppUri
|
||||
[string] $AppUri,
|
||||
|
||||
# The application identifer.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ById')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $AppId
|
||||
)
|
||||
|
||||
$application = Find-GraphApplication -AppUri $AppUri
|
||||
$application = Find-GraphApplication @PSBoundParameters
|
||||
if (-not $application)
|
||||
{
|
||||
Write-Error "Application with identifier '$AppUri' not found"
|
||||
Write-Error "Application with identifier '${AppUri}${AppId}' not found"
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -969,7 +997,7 @@ function Initialize-GraphApplicationServicePrincipal
|
|||
{
|
||||
$getScript = { (Invoke-GraphApi -ApiPath servicePrincipals -QueryParameters @{ '$filter' = "appId eq '$ApplicationId'" }).value }
|
||||
|
||||
# Create a service principal for the application (if one doesn't already exist) # TODO: support update tags
|
||||
# Create a service principal for the application (if one doesn't already exist)
|
||||
if (-not ($primaryServicePrincipal = & $getScript))
|
||||
{
|
||||
Write-Verbose "Creating service principal for application '$ApplicationId' in AAD..." -Verbose
|
||||
|
@ -1096,32 +1124,39 @@ function Initialize-GraphOAuth2PermissionGrant
|
|||
[string] $ConsentType = 'AllPrincipals'
|
||||
)
|
||||
|
||||
# https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/entity-and-complex-type-reference#oauth2permissiongrant-entity
|
||||
|
||||
# Ensure the application service principals exist in the directory tenant
|
||||
|
||||
$clientApplicationServicePrincipal = if ($ClientApplicationId)
|
||||
{
|
||||
Initialize-GraphApplicationServicePrincipal -ApplicationId $ClientApplicationId
|
||||
Initialize-GraphApplicationServicePrincipal -ApplicationId $ClientApplicationId -ErrorAction Stop
|
||||
}
|
||||
else
|
||||
{
|
||||
Get-GraphApplicationServicePrincipal -ApplicationIdentifierUri $ClientApplicationIdentifierUri
|
||||
Get-GraphApplicationServicePrincipal -ApplicationIdentifierUri $ClientApplicationIdentifierUri -ErrorAction Stop
|
||||
}
|
||||
|
||||
$resourceApplicationServicePrincipal = if ($ResourceApplicationId)
|
||||
{
|
||||
Initialize-GraphApplicationServicePrincipal -ApplicationId $ResourceApplicationId
|
||||
Initialize-GraphApplicationServicePrincipal -ApplicationId $ResourceApplicationId -ErrorAction Stop
|
||||
}
|
||||
else
|
||||
{
|
||||
Get-GraphApplicationServicePrincipal -ApplicationIdentifierUri $ResourceApplicationIdentifierUri
|
||||
Get-GraphApplicationServicePrincipal -ApplicationIdentifierUri $ResourceApplicationIdentifierUri -ErrorAction Stop
|
||||
}
|
||||
|
||||
# TODO: Do we need to support updating expired permission grants? The documentation appears to say these properties should be ignored: "https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/entity-and-complex-type-reference#oauth2permissiongrant-entity"
|
||||
|
||||
# Note: value=Invalid characters found in scope. Allowed characters are %x20 / %x21 / %x23-5B / %x5D-7E
|
||||
$scopesToGrant = $Scope.Split(' ')
|
||||
|
||||
# Note: the permission grants do not expire, but we must provide an expiration date to the API
|
||||
$queryParameters = @{
|
||||
'$filter' = "resourceId eq '$($resourceApplicationServicePrincipal.objectId)' and clientId eq '$($clientApplicationServicePrincipal.objectId)'"
|
||||
'$top' = '999'
|
||||
}
|
||||
if (-not (Invoke-GraphApi -ApiPath oauth2PermissionGrants -QueryParameters $queryParameters).Value)
|
||||
$existingGrant = (Invoke-GraphApi -ApiPath oauth2PermissionGrants -QueryParameters $queryParameters).Value | Select -First 1
|
||||
|
||||
if (-not $existingGrant)
|
||||
{
|
||||
Write-Verbose "Granting OAuth2Permission '$Scope' to application service principal '$($clientApplicationServicePrincipal.appDisplayName)' on behalf of application '$($resourceApplicationServicePrincipal.appDisplayName)'..." -Verbose
|
||||
$response = Invoke-GraphApi -Method Post -ApiPath oauth2PermissionGrants -Body (ConvertTo-Json ([pscustomobject]@{
|
||||
|
@ -1133,10 +1168,112 @@ function Initialize-GraphOAuth2PermissionGrant
|
|||
startTime = [DateTime]::UtcNow.ToString('o')
|
||||
expiryTime = [DateTime]::UtcNow.AddYears(1).ToString('o')
|
||||
}))
|
||||
|
||||
Write-Verbose "Sleeping for 3 seconds to allow the permission grant to propagate..." -Verbose
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "OAuth2Permission '$Scope' already granted to client application service principal '$($clientApplicationServicePrincipal.appDisplayName)' on behalf of resource application '$($resourceApplicationServicePrincipal.appDisplayName)'." -Verbose
|
||||
Write-Verbose "Existing OAuth2PermissionGrant found: $(ConvertTo-Json $existingGrant)" -Verbose
|
||||
|
||||
$existingScopes = $existingGrant.scope.Split(' ')
|
||||
$missingScopes = $scopesToGrant | Where { $_ -inotin $existingScopes }
|
||||
|
||||
if ($missingScopes.Count)
|
||||
{
|
||||
$fullScopes = $existingGrant.scope += (' ' + [string]::Join(' ', $missingScopes))
|
||||
Write-Verbose "Updating OAuth2PermissionGrant scopes to include missing scopes '$([string]::Join(' ', $missingScopes))' to client application service principal '$($clientApplicationServicePrincipal.appDisplayName)' on behalf of resource application '$($resourceApplicationServicePrincipal.appDisplayName)'. Full Scopes will include: '$fullScopes'" -Verbose
|
||||
$response = Invoke-GraphApi -Method Patch -ApiPath "oauth2PermissionGrants/$($existingGrant.objectId)" -Body (ConvertTo-Json ([pscustomobject]@{ scope = $fullScopes }))
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "OAuth2Permission '$Scope' already granted to client application service principal '$($clientApplicationServicePrincipal.appDisplayName)' on behalf of resource application '$($resourceApplicationServicePrincipal.appDisplayName)'. Full Scopes: '$($existingGrant.scope)'" -Verbose
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function Initialize-GraphAppRoleAssignment
|
||||
{
|
||||
[CmdletBinding(DefaultParameterSetName='ClientAppId_ResourceAppId')]
|
||||
param
|
||||
(
|
||||
# The application identifier of the client application.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_ResourceAppId')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_ResourceIdentifierUri')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ClientApplicationId,
|
||||
|
||||
# The application identifier URI of the client application.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_ResourceAppId')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_ResourceIdentifierUri')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ClientApplicationIdentifierUri,
|
||||
|
||||
# The application identifier of the resource application.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_ResourceAppId')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_ResourceAppId')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ResourceApplicationId,
|
||||
|
||||
# The application identifier URI of the resource application.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_ResourceIdentifierUri')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_ResourceIdentifierUri')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ResourceApplicationIdentifierUri,
|
||||
|
||||
# The identifier of the app role permission to grant.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $RoleId,
|
||||
|
||||
# The type of the service principal to with the permission will be granted (e.g. 'ServicePrincipal' [default value]).
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidateSet('ServicePrincipal', 'User', 'Group')]
|
||||
[string] $PrincipalType = 'ServicePrincipal'
|
||||
)
|
||||
|
||||
# https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/entity-and-complex-type-reference#approleassignment-entity
|
||||
|
||||
# Ensure the application service principals exist in the directory tenant
|
||||
|
||||
$clientApplicationServicePrincipal = if ($ClientApplicationId)
|
||||
{
|
||||
Initialize-GraphApplicationServicePrincipal -ApplicationId $ClientApplicationId -ErrorAction Stop
|
||||
}
|
||||
else
|
||||
{
|
||||
Get-GraphApplicationServicePrincipal -ApplicationIdentifierUri $ClientApplicationIdentifierUri -ErrorAction Stop
|
||||
}
|
||||
|
||||
$resourceApplicationServicePrincipal = if ($ResourceApplicationId)
|
||||
{
|
||||
Initialize-GraphApplicationServicePrincipal -ApplicationId $ResourceApplicationId -ErrorAction Stop
|
||||
}
|
||||
else
|
||||
{
|
||||
Get-GraphApplicationServicePrincipal -ApplicationIdentifierUri $ResourceApplicationIdentifierUri -ErrorAction Stop
|
||||
}
|
||||
|
||||
$existingAssignments = (Invoke-GraphApi -ApiPath "servicePrincipals/$($clientApplicationServicePrincipal.objectId)/appRoleAssignedTo").value
|
||||
$existingAssignment = $existingAssignments |
|
||||
Where id -EQ $RoleId |
|
||||
Where resourceId -EQ $resourceApplicationServicePrincipal.objectId
|
||||
|
||||
if (-not $existingAssignment)
|
||||
{
|
||||
Write-Verbose "Granting AppRoleAssignment '$RoleId' to application service principal '$($clientApplicationServicePrincipal.appDisplayName)' on behalf of application '$($resourceApplicationServicePrincipal.appDisplayName)'..." -Verbose
|
||||
$response = Invoke-GraphApi -Method Post -ApiPath "servicePrincipals/$($clientApplicationServicePrincipal.objectId)/appRoleAssignments" -Body (ConvertTo-Json ([pscustomobject]@{
|
||||
principalId = $clientApplicationServicePrincipal.objectId
|
||||
principalType = $PrincipalType
|
||||
resourceId = $resourceApplicationServicePrincipal.objectId
|
||||
id = $RoleId
|
||||
}))
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "AppRoleAssignment '$RoleId' already granted to client application service principal '$($clientApplicationServicePrincipal.appDisplayName)' on behalf of resource application '$($resourceApplicationServicePrincipal.appDisplayName)'." -Verbose
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1161,7 +1298,7 @@ function Initialize-GraphDirectoryRoleMembership
|
|||
[string] $RoleDisplayName
|
||||
)
|
||||
|
||||
# Ensure the application service principal exist in the directory tenant
|
||||
# Ensure the application service principal exists in the directory tenant
|
||||
$applicationServicePrincipal = Initialize-GraphApplicationServicePrincipal -ApplicationId $ApplicationId
|
||||
|
||||
# https://msdn.microsoft.com/en-us/Library/Azure/Ad/Graph/api/directoryroles-operations#AddDirectoryRoleMembers
|
||||
|
@ -1191,7 +1328,7 @@ function Initialize-GraphDirectoryRoleMembership
|
|||
$response = Invoke-GraphApi -Method Post -ApiPath $apiPath -Body (ConvertTo-Json ([pscustomobject]@{
|
||||
securityEnabledOnly = $false
|
||||
}))
|
||||
|
||||
|
||||
if ($response.value -icontains $roleObjectId)
|
||||
{
|
||||
Write-Verbose "Membership already granted to directory role '$RoleDisplayName' ($($roleObjectId)) for application service principal '$($applicationServicePrincipal.appDisplayName)'." -Verbose
|
||||
|
@ -1206,6 +1343,461 @@ function Initialize-GraphDirectoryRoleMembership
|
|||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Creates a new representation of a permission exposed by a resource application and grantable to a client application.
|
||||
#>
|
||||
function New-GraphPermissionDescription
|
||||
{
|
||||
[CmdletBinding(DefaultParameterSetName='ClientAppId_ResourceAppId')]
|
||||
param
|
||||
(
|
||||
# The application identifier of the client application.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_ResourceAppId')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_ResourceIdentifierUri')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_Resource')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ClientApplicationId,
|
||||
|
||||
# The application identifier URI of the client application.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_ResourceAppId')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_ResourceIdentifierUri')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_Resource')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ClientApplicationIdentifierUri,
|
||||
|
||||
# The object reprsentation client application service principal.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='Client_ResourceAppId')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='Client_ResourceIdentifierUri')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='Client_Resource')]
|
||||
[pscustomobject] $ClientApplicationServicePrincipal,
|
||||
|
||||
# The application identifier of the resource application.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_ResourceAppId')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_ResourceAppId')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='Client_ResourceAppId')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ResourceApplicationId,
|
||||
|
||||
# The application identifier URI of the resource application.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_ResourceIdentifierUri')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_ResourceIdentifierUri')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='Client_ResourceIdentifierUri')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ResourceApplicationIdentifierUri,
|
||||
|
||||
# The object reprsentation resource application service principal.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientAppId_Resource')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ClientIdentifierUri_Resource')]
|
||||
[Parameter(Mandatory=$true, ParameterSetName='Client_Resource')]
|
||||
[pscustomobject] $ResourceApplicationServicePrincipal,
|
||||
|
||||
# The type of the permission.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidateSet('Application', 'Delegated')]
|
||||
[string] $PermissionType,
|
||||
|
||||
# The identifier of the permission.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $PermissionId,
|
||||
|
||||
# Indicates whether the permission has been granted (consented).
|
||||
[Parameter()]
|
||||
[ValidateNotNull()]
|
||||
[switch] $IsConsented,
|
||||
|
||||
# Indicates whether the current permission consent status should be queried.
|
||||
[Parameter()]
|
||||
[ValidateNotNull()]
|
||||
[switch] $LookupConsentStatus
|
||||
)
|
||||
|
||||
# Lookup / initialize client service principal
|
||||
$ClientApplicationServicePrincipal = if ($ClientApplicationServicePrincipal)
|
||||
{
|
||||
$ClientApplicationServicePrincipal
|
||||
}
|
||||
elseif ($ClientApplicationId)
|
||||
{
|
||||
Initialize-GraphApplicationServicePrincipal -ApplicationId $ClientApplicationId -ErrorAction Stop
|
||||
}
|
||||
else
|
||||
{
|
||||
Get-GraphApplicationServicePrincipal -ApplicationIdentifierUri $ClientApplicationIdentifierUri -ErrorAction Stop
|
||||
}
|
||||
|
||||
# Lookup / initialize resource service principal
|
||||
$ResourceApplicationServicePrincipal = if ($ResourceApplicationServicePrincipal)
|
||||
{
|
||||
$ResourceApplicationServicePrincipal
|
||||
}
|
||||
elseif ($ResourceApplicationId)
|
||||
{
|
||||
Initialize-GraphApplicationServicePrincipal -ApplicationId $ResourceApplicationId -ErrorAction Stop
|
||||
}
|
||||
else
|
||||
{
|
||||
Get-GraphApplicationServicePrincipal -ApplicationIdentifierUri $ResourceApplicationIdentifierUri -ErrorAction Stop
|
||||
}
|
||||
|
||||
$permissionProperties = [ordered]@{
|
||||
clientApplicationId = $ClientApplicationServicePrincipal.appId
|
||||
clientApplicationDisplayName = $ClientApplicationServicePrincipal.appDisplayName
|
||||
resourceApplicationId = $ResourceApplicationServicePrincipal.appId
|
||||
resourceApplicationDisplayName = $ResourceApplicationServicePrincipal.appDisplayName
|
||||
}
|
||||
|
||||
$permissionProperties += [ordered]@{
|
||||
isConsented = $IsConsented
|
||||
permissionType = $PermissionType
|
||||
permissionId = $PermissionId
|
||||
}
|
||||
|
||||
switch ($PermissionType)
|
||||
{
|
||||
'Application'
|
||||
{
|
||||
$appRole = $ResourceApplicationServicePrincipal.appRoles | Where id -EQ $PermissionId
|
||||
$permissionProperties += [ordered]@{
|
||||
permissionName = $appRole.value
|
||||
permissionDisplayName = $appRole.displayName
|
||||
permissionDescription = $appRole.description
|
||||
}
|
||||
|
||||
if ($LookupConsentStatus)
|
||||
{
|
||||
$existingAppRoleAssignments = (Invoke-GraphApi -ApiPath "servicePrincipals/$($ClientApplicationServicePrincipal.objectId)/appRoleAssignedTo").value
|
||||
$permissionProperties.isConsented = if ($existingAppRoleAssignments | Where id -EQ $PermissionId) {$true} else {$false}
|
||||
}
|
||||
}
|
||||
|
||||
'Delegated'
|
||||
{
|
||||
$oAuth2Permission = $ResourceApplicationServicePrincipal.oauth2Permissions | Where id -EQ $PermissionId
|
||||
$permissionProperties += [ordered]@{
|
||||
permissionName = $oAuth2Permission.value
|
||||
permissionDisplayName = $oAuth2Permission.adminConsentDisplayName
|
||||
permissionDescription = $oAuth2Permission.adminConsentDescription
|
||||
}
|
||||
|
||||
if ($LookupConsentStatus)
|
||||
{
|
||||
$queryParameters = @{
|
||||
'$filter' = "resourceId eq '$($ResourceApplicationServicePrincipal.objectId)' and clientId eq '$($ClientApplicationServicePrincipal.objectId)'"
|
||||
'$top' = '999'
|
||||
}
|
||||
$existingOAuth2PermissionGrants = (Invoke-GraphApi -ApiPath oauth2PermissionGrants -QueryParameters $queryParameters).Value
|
||||
$permissionProperties.isConsented = if ($existingOAuth2PermissionGrants) {$true} else {$false}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output ([pscustomobject]$permissionProperties)
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Gets all permissions which have been granted to the specified application. If the application was created in the current directory tenant, also returns permissions which have not been consented but which are advertised as "required" in the application's manifest.
|
||||
#>
|
||||
function Get-GraphApplicationPermissions
|
||||
{
|
||||
[CmdletBinding()]
|
||||
[OutputType([pscustomobject])]
|
||||
param
|
||||
(
|
||||
# The application identifier for which all consented permissions should be retrieved.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[Alias('appId')]
|
||||
[string] $ApplicationId
|
||||
)
|
||||
|
||||
# Ensure the application service principal exists in the directory tenant
|
||||
$applicationServicePrincipal = Initialize-GraphApplicationServicePrincipal -ApplicationId $ApplicationId -ErrorAction Stop
|
||||
|
||||
# Identify which permissions have already been granted
|
||||
$existingAppRoleAssignments = (Invoke-GraphApi -ApiPath "servicePrincipals/$($applicationServicePrincipal.objectId)/appRoleAssignedTo").value
|
||||
$existingClientOAuth2PermissionGrants = (Invoke-GraphApi -ApiPath oauth2PermissionGrants -QueryParameters @{ '$filter' = "clientId eq '$($applicationServicePrincipal.objectId)'"; '$top' = '999' }).Value
|
||||
$existingResourceOAuth2PermissionGrants = @() # Note - there is an issue with this API at the moment, it returns 404 on the provided nextLink URI # (Invoke-GraphApi -ApiPath oauth2PermissionGrants -QueryParameters @{ '$filter' = "resourceId eq '$($applicationServicePrincipal.objectId)'"; '$top' = '999' }).Value
|
||||
|
||||
# Build a representation of each permission which has been granted
|
||||
$permissions = @()
|
||||
foreach ($existingAppRoleAssignment in $existingAppRoleAssignments)
|
||||
{
|
||||
$permissionParams = @{
|
||||
ClientApplicationServicePrincipal = $applicationServicePrincipal
|
||||
ResourceApplicationServicePrincipal = Invoke-GraphApi -ApiPath "directoryObjects/$($existingAppRoleAssignment.resourceId)" -ErrorAction Stop
|
||||
PermissionType = 'Application'
|
||||
PermissionId = $existingAppRoleAssignment.id
|
||||
IsConsented = $true
|
||||
}
|
||||
$permissions += New-GraphPermissionDescription @permissionParams
|
||||
}
|
||||
foreach ($existingOAuth2PermissionGrant in $existingClientOAuth2PermissionGrants)
|
||||
{
|
||||
$permissionParams = @{
|
||||
ClientApplicationServicePrincipal = $applicationServicePrincipal
|
||||
ResourceApplicationServicePrincipal = Invoke-GraphApi -ApiPath "directoryObjects/$($existingOAuth2PermissionGrant.resourceId)" -ErrorAction Stop
|
||||
PermissionType = 'Delegated'
|
||||
IsConsented = $true
|
||||
}
|
||||
foreach ($scope in $existingOAuth2PermissionGrant.scope.split(' '))
|
||||
{
|
||||
$oAuth2Permission = $permissionParams.ResourceApplicationServicePrincipal.oauth2Permissions | Where value -EQ $scope
|
||||
$permissions += New-GraphPermissionDescription @permissionParams -PermissionId $oAuth2Permission.id
|
||||
}
|
||||
}
|
||||
foreach ($existingOAuth2PermissionGrant in $existingResourceOAuth2PermissionGrants)
|
||||
{
|
||||
$permissionParams = @{
|
||||
ClientApplicationServicePrincipal = Invoke-GraphApi -ApiPath "directoryObjects/$($existingOAuth2PermissionGrant.clientId)" -ErrorAction Stop
|
||||
ResourceApplicationServicePrincipal = $applicationServicePrincipal
|
||||
PermissionType = 'Delegated'
|
||||
IsConsented = $true
|
||||
}
|
||||
foreach ($scope in $existingOAuth2PermissionGrant.scope.split(' '))
|
||||
{
|
||||
$oAuth2Permission = $permissionParams.ResourceApplicationServicePrincipal.oauth2Permissions | Where value -EQ $scope
|
||||
$permissions += New-GraphPermissionDescription @permissionParams -PermissionId $oAuth2Permission.id
|
||||
}
|
||||
}
|
||||
|
||||
# Attempt to get unconsented permissions if we can access the application object (e.g. if the application exists in the same directory in which we are currently authenticated)
|
||||
if (($application = Find-GraphApplication -AppId $ApplicationId))
|
||||
{
|
||||
foreach ($requiredResource in $application.requiredResourceAccess)
|
||||
{
|
||||
$permissionParams = @{
|
||||
ClientApplicationServicePrincipal = $applicationServicePrincipal
|
||||
ResourceApplicationServicePrincipal = Initialize-GraphApplicationServicePrincipal -ApplicationId $requiredResource.resourceAppId
|
||||
IsConsented = $false
|
||||
}
|
||||
foreach ($resourceAccess in $requiredResource.resourceAccess)
|
||||
{
|
||||
$relatedConsentedPermissions = $permissions | Where resourceApplicationId -EQ $requiredResource.resourceAppId | Where permissionId -EQ $resourceAccess.id
|
||||
# $resourceAccess.type is one of: 'Role', 'Scope', 'Role,Scope', or 'Scope,Role'
|
||||
if ($resourceAccess.type -ilike '*Role*')
|
||||
{
|
||||
if (-not ($relatedConsentedPermissions | Where permissionType -EQ 'Application'))
|
||||
{
|
||||
$permissions += New-GraphPermissionDescription @permissionParams -PermissionType Application -PermissionId $resourceAccess.id
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "Application permission '$($resourceAccess.id)' of type 'AppRoleAssignment' already consented for application '$($applicationServicePrincipal.appDisplayName)' ('$ApplicationId')."
|
||||
}
|
||||
}
|
||||
if ($resourceAccess.type -ilike '*Scope*')
|
||||
{
|
||||
if (-not ($relatedConsentedPermissions | Where permissionType -EQ 'Delegated'))
|
||||
{
|
||||
$permissions += New-GraphPermissionDescription @permissionParams -PermissionType Delegated -PermissionId $resourceAccess.id
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "Application permission '$($resourceAccess.id)' of type 'OAuth2PermissionGrant' already consented for application '$($applicationServicePrincipal.appDisplayName)' ('$ApplicationId')."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "Unable to retrieve application with appId '$ApplicationId' and will be unable to retrieve information on any additional required permissions which have not been consented." -Verbose
|
||||
}
|
||||
|
||||
if (-not $permissions.Count)
|
||||
{
|
||||
if ($application)
|
||||
{
|
||||
Write-Verbose "Application '$($applicationServicePrincipal.appDisplayName)' ('$ApplicationId') does not have any consented permissions, nor does it advertise any additional required permissions in its application manifest." -Verbose
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "Application '$($applicationServicePrincipal.appDisplayName)' ('$ApplicationId') does not have any consented permissions in the current directory tenant." -Verbose
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output $permissions
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Grants a permission to a graph application. Use the 'New-GraphApplicationPermission' or 'Get-GraphApplicationPermissions' cmdlets to create an instance of the permission object or to see its structure.
|
||||
#>
|
||||
function Grant-GraphApplicationPermission
|
||||
{
|
||||
[CmdletBinding()]
|
||||
param
|
||||
(
|
||||
# The graph permission description object.
|
||||
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[pscustomobject] $PermissionDescription
|
||||
)
|
||||
process
|
||||
{
|
||||
Write-Verbose "Granting permission '$($PermissionDescription.permissionName)' ($($PermissionDescription.PermissionId)) exposed by application '$($PermissionDescription.resourceApplicationDisplayName)' ($($PermissionDescription.resourceApplicationId)) of type '$($PermissionDescription.PermissionType)' to application '$($PermissionDescription.clientApplicationDisplayName)' ($($PermissionDescription.clientApplicationId))" -Verbose
|
||||
$params = @{ ClientApplicationId = $PermissionDescription.clientApplicationId; ResourceApplicationId = $PermissionDescription.resourceApplicationId }
|
||||
switch ($PermissionDescription.permissionType)
|
||||
{
|
||||
'Application' { Initialize-GraphAppRoleAssignment @params -RoleId $PermissionDescription.permissionId -Verbose }
|
||||
'Delegated' { Initialize-GraphOAuth2PermissionGrant @params -Scope $PermissionDescription.permissionName -Verbose }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Grants all permissions required by an application which are specified in the application manifest. Only applies to the home directory of the application.
|
||||
#>
|
||||
function Grant-GraphApplicationPermissions
|
||||
{
|
||||
[CmdletBinding()]
|
||||
param
|
||||
(
|
||||
# The application identifier for which all required permissions should be granted.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ApplicationId
|
||||
)
|
||||
# Ensure the application can be retrieved in the current directory tenant
|
||||
$application = Get-GraphApplication -AppId $ApplicationId -ErrorAction Stop
|
||||
$permissions = Get-GraphApplicationPermissions -ApplicationId $ApplicationId
|
||||
foreach ($permission in $permissions)
|
||||
{
|
||||
if ($permission.isConsented)
|
||||
{
|
||||
Write-Verbose "Permission '$($permission.permissionName)' ($($permission.PermissionId)) exposed by application '$($permission.resourceApplicationDisplayName)' ($($permission.resourceApplicationId)) of type '$($permission.PermissionType)' has already been granted to application '$($permission.clientApplicationDisplayName)' ($($permission.clientApplicationId))" -Verbose
|
||||
}
|
||||
else
|
||||
{
|
||||
Grant-GraphApplicationPermission -PermissionDescription $permission
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Writes a representation of the specified Graph permission descriptions to the current PowerShell host console window.
|
||||
#>
|
||||
function Show-GraphApplicationPermissionDescriptions
|
||||
{
|
||||
[CmdletBinding()]
|
||||
param
|
||||
(
|
||||
# The graph permission description object.
|
||||
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[pscustomobject[]] $PermissionDescription,
|
||||
|
||||
# The text display to use above the permission descriptions.
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $DisplayHeader = 'Microsoft Azure Stack - Required Directory Permissions',
|
||||
|
||||
# The text display to use below the permission descriptions.
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $DisplayFooter = '(X) = Consent given, ( ) = Consent not given',
|
||||
|
||||
# Indicates that any duplicate permissions should be filtered-out from the display.
|
||||
[Parameter()]
|
||||
[Switch] $FilterDuplicates = $true
|
||||
)
|
||||
begin
|
||||
{
|
||||
$permissions = @()
|
||||
}
|
||||
process
|
||||
{
|
||||
foreach ($permission in $PermissionDescription)
|
||||
{
|
||||
if ($FilterDuplicates -and ($permissions |
|
||||
Where clientApplicationId -EQ $permission.clientApplicationId |
|
||||
Where resourceApplicationId -EQ $permission.resourceApplicationId |
|
||||
Where permissionId -EQ $permission.permissionId |
|
||||
Where permissionType -EQ $permission.permissionType))
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
$permissions += $permission
|
||||
}
|
||||
}
|
||||
end
|
||||
{
|
||||
<# Writes a textual consent display to the console similar to this:
|
||||
+------------------------------------------------------------+
|
||||
| Microsoft Azure Stack - Required Directory Permissions |
|
||||
+------------------------------------------------------------+
|
||||
| Access Azure Stack [bryanr-env] |
|
||||
| (X) Delegated to: Microsoft Azure Stack |
|
||||
| |
|
||||
| Access the directory as the signed-in user |
|
||||
| (X) Delegated to: Azure Stack |
|
||||
| |
|
||||
| Read all users' basic profiles |
|
||||
| (X) Delegated to: Azure Stack |
|
||||
| |
|
||||
| Read all users' full profiles |
|
||||
| (X) Delegated to: Azure Stack |
|
||||
| |
|
||||
| Read directory data |
|
||||
| (X) Granted to: Azure Stack |
|
||||
| (X) Granted to: AzureStack - Policy |
|
||||
| (X) Delegated to: Azure Stack |
|
||||
| (X) Delegated to: Microsoft Azure Stack |
|
||||
| |
|
||||
| Sign in and read user profile |
|
||||
| (X) Delegated to: Azure Stack |
|
||||
| (X) Delegated to: Microsoft Azure Stack |
|
||||
| |
|
||||
+------------------------------------------------------------+
|
||||
| (X) = Consent given, ( ) = Consent not given |
|
||||
+------------------------------------------------------------+
|
||||
#>
|
||||
|
||||
$header = $DisplayHeader
|
||||
$footer = $DisplayFooter
|
||||
|
||||
$lines = @()
|
||||
foreach ($permissionGroup in @($permissions | Sort resourceApplicationDisplayName, permissionDisplayName | Group permissionId))
|
||||
{
|
||||
$lines += "{0}" -f $permissionGroup.Group[0].permissionDisplayName
|
||||
foreach ($permission in @($permissionGroup.Group | Sort permissionType, clientApplicationDisplayName))
|
||||
{
|
||||
$lines += " {0} {1} {2}" -f @(
|
||||
($consentDisplay = if ($permission.isConsented) {'(X)'} else {'( )'})
|
||||
($typeDisplay = switch ($permission.permissionType) { 'Application' { 'Granted to: ' }; 'Delegated' { 'Delegated to:' } })
|
||||
$permission.clientApplicationDisplayName
|
||||
)
|
||||
}
|
||||
$lines += ''
|
||||
}
|
||||
|
||||
$max = (($lines + @($header, $footer)) | Measure Length -Maximum).Maximum
|
||||
$div = '+-{0}-+' -f (New-Object string('-', $max))
|
||||
|
||||
$lines = @(
|
||||
$div
|
||||
'| {0} |' -f "$header".PadRight($max)
|
||||
$div
|
||||
$lines | ForEach { '| {0} |' -f "$_".PadRight($max) }
|
||||
$div
|
||||
'| {0} |' -f "$footer".PadRight($max)
|
||||
$div
|
||||
)
|
||||
|
||||
foreach ($line in $lines)
|
||||
{
|
||||
Write-Host $line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Creates or updates an application in Graph with an implicit service principal and the specified properties.
|
||||
|
@ -1251,7 +1843,8 @@ function Initialize-GraphApplication
|
|||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidateSet(
|
||||
'ReadDirectoryData'
|
||||
'ReadDirectoryData',
|
||||
'ManageAppsThatThisAppCreatesOrOwns'
|
||||
)]
|
||||
[String[]] $ApplicationAadPermissions = @(),
|
||||
|
||||
|
@ -1307,9 +1900,13 @@ function Initialize-GraphApplication
|
|||
[Parameter()]
|
||||
[Switch] $AvailableToOtherTenants = $true,
|
||||
|
||||
# Indicates that the application service principal should be given membership in the directory readers role to activate the role permission ReadDirectoryData. True by default.
|
||||
# Indicates that the application service principal should have all declared application permissions consented-to. True by default.
|
||||
[Parameter()]
|
||||
[Switch] $UseDirectoryReadersRolePermission = $true
|
||||
[Switch] $ConsentToAppPermissions = $true,
|
||||
|
||||
# Indicates that any existing client certificates associated to this application should be removed. False by default.
|
||||
[Parameter()]
|
||||
[Switch] $RemoveExistingClientCertificates
|
||||
)
|
||||
|
||||
if ($ClientCertificateThumbprint)
|
||||
|
@ -1393,17 +1990,20 @@ function Initialize-GraphApplication
|
|||
}
|
||||
|
||||
# Initialize the application key credentials with which it can authenticate
|
||||
$requestBody['keyCredentials@odata.type'] = "Collection(Microsoft.DirectoryServices.KeyCredential)"
|
||||
$requestBody['keyCredentials'] = @(@($existingApplication.keyCredentials) | Where { $_ -ne $null })
|
||||
if ($RemoveExistingClientCertificates)
|
||||
{
|
||||
$requestBody['keyCredentials'] = @()
|
||||
}
|
||||
if ($ClientCertificate)
|
||||
{
|
||||
$customKeyIdentifier = [Convert]::ToBase64String($ClientCertificate.GetCertHash())
|
||||
if (-not (@($existingApplication.keyCredentials) | Where customKeyIdentifier -EQ $customKeyIdentifier))
|
||||
if (-not (@($requestBody['keyCredentials']) | Where customKeyIdentifier -EQ $customKeyIdentifier))
|
||||
{
|
||||
Write-Verbose "Adding new key credentials to application using client certificate '$($ClientCertificate.Subject)' ($($ClientCertificate.Thumbprint))" -Verbose
|
||||
|
||||
$requestBody['keyCredentials@odata.type'] = "Collection(Microsoft.DirectoryServices.KeyCredential)"
|
||||
$requestBody['keyCredentials'] = @(@($existingApplication.keyCredentials) | Where { $_ -ne $null })
|
||||
|
||||
$requestBody['keyCredentials'] += @(@{
|
||||
$requestBody['keyCredentials'] += @(,([pscustomobject]@{
|
||||
keyId = [Guid]::NewGuid()
|
||||
type = "AsymmetricX509Cert"
|
||||
usage = "Verify"
|
||||
|
@ -1411,7 +2011,7 @@ function Initialize-GraphApplication
|
|||
value = [Convert]::ToBase64String($ClientCertificate.GetRawCertData())
|
||||
startDate = $ClientCertificate.NotBefore.ToUniversalTime().ToString('o')
|
||||
endDate = $ClientCertificate.NotAfter.ToUniversalTime().ToString('o')
|
||||
})
|
||||
}))
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1617,21 +2217,61 @@ function Initialize-GraphApplication
|
|||
Initialize-GraphOAuth2PermissionGrant @params
|
||||
}
|
||||
|
||||
# Initialize directory role membership
|
||||
if ($UseDirectoryReadersRolePermission -and ($ApplicationAadPermissions -icontains 'ReadDirectoryData'))
|
||||
# "Consent" to application permissions
|
||||
if ($ConsentToAppPermissions)
|
||||
{
|
||||
$params = @{
|
||||
ApplicationId = $application.appId
|
||||
RoleDisplayName = 'Directory Readers'
|
||||
}
|
||||
|
||||
Initialize-GraphDirectoryRoleMembership @params
|
||||
Grant-GraphApplicationPermissions -ApplicationId $application.appId
|
||||
}
|
||||
|
||||
# Return the application in its final (current) state
|
||||
Get-GraphApplication -AppUri $IdentifierUri | Write-Output
|
||||
}
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Creates or updates an application in Graph with an implicit service principal and the specified properties.
|
||||
#>
|
||||
function Initialize-GraphApplicationOwner
|
||||
{
|
||||
[CmdletBinding(DefaultParameterSetName='ById')]
|
||||
param
|
||||
(
|
||||
# The application identifier.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ById')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ApplicationId,
|
||||
|
||||
# The application identifier URI.
|
||||
[Parameter(Mandatory=$true, ParameterSetName='ByUri')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $ApplicationIdentifierUri,
|
||||
|
||||
# The identifier of the object (user, service principal, etc.) to which ownership of the target application should be granted.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $OwnerObjectId
|
||||
)
|
||||
|
||||
# Lookup the target objects
|
||||
$params = if ($ApplicationId) {@{ AppId = $ApplicationId }} else {@{ AppUri = $ApplicationIdentifierUri }}
|
||||
$application = Get-GraphApplication @params -ErrorAction Stop
|
||||
$owner = Invoke-GraphApi -ApiPath "directoryObjects/$OwnerObjectId" -ErrorAction Stop
|
||||
|
||||
# Lookup the existing owners and grant ownership if not already granted
|
||||
$owners = (Invoke-GraphApi -Method Get -ApiPath "applications/$($application.objectId)/owners" -ErrorAction Stop).value
|
||||
if ($owners | Where objectId -EQ $OwnerObjectId)
|
||||
{
|
||||
Write-Verbose "Object '$($owner.objectId)' of type '$($owner.objectType)' is already an owner of the application '$($application.displayName)' ($($application.appId))" -Verbose
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "Granting ownership of application '$($application.displayName)' ($($application.appId)) to object '$($owner.objectId)' of type '$($owner.objectType)'." -Verbose
|
||||
Invoke-GraphApi -Method Post -ApiPath "applications/$($application.objectId)/`$links/owners" -Verbose -ErrorAction Stop -Body (ConvertTo-Json ([pscustomobject]@{
|
||||
url = '{0}/directoryObjects/{1}' -f $Script:GraphEnvironment.GraphEndpoint.AbsoluteUri.TrimEnd('/'), $OwnerObjectId
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
[System.Reflection.Assembly]::LoadWithPartialName('System.Web') | Out-Null
|
||||
|
||||
<#
|
||||
|
@ -1671,7 +2311,14 @@ Export-ModuleMember -Function @(
|
|||
'Initialize-GraphApplicationServicePrincipal'
|
||||
'Update-GraphApplicationServicePrincipalTags'
|
||||
'Initialize-GraphOAuth2PermissionGrant'
|
||||
'Initialize-GraphAppRoleAssignment'
|
||||
'Initialize-GraphDirectoryRoleMembership'
|
||||
'New-GraphPermissionDescription'
|
||||
'Get-GraphApplicationPermissions'
|
||||
'Grant-GraphApplicationPermission'
|
||||
'Grant-GraphApplicationPermissions'
|
||||
'Show-GraphApplicationPermissionDescriptions'
|
||||
'Initialize-GraphApplication'
|
||||
'Initialize-GraphApplicationOwner'
|
||||
#'ConvertTo-QueryString'
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Azure Stack Identity
|
||||
|
||||
```powershell
|
||||
Install-Module -Name 'AzureRm.Bootstrapper' -Scope CurrentUser
|
||||
// Place your settings in this file to overwrite the default settings
|
||||
{
|
||||
"workbench.colorTheme": "Abyss"
|
||||
}nstall-Module -Name 'AzureRm.Bootstrapper' -Scope CurrentUser
|
||||
Install-AzureRmProfile -profile '2017-03-09-profile' -Force -Scope CurrentUser
|
||||
Install-Module -Name AzureStack -RequiredVersion 1.2.9 -Scope CurrentUser
|
||||
```
|
||||
|
@ -48,22 +48,6 @@ There are two personas involved in implementing this scenario.
|
|||
|
||||
### Azure Stack Administrator
|
||||
|
||||
#### Pre-Requisite: Populate Azure Resource Manager with AzureStack Applications
|
||||
|
||||
- This step is a temporary workaround and needed only for the TP3 (March) release of Azure Stack
|
||||
- Execute this cmdlet as the **Azure Stack Service Administrator**, from the Console VM or the DVM replacing ```$azureStackDirectoryTenant``` with the directory tenant that Azure Stack is registered to and ```$guestDirectoryTenant``` with the directory that needs to be onboarded to Azure Stack.
|
||||
|
||||
__NOTE:__ This cmd needs to be run **only once** throughout the entire life cycle of that Azure Stack installation. You do **not** have to run this step every time you need to add a new directory.
|
||||
|
||||
```powershell
|
||||
$adminARMEndpoint = "https://adminmanagement.<region>.<domain>"
|
||||
$azureStackDirectoryTenant = "<homeDirectoryTenant>.onmicrosoft.com"
|
||||
$guestDirectoryTenantToBeOnboarded = "<guestDirectoryTenant>.onmicrosoft.com"
|
||||
|
||||
Publish-AzureStackApplicationsToARM -AdminResourceManagerEndpoint $adminARMEndpoint `
|
||||
-DirectoryTenantName $azureStackDirectoryTenant
|
||||
```
|
||||
|
||||
#### Step 1: Onboard the Guest Directory Tenant to Azure Stack
|
||||
|
||||
This step will let Azure Resource manager know that it can accept users and service principals from the guest directory tenant.
|
||||
|
@ -72,9 +56,11 @@ This step will let Azure Resource manager know that it can accept users and serv
|
|||
$adminARMEndpoint = "https://adminmanagement.<region>.<domain>"
|
||||
$azureStackDirectoryTenant = "<homeDirectoryTenant>.onmicrosoft.com" # this is the primary tenant Azure Stack is registered to
|
||||
$guestDirectoryTenantToBeOnboarded = "<guestDirectoryTenant>.onmicrosoft.com" # this is the new tenant that needs to be onboarded to Azure Stack
|
||||
|
||||
$location = "local"
|
||||
Register-GuestDirectoryTenantToAzureStack -AdminResourceManagerEndpoint $adminARMEndpoint `
|
||||
-DirectoryTenantName $azureStackDirectoryTenant -GuestDirectoryTenantName $guestDirectoryTenantToBeOnboarded
|
||||
-DirectoryTenantName $azureStackDirectoryTenant `
|
||||
-GuestDirectoryTenantName $guestDirectoryTenantToBeOnboarded `
|
||||
-Location $location
|
||||
```
|
||||
|
||||
With this step, the work of the Azure Stack administrator is done.
|
||||
|
@ -85,7 +71,7 @@ The following steps need to be completed by the **Directory Tenant Administrator
|
|||
|
||||
#### Step 2: Providing UI-based consent to Azure Stack Portal and ARM
|
||||
|
||||
- This is an important step. Open up a web browser, and go to `https://portal.<region>.<domain>/guest/signup/<guestDirectoryName>`. Note that this is the directory tenant that needs to be onboarded to Azure Stack.
|
||||
- This is an important step. Open up a web browser, and go to `https://portal.<region>.<domain>/guest/signup/<guestDirectoryName>`. Note that this is the directory tenant that needs to be onboarded to Azure Stack.
|
||||
- This will take you to an AAD sign in page where you need to enter your credentials and click on 'Accept' on the consent screen.
|
||||
|
||||
#### Step 3: Registering Azure Stack applications with the Guest Directory
|
||||
|
@ -97,5 +83,5 @@ $tenantARMEndpoint = "https://management.<region>.<domain>"
|
|||
$guestDirectoryTenantName = "<guestDirectoryTenant>.onmicrosoft.com" # this is the new tenant that needs to be onboarded to Azure Stack
|
||||
|
||||
Register-AzureStackWithMyDirectoryTenant -TenantResourceManagerEndpoint $tenantARMEndpoint `
|
||||
-DirectoryTenantName $guestDirectoryTenantName -Verbose -Debug
|
||||
-DirectoryTenantName $guestDirectoryTenantName
|
||||
```
|
||||
|
|
Загрузка…
Ссылка в новой задаче