How does Azure PowerShell work with username/passw

2020-07-06 06:18发布

问题:

I want to understand how Azure PowerShell makes Azure API calls using AAD username/password based credentials.

My understanding is that an app needs a client-id before it can make Azure API calls. This client ID must be registered with a user’s account.

Does Azure PowerShell have a client-id? If so, how does it work without explicitly registering it with Azure accounts? Is it a special id that has been whitelisted across accounts?

回答1:

You don't need to create an application registration in Azure Active Directory for Azure Powershell. To leverage username/password credentials of an Azure AD user, you can use the Add-AzureAccount cmdlet:

$username = "admin@your_account.onmicrosoft.com"
$password = "SuperSecretPassword" | ConvertTo-SecureString -AsPlainText -Force

$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password 
Add-AzureAccount -Credential $credential 

Just make sure you have the latest version of the Azure PowerShell module installed, and the account you're using is an organizational account (as opposed to a Microsoft account) or a native Azure AD user.

To answer the last part of your question, there is a well-known client ID for Azure PowerShell ("1950a258-227b-4e31-a9cf-717495945fc2"). It's hard coded in the Azure Powershell module and can be used to authenticate PowerShell scripts to Azure AD when they invoke the Azure Management APIs directly:

# Load Active Directory Authentication Library (ADAL) Assemblies
$adal = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalforms = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll"
[System.Reflection.Assembly]::LoadFrom($adal)
[System.Reflection.Assembly]::LoadFrom($adalforms)

# Set Azure AD Tenant name
$adTenant = "yourtenant.onmicrosoft.com" 

# Set well-known client ID for Azure PowerShell
$clientId = "1950a258-227b-4e31-a9cf-717495945fc2" 

# Set redirect URI for Azure PowerShell
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"

# Set Resource URI to Azure Service Management API
$resourceAppIdURI = "https://management.core.windows.net/"

# Set Authority to Azure AD Tenant
$authority = "https://login.windows.net/$adTenant"

# Set user credentials (*** obviously you wouldn't have the password in clear text in a production script ***)
$userName = "admin@your_tenant.onmicrosoft.com"
$password = "SecretPassword"
$creds = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList $userName,$password

# Create AuthenticationContext tied to Azure AD Tenant
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority

# Acquire token
$authResult = $authContext.AcquireToken($resourceAppIdURI,$clientId,$creds)


回答2:

You can also get the desired behavior where users of your applications need to provide only user name and password just like Azure PowerShell.

This is what you can do too.

a) In order for users, outside your tenant directory, to access your web api the web api needs to be multi-tenant. Let's say you went ahead and configured this in Azure AD.

b) Next, you create a native application in your directory with delegated permissions to your web api.

c) Then, you add the native client app ID into the web api knownClientIds property by updating its manifest. The reason you do this is for consent flow, which after user's approval gets the Web API SPN provisioned in the user's directory. This needs to happen for multi-tenant apps. Read more here.

Now, let's say you ship an application that makes calls to the Web API (e.g. Azure powerShell). You can simply hardcode the client ID and APP ID URI (redirect URI) in your application and users don't need to provide it.

But the most important thing now is to use the correct ADAL API. If you use the below API, this will raise an error

var uc = new UserCredential(userName, userPassword); authresult = context.AcquireToken(webapiresourceid, nativeclientID, uc);

AADSTS65001: The user or administrator has not consented to use the application with ID '<app id guid>'. Send an interactive authorization request for this user and resource.

The reason is the user has to consent since the Web API is multi-tenant. For native client applications this is not needed since they are multi-tenant by default.

So, you have to use the below API and this will raise consent form (only first time)

result = context.AcquireToken( webapiresourceid, nativeclientID, new Uri("nativeclientRedirectURI"), PromptBehavior.Auto, new UserIdentifier(userName, UserIdentifierType.RequiredDisplayableId));

Note, native client ID and native client URI both can be hardcoded just like how Azure PowerShell did it in above answer.