GitHub Actions owned runners on AWS/Azure + Workload Identity Federation on GCP

Gpetruccelli
Zencore Engineering
5 min readJan 17, 2024

--

GitHub Actions has gained widespread popularity as a go-to choice for continuous integration and continuous delivery (CI/CD) platforms. Traditionally, service account keys were utilized to automate software delivery to Google Cloud, Azure, and AWS. However, the risk of accidental leaks associated with service account keys has prompted many organizations to disable their use. To address this challenge, Google Cloud introduced Workload Identity Federation (WIF), enabling keyless authentication from GitHub Actions. The same principle applies when configuring WIF for Azure and AWS. The configuration process is relatively straightforward when you use GitHub Action owned runners, but it’s not that easy when you need or decide to host the GitHub Actions runners in your own infrastructure.

In this article, I’m going to explain these configurations for AWS, Azure, and GitHub Actions using Terraform. We’ll delve into the step-by-step process of setting up Workload Identity Federation (WIF) to facilitate keyless authentication with each of these cloud platforms. Terraform, a powerful infrastructure-as-code tool, will be our primary vehicle for configuring and managing these authentication mechanisms seamlessly. By the end of this tutorial, you should have a clear understanding of how to implement WIF for Google Cloud, AWS, and Azure in conjunction with GitHub Actions, streamlining your CI/CD workflows and bolstering the security of your cloud-based operations.

Accessing from other Clouds to GCP

This section provides an overview of how you can assign credentials and authenticate to GCP first from Amazon Web Services (AWS) and then from Azure, without the use of service account keys. For our example we’ll be assigning permissions to upload docker images to Artifact Registry.

AWS

Step 1: GCP Service Account Creation

The GCP service account will be what our AWS application will authenticate with in order to access Artifact Registry. For the below example, we are using a Google Terraform Module.

locals {
service_accounts = {
"artifact-githubactions-sa" = {
project = "project-id"
prefix = "gha"
names = ["artifact-githubactions-sa"]
description = "Artifact Registry / GitHub Actions Integration SA"
project_roles = ["project-id=>roles/artifactregistry.writer"]
},
}
}

module "service_accounts" {
source = "terraform-google-modules/service-accounts/google"
version = "4.2.1"
for_each = local.service_accounts
project_id = each.value.project
prefix = each.value.prefix
names = each.value.names
description = each.value.description
project_roles = each.value.project_roles
}

Step 2: AWS IAM Role Creation

Now we need to create, or use an existing AWS IAM role that the application will assume while running on AWS. This role will be what we use to exchange tokens with GCP and access our Artifact Registry.

aws iam create-role --role-name "git-runners"  \      
--assume-role-policy-document "/runners/assume.json" \
--description "Allows Github Runners to write Artifact Registry"

Here is an example of the JSON file to create the role:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "sts:AssumeRole",
}
]
}

Of course we need to attach an IAM policy as needed and desired to the new role:

aws iam attach-role-policy --role-name "git-runners" \
--policy-arn arn:aws:iam::123456789012:policy/PolicyForRole

Step 3: Workload Identity Pool Creation

As I mentioned before, we are going to create the Workload Identity Pool (logical containers for external identities, like AWS roles) with Terraform.

resource "google_iam_workload_identity_pool" "pool" {
project = "your-project-id"
workload_identity_pool_id = "aws-pool"
}

Step 4: Workload Identity Provider Creation

In the same way, using Terraform, we are going to create our AWS identity provider in GCP.

The following example contains the flag — attribute-condition and is not required to create an identity pool provider. We have included this additional security configuration as an example of one way to restrict GCP token generation.

resource "google_iam_workload_identity_pool_provider" "aws_provider" {
project = "your-project-id"
workload_identity_pool_id = google_iam_workload_identity_pool.pool.workload_identity_pool_id
workload_identity_pool_provider_id = "aws-provider"
attribute_condition = "assertion.arn.startsWith('arn:aws:sts::123456789012:assumed-role/git-runners'"
aws {
account_id = "123456789012"
}
}

Step 5: GCP Service Account Impersonation Binding

Finally, we need to provide the AWS role the ability to impersonate our GCP service account. We do this by binding the IAM role roles/iam.workloadIdentityUser to the GCP service account, also using Terraform.

resource "google_service_account_iam_member" "wi_user" {
service_account_id = module.service_accounts["artifact-githubactions-sa"].service_account.id
role = "roles/iam.workloadIdentityUser"
member = "principalSet://iam.googleapis.com/projects/210987654321/locations/global/workloadIdentityPools/aws-pool/attribute.aws_role/arn:aws:sts::123456789012:assumed-role/git-runners"
}

Azure

The steps for the GCP config are pretty similar, so for example the SA creation it’s the same. We will add here the steps that are different for Azure.

Step 1: Azure Service Principal Creation

Firstly, you’ll need to create an Azure Service Principal that GitHub Actions can use to authenticate with Azure.

# Set the subscription to the desired subscription ID
az account set --subscription <Subscription-ID>

# Create a service principal and assign it a contributor role to a specific resource group
az ad sp create-for-rbac --name git-runners --role contributor --scopes /subscriptions/<Subscription-ID>/resourceGroups/<Resource-Group-Name>

Step 2: Workload Identity Pool Creation

Similar than AWS, we need to create a pool for Azure:

resource "google_iam_workload_identity_pool" "pool" {
project = "your-project-id"
workload_identity_pool_id = "azure-pool"
}

Step 3: Azure Workload Identity Provider Creation

Create an Azure Workload Identity Provider in GCP using Terraform:

resource "google_iam_workload_identity_pool_provider" "azure_provider" {
project = "your-project-id"
workload_identity_pool_id = google_iam_workload_identity_pool.pool.workload_identity_pool_id
workload_identity_pool_provider_id = "azure-provider"
attribute_condition = "assertion.iss.endsWith('/<AZURE-TENANT-ID>/v2.0')"
azure {
tenant_id = "<AZURE-TENANT-ID>"
}
}

Step 4: Azure Service Principal Impersonation Binding

Bind the Azure Service Principal the ability to impersonate your GCP service account using Terraform:

resource "google_service_account_iam_member" "wi_user_azure" {
service_account_id = module.service_accounts["artifact-githubactions-sa"].service_account.id
role = "roles/iam.workloadIdentityUser"
member = "principalSet://iam.googleapis.com/projects/210987654321/locations/global/workloadIdentityPools/azure-pool/attribute.azure_tenant_id/<AZURE-TENANT-ID>"
}

Conclusion

In conclusion, implementing Workload Identity Federation (WIF) with GitHub Actions using Terraform provides a robust and secure approach to CI/CD workflows. By leveraging WIF for Google Cloud, AWS, and Azure, organizations can eliminate the risks associated with service account keys while ensuring seamless integration between their infrastructure and cloud platforms. This keyless authentication not only enhances the security posture of CI/CD workflows but also simplifies the management of credentials across diverse cloud environments. As you adopt these configurations, you empower your development teams to deliver software with confidence and efficiency.

--

--