Invitar a usuarios externos a un tenant de Azure AD a través de Microsoft Graph y Azure CLI

Hace ya algún tiempo compartí contigo cómo podías hacer llamadas a la API de Microsoft Graph utilizando Azure CLI, lo cual simplifica el manejo del token necesario para las acciones con esta. En este artículo te cuento cómo he usado esta misma técnica para invitar a usuarios externos a un tenant de Azure AD de manera programática, ya que esta acción no está disponible a través de Azure CLI directamente.

Crear un service principal

Si lo que quieres es automatizar este proceso, es necesario crear un service principal al que le daremos los permisos necesarios para llevar a cabo las llamadas a Microsoft Graph:

# Az login with an Azure AD administrator
az login --allow-no-subscriptions
# Create a service principal
SP=$(az ad sp create-for-rbac --name "ms-graph-client" --skip-assignment)
# Get the client id
CLIENT_ID=$(echo $SP | jq -r .appId)
# Get the client secret
CLIENT_SECRET=$(echo $SP | jq -r .password)
# Get the tenant id
TENANT_ID=$(echo $SP | jq -r .tenant)

Nota: en este ejemplo estoy usando la herramienta jq, para recuperar las propiedades que me interesan del JSON de vuelta cuando se crea un service principal.

Este debería de tener los permisos mínimos necesarios para que solo sea posible efectuar esta llamada en concreto.

Asignar los permisos de Microsoft Graph al service principal

Según la documentación de esta API, necesito los siguientes roles para crear invitados: User.Invite.All, User.ReadWrite.All, Directory.ReadWrite.All. Puedes agregarlos también a través de Azure CLI, como ya te mostré en este otro artículo:

# Add Microsoft Graph Permissions
# https://docs.microsoft.com/es-es/graph/permissions-reference#retrieving-permission-ids
USER_READ_WRITE_APP_PERMISSION=$(az ad sp list --query "[?appDisplayName=='Microsoft Graph'].{permissions:appRoles}[0].permissions[?value=='User.ReadWrite.All'].id" --all | jq -r '.[]')
USER_INVITE_ALL_APP_PERMISSION=$(az ad sp list --query "[?appDisplayName=='Microsoft Graph'].{permissions:appRoles}[0].permissions[?value=='User.Invite.All'].id" --all | jq -r '.[]')
DIRECTORY_READ_ALL_APP_PERMISSION=$(az ad sp list --query "[?appDisplayName=='Microsoft Graph'].{permissions:appRoles}[0].permissions[?value=='Directory.Read.All'].id" --all | jq -r '.[]')
# Assign the permission to the service principal
az ad app permission add --id $CLIENT_ID --api 00000003-0000-0000-c000-000000000000 --api-permissions $USER_READ_WRITE_APP_PERMISSION=Role  $USER_INVITE_ALL_APP_PERMISSION=Role $DIRECTORY_READ_ALL_APP_PERMISSION=Role
# Grant permissions to the service principal
az ad app permission admin-consent --id $CLIENT_ID
# check the permissions
az ad app permission list --id $CLIENT_ID

Iniciar sesión en Azure CLI con el service principal

Para que Azure CLI obtenga el access token que corresponde, debes iniciar sesión con el service principal que creaste:

#delete cached token
rm -f ~/.azure/msal_token_cache.json
# Login using service principal
az login --service-principal -u $CLIENT_ID -p $CLIENT_SECRET --tenant $TENANT_ID --allow-no-subscriptions

Si quieres puedes comprobar el token generado para esta sesión y así verificar que tiene los roles que necesitas:

# Get the access token you are using for this session
ACCESS_TOKEN=$(az account get-access-token --resource-type ms-graph | jq -r '.accessToken')
echo "http://jwt.ms/#access_token=$ACCESS_TOKEN"

Crear invitaciones a través de Microsoft Graph y Azure CLI

Ahora ya tienes todo lo que necesitas para crear tus invitados en tu tenant de Azure AD. Lo último que tienes que hacer es invitarlos 😙

# Create a guest user via Microsoft Graph
# https://learn.microsoft.com/en-us/graph/api/invitation-post?view=graph-rest-1.0&tabs=http
REQUEST_URL="https://graph.microsoft.com/v1.0/invitations"
GUEST_USER_EMAIL="<GUEST_EMAIL>"
GUEST_USER_NAME="<GUEST_USER_NAME>"
az rest --method post --uri $REQUEST_URL \
--body "{\"invitedUserEmailAddress\": \"$GUEST_USER_EMAIL\", 
        \"inviteRedirectUrl\": \"https://portal.azure.com\", 
        \"sendInvitationMessage\": true, 
        \"invitedUserDisplayName\": \"$GUEST_USER_NAME\"}"

En este enlace tienes todas las propiedades que puedes añadir al body de la llamada.

¡Saludos!