Part 5. Provision Azure resources with Terraform from GCP with token exchange

Λ\: Clément Bosc - Feb 14 '23 - - Dev Community

A small bonus for a use case we had in my project : all our CI is on GCP, using Cloud Build and I wanted to create Azure Resource along with Google resources with Terraform by exchanging my Cloud Build service account identity for an equivalent Azure identity.

Provision Azure resources with Terraform from GCP with token exchange

If you don’t know what I am referring to with identity federation and multi-cloud token exchange, and to understand the prerequisite, make sure to catch up with the previous article of the links above.

Create Azure resources with Terraform from GCP

Remember the Part 3 of this series, we needed to exchange a Google identity token for an Azure access token and we used a curl request to the AAD Authorization Server, let’s do the same with Terraform, using the http provider !

The http provider is a provider maintained by Hashicorp that cannot create resource, but only make HTTP request on the form of Terraform Data Sources.

1. Add Terraform providers

First let’s add the required providers : google for creating GCP resources and get the ID token, http to exchange the token and azurerm to create Azure resource.

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "4.52.0"
    }
    http = {
      source  = "hashicorp/http"
      version = "3.2.1"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "3.42.0"
    }
  }
}

# Simply configure the Google provider and use Application Default Credentials
provider "google" {
  project = var.google_project_id
}
Enter fullscreen mode Exit fullscreen mode

2. Generate the Google ID token (JWT token)

data "google_service_account_id_token" "oidc" {
    # the GCP SA mapped to Azure App Registration
  target_service_account = var.target_service_account
  target_audience        = "api://AzureADTokenExchange"
}
Enter fullscreen mode Exit fullscreen mode

3. Call the Azure Authorization Server to Exchange the access token

Here we first build the payload in a local variable using the same parameters described in Part 3 of this series. We finally query the Authorization Server with the data "http" "azure_id_token" Data Source.

locals {
  azure_id_token_request_body_obj = {
    client_id             = var.client_id
    scope                 = ".default"
    client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    client_assertion      = data.google_service_account_id_token.oidc.id_token
    grant_type            = "client_credentials"
  }

  azure_id_token_request_body = join("&", formatlist("%s=%s", keys(local.azure_id_token_request_body_obj), values(local.azure_id_token_request_body_obj)))
}

data "http" "azure_id_token" {
  url    = "${var.aad_authority}${var.azure_tenant_id}/oauth2/v2.0/token"
  method = "GET"

  request_headers = {
    Content-Type = "application/x-www-form-urlencoded"
  }

  request_body = local.azure_id_token_request_body
}
Enter fullscreen mode Exit fullscreen mode

4. C*onfigure Azure provider and create resource !*

You can now configure the azurerm provider by using the oidc_token with the resulting exchanged access_token. You are ready to go with Azure resource creation along with GCP resource creation ! Make sure to give the correct IAM role to the target App Registration depending of what resources you want to create.

Here we create a resource_group and an Azure Storage Account 💾

provider "azurerm" {
    features {}

    oidc_token = jsondecode(data.http.azure_id_token.body).access_token
}

resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "West Europe"
}

resource "azurerm_storage_account" "example" {
  name                     = "storageaccountname"
  resource_group_name      = azurerm_resource_group.example.name
  location                 = azurerm_resource_group.example.location
  account_tier             = "Standard"
  account_replication_type = "GRS"

  tags = {
    environment = "staging"
  }
}

Enter fullscreen mode Exit fullscreen mode

It’s the end of this multi-cloud service-to-service identity federation series of articles.

After this series of articles you know how to setup identity federation between GCP and Azure in a secure way. We saw what are access tokens and ID tokens and how they are used by Cloud providers. We saw the steps to exchange a Google ID token for an Azure access token and how to impersonate a GCP service account from an Azure App registration using Workload Identity Federation. Finally with Part 4 and Part 5 we detailed concret implementation in Python and Terraform for your production applications.

If you keep exposing service account keys or secrets after this, you have no excuse !

Thanks for reading! I'm Clement, Data Engineer at Stack Labs.

If you want to discover the Stack Labs Data Platform or join an enthusiast data engineering team and work on awesome technical subjects, please contact us.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .