Llamar a la función Azure desde PowerShell
Esta publicación es parte de una serie sobre Azure Functions y PowerShell. ¡Consulta la lista de otras publicaciones de la serie!
Hasta ahora, hemos configurado una aplicación de funciones de Azure y hemos configurado la autenticación y autorización mediante Azure Active Directory. Ahora todo funciona siempre que esperemos que los usuarios accedan a nuestra función con un navegador. A menudo uso funciones para crear una API que se llama mediante código. En mi caso, el código suele ser un script de PowerShell, pero por el momento llamaremos al código una aplicación.
Registrar una aplicación cliente que llama a una API
Dado que nuestra aplicación se ejecuta en una computadora cliente en este escenario, no podemos confiar en la identidad de la aplicación, porque eso requeriría que la aplicación tenga algún tipo de secreto, ya sea una contraseña o un certificado, y ese secreto tendría que estar incrustado. en código, que a su vez puede ser extraído por el usuario de nuestra aplicación. Como siempre, ¡no hay secretos en el código!
Esto también significa que no podemos confiar en Azure AD para enviar el token a un punto final público especificado en un RedirectURI, ya que lo más probable es que nuestro cliente no esté expuesto a Internet.
Para solucionar esto, existe algo llamado cliente público. Básicamente, todas las aplicaciones que registremos se pueden configurar para que actúen como clientes si van a llamar a otro servicio. Las aplicaciones habituales utilizadas para servidores web y API se denominan clientes confidenciales, ya que su código se ejecuta en un entorno controlado donde podemos proporcionar un secreto (contraseña o certificado) de forma segura. Las aplicaciones de escritorio y los scripts no pueden almacenar un secreto de forma segura, y esto los convierte en parte de la categoría de clientes públicos.
En nuestro caso, necesitaremos una aplicación (o registro de aplicación) configurada utilizando el enfoque de cliente nativo. Un cliente nativo es sólo otra palabra para una aplicación como una aplicación de escritorio o un script. Ahora podríamos agregar esta configuración a nuestra aplicación actual, pero para mantener las cosas un poco más limpias y separadas, cubriré cómo crear una aplicación cliente separada.
¡Veamos un ejemplo de registro de aplicación!
Al igual que antes, ejecutaré la CLI de Azure desde PowerShell. Necesitaremos la identificación de la aplicación (también conocida como clientId) de nuestra aplicación de servidor que creamos antes. Esta vez configuraremos la uri de redireccionamiento en http://localhost.
Con la identificación de nuestra aplicación de servidor, podemos obtener todos los permisos oauth2 de la aplicación y usar el permiso user_impersonation para otorgar permiso a nuestra aplicación cliente para iniciar sesión en nuestra aplicación de servidor como usuario.
Una vez creada la aplicación, debemos configurar la versión solicitada de AccessToken en 2 tal como lo hicimos para la aplicación del servidor. Esto le indica a Azure AD que queremos un token de la versión 2, que necesitamos ya que especificamos v2.0 al final de nuestro Emisor al configurar la autenticación en modo avanzado en nuestro servicio de aplicaciones.
$ClientAppDisplayName = 'MySuperApiClient'
$ServerAppId = 'c491d3f8-0d15-4ea5-96fd-957601d579fa'
$RedirectUri = 'https://localhost'
# Get the server app
$ServerApp = az ad app show --id $ServerAppId | ConvertFrom-Json
# Get oAuthPermission for user_impersonation from server app
$oAuthPermissionId = az ad app show --id $ServerApp.AppId --query "oauth2Permissions[?value=='user_impersonation'].id" -o tsv
# Build part of a manifest for requiredResourceAccess
$requiredResourceAccess = @{
resourceAppId = $ServerApp.AppId
resourceAccess = @(
@{
id = $oAuthPermissionId
type = 'Scope'
}
)
} | ConvertTo-Json -AsArray -Depth 4 -Compress | ConvertTo-Json
# Pipe to ConvertTo-Json twice to escape all quotes, or az cli will remove them when parsing
# Register client application
$ClientApp = az ad app create --display-name $ClientAppDisplayName --native-app --reply-urls $RedirectUri --required-resource-accesses $requiredResourceAccess | ConvertFrom-Json
# Create a service principal for the application
$null = az ad sp create --id $ClientApp.appId | ConvertFrom-Json
# Consent the application for all users
$null = az ad app permission grant --id $ClientApp.AppId --api $ServerApp.AppId
# Disable implicit flow, we don't need this for authcode or device code flows
$null = az ad app update --id $ClientApp.AppId --set oauth2AllowIdTokenImplicitFlow=false *>&1
# Set application to use V2 access tokens
$Body = @{
api = @{
requestedAccessTokenVersion = 2
}
} | ConvertTo-Json -Compress | ConvertTo-Json
# Pipe to ConvertTo-Json twice to escape all quotes, or az cli will remove them when parsing
$null = az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$($ClientApp.objectId)" --body $Body --headers "Content-Type=application/json"
# Output the ClientId for use later
Write-Output $ClientApp.AppId
Ahora necesitamos configurar nuestra aplicación cliente para que requiera que a los usuarios se les asigne un rol tal como lo hicimos con nuestra aplicación de servidor.
$ClientAppId = '479fe3c1-a9a4-4098-b343-db98e7c6e81b'
az ad sp update --id $ClientAppId --set appRoleAssignmentRequired=true
Y nuevamente podemos asignar la función de acceso predeterminada a los usuarios tal como lo hicimos con la aplicación del servidor anterior, esta vez usando nuestro clent appId en lugar del servidor.
$AppId = '479fe3c1-a9a4-4098-b343-db98e7c6e81b'
$UserPrincipalName = '[email '
$App = az ad sp show --id $AppId | ConvertFrom-Json
$principalId = az ad user show --id $UserPrincipalName --query 'objectId' -o tsv
$Body = @{
appRoleId = [Guid]::Empty.Guid
principalId = $principalId
resourceId = $App.objectId
} | ConvertTo-Json -Compress | ConvertTo-Json
az rest --method post --uri "https://graph.microsoft.com/v1.0/users/$UserPrincipalName/appRoleAssignments" --body $Body --headers "Content-Type=application/json"
¡Y eso es todo! Ahora que tenemos un servidor y una aplicación cliente, los hemos configurado para que requieran que se asigne un rol a los usuarios. Obtener acceso a la aplicación del servidor permitirá a los usuarios navegar hasta el servidor usando un navegador, otorgar acceso a la aplicación del cliente permitirá al usuario acceder al servidor usando la aplicación del cliente.
Ahora, por supuesto, podemos combinar estas aplicaciones en una simplemente agregando una plataforma de cliente nativa a nuestra aplicación de servidor, lo que probablemente sería la mejor manera de hacerlo si está creando una API usando Azure Functions, pero elegí usar dos aplicaciones en Mi ejemplo cubre ambos lados, a veces solo necesitamos configurar una aplicación de servidor, otras veces solo necesitamos configurar una aplicación de cliente y, a veces, las aplicaciones de cliente necesitan permisos para varias API.
Cubramos rápidamente la última parte de esta publicación bastante extensa. ¿Cómo accedo a mi API usando PowerShell?
Obtenga tokens OAuth de AzureAD con PowerShell
Hay muchas formas de obtener tokens de oauth de Azure AD. Esta vez voy a utilizar el módulo de PowerShell MSAL.PS. MSAL.PS es un contenedor de la biblioteca cliente MSAL para dotnet y tiene todo lo que necesitamos integrado, ¡listo para consumir!
Comience instalando el módulo.
Install-Module MSAL.PS
Ahora necesitamos el ID de cliente de la aplicación para la que solicitamos un token, el ID de inquilino de nuestro inquilino y una lista de ámbitos. El URI de redireccionamiento debe coincidir con un URI de redireccionamiento configurado en nuestra aplicación.
$ClientAppId = '479fe3c1-a9a4-4098-b343-db98e7c6e81b'
$TenantId = "b5fbba89-c11c-4ba3-baf3-67fb6d5fb61f"
$Scopes = 'https://mysuperapi.azurewebsites.net/user_impersonation'
$RedirectUri = 'http://localhost'
Import-Module 'MSAL.PS' -ErrorAction 'Stop'
$PublicClient = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($ClientAppId).WithRedirectUri($RedirectUri).Build()
$token = Get-MsalToken -PublicClientApplication $PublicClient -TenantId $TenantId -Scopes $Scopes
Invoke-RestMethod -Uri "https://mysuperapi.azurewebsites.net/api/mysuperfunc" -Headers @{Authorization = "Bearer $($token.AccessToken)" }