Skip to content

Instantly share code, notes, and snippets.

@simongottschlag
Created February 10, 2026 20:48
Show Gist options
  • Select an option

  • Save simongottschlag/946515cdaf01f2ce6271aa116f557aed to your computer and use it in GitHub Desktop.

Select an option

Save simongottschlag/946515cdaf01f2ce6271aa116f557aed to your computer and use it in GitHub Desktop.
Terraform example for accessing a separate Entra tenant using an UAMI and Multi Tenant app
resource "azapi_resource" "resource_group_cae" {
type = "Microsoft.Resources/resourceGroups@2025-04-01"
name = "rg-${var.environment}-${var.location_short}-cae"
location = var.location
response_export_values = {}
}
resource "azapi_resource" "container_app_environment" {
type = "Microsoft.App/managedEnvironments@2025-10-02-preview"
name = "cae-${var.environment}-${var.location_short}"
parent_id = azapi_resource.resource_group_cae.id
location = var.location
body = {
properties = {
publicNetworkAccess = "Enabled"
workloadProfiles = [
{
workloadProfileType = "Consumption"
name = "Consumption"
}
]
}
}
response_export_values = {}
}
resource "azapi_resource" "user_assigned_identity_container_app_azure_cli" {
type = "Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview"
name = "uami-ca-${var.environment}-${var.location_short}-azure-cli"
parent_id = azapi_resource.resource_group_cae.id
location = var.location
response_export_values = {
client_id = "properties.clientId"
principal_id = "properties.principalId"
}
}
resource "azapi_resource" "container_app_azure_cli" {
type = "Microsoft.App/containerApps@2025-10-02-preview"
parent_id = azapi_resource.resource_group_cae.id
name = "ca-${var.environment}-${var.location_short}-azure-cli"
location = azapi_resource.container_app_environment.location
identity {
type = "UserAssigned"
identity_ids = [azapi_resource.user_assigned_identity_container_app_azure_cli.id]
}
body = {
properties = {
configuration = {
activeRevisionsMode = "Single"
}
managedEnvironmentId = azapi_resource.container_app_environment.id
template = {
containers = [
{
name = "azure-cli"
image = "mcr.microsoft.com/azure-cli:2.83.0-azurelinux3.0"
command = [
"sleep",
]
args = [
"infinity",
]
env = [
{
name = "AZURE_CLIENT_ID"
value = azapi_resource.user_assigned_identity_container_app_azure_cli.output.client_id
},
]
resources = {
cpu = 0.25
memory = "0.5Gi"
}
},
]
scale = {
minReplicas = 1
maxReplicas = 1
}
}
}
}
response_export_values = {}
}
output "container_app_azure_cli" {
value = <<EOT
az login --identity --allow-no-subscriptions --client-id $AZURE_CLIENT_ID
UAMI_ACCESS_TOKEN=$(az account get-access-token --scope api://AzureADTokenExchange/.default --output tsv --query accessToken)
CUSTOMER_ACCESS_TOKEN=$(curl -sS -X POST "https://login.microsoftonline.com/${var.customer_tenant_id}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_id=${azuread_application.provider_app.client_id}" \
--data-urlencode "scope=https://graph.microsoft.com/.default" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
--data-urlencode "client_assertion=$${UAMI_ACCESS_TOKEN}" | jq -r '.access_token')
curl -sS https://graph.microsoft.com/v1.0/applications \
-H "Authorization: Bearer $${CUSTOMER_ACCESS_TOKEN}" \
-H "Accept: application/json" | jq
EOT
}
resource "azuread_service_principal" "customer_provider_app" {
provider = azuread.customer_entra
client_id = azuread_application.provider_app.client_id
}
data "azuread_application_published_app_ids" "customer_well_known" {
provider = azuread.customer_entra
}
data "azuread_service_principal" "msgraph" {
provider = azuread.customer_entra
client_id = data.azuread_application_published_app_ids.customer_well_known.result.MicrosoftGraph
}
resource "azuread_app_role_assignment" "customer_provider_app_msgraph_directory_read_all" {
provider = azuread.customer_entra
app_role_id = data.azuread_service_principal.msgraph.app_role_ids["Directory.Read.All"]
principal_object_id = azuread_service_principal.customer_provider_app.object_id
resource_object_id = data.azuread_service_principal.msgraph.object_id
}
terraform {
required_providers {
azapi = {
source = "Azure/azapi"
version = "2.8.0"
}
azuread = {
source = "hashicorp/azuread"
version = "3.7.0"
}
}
}
provider "azapi" {}
provider "azuread" {
alias = "provider_entra"
tenant_id = var.provider_tenant_id
}
provider "azuread" {
alias = "customer_entra"
tenant_id = var.customer_tenant_id
}
resource "azuread_application" "provider_app" {
provider = azuread.provider_entra
display_name = "provider-app"
identifier_uris = ["api://provider-app"]
sign_in_audience = "AzureADMultipleOrgs"
api {
requested_access_token_version = 2
}
}
resource "azuread_application_federated_identity_credential" "container_app_azure_cli" {
provider = azuread.provider_entra
application_id = azuread_application.provider_app.id
display_name = "azure-cli"
audiences = ["api://AzureADTokenExchange"]
issuer = "https://login.microsoftonline.com/${var.provider_tenant_id}/v2.0"
subject = azapi_resource.user_assigned_identity_container_app_azure_cli.output.principal_id
}
variable "provider_tenant_id" {
description = "The tenant ID of the provider subscription."
type = string
}
variable "provider_subscription_id" {
description = "The subscription ID of the provider subscription."
type = string
}
variable "customer_tenant_id" {
description = "The tenant ID of the customer subscription."
type = string
}
variable "environment" {
description = "The environment to deploy to. This can be used to differentiate between different environments such as dev, test, and prod."
type = string
}
variable "location" {
description = "The Azure region to deploy resources to."
type = string
}
variable "location_short" {
description = "The short name of the Azure region to deploy resources to. This is used for naming resources that have a length limit."
type = string
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment