A Seamless CI/CD Journey: GitActions to GKE via Workload Identity Federation

Mohak
8 min readMay 29, 2024

Project Prelude

In today’s fast-paced digital landscape, delivering high-quality software at speed is paramount for businesses to stay competitive. Embracing DevOps practices has become essential, enabling teams to automate processes and accelerate the deployment of new features and updates. Recently, I had the opportunity to embark on an exciting project aimed at streamlining the DevOps workflow for a client. Leveraging cutting-edge technologies and best practices, we crafted an integrated CI/CD solution that not only enhances efficiency but also ensures the reliability and scalability of their software delivery pipeline.

Project Snapshot

At the heart of our solution lies a robust CI/CD pipeline that seamlessly orchestrates the build, testing, and deployment of software artifacts. Powered by GitActions, this pipeline automates repetitive tasks, enabling developers to focus on writing code without worrying about the intricacies of the deployment process. As part of our strategy to containerize applications for portability and consistency, we utilize Docker to package our software into lightweight, isolated containers.

To store and manage our Docker images, we employ Artifact Registry, a secure and scalable solution that integrates seamlessly with our CI/CD pipeline. With Artifact Registry, we ensure that our artifacts are stored securely and can be easily accessed by our deployment process.

For orchestrating our containerized workloads, we turn to Google Kubernetes Engine (GKE), a managed Kubernetes service that provides a highly available and scalable container orchestration platform. Leveraging GKE, we can deploy, manage, and scale our applications with ease, taking advantage of Kubernetes’ robust features for automated deployment, scaling, and monitoring.

One of the key challenges we addressed in our project was ensuring secure authentication and authorization for our workloads running on GKE. To achieve this, we implemented Workload Identity Federation, a mechanism that allows us to securely authenticate our workloads using Google Cloud IAM permissions.

In the following sections, we’ll delve deeper into each component of our integrated CI/CD solution, exploring the architecture, implementation details, and best practices we’ve adopted along the way. From GitActions to GKE, join us on our journey as we unravel the secrets to mastering DevOps and revolutionizing software delivery.

Key Concepts Clarification Round!

Workload Identity Federation

So this is the topic for which most audience came for. Here, don’t get confuse with Workload Identity in GKE, this is a secure 3rd party authentication mechanism. We use Workload Identity Federation to generate OIDC token and exchange them between the parties (GCP & GitActions).

This GitHub Action authenticates to Google Cloud. It supports authentication via a Google Cloud Service Account Key JSON and authentication via Workload Identity Federation.

Workload Identity Federation is recommended over Service Account Keys as it obviates the need to export a long-lived credential and establishes a trust delegation relationship between a particular GitHub Actions workflow invocation and permissions on Google Cloud. There are three ways to set up this GitHub Action to authenticate to Google Cloud:

  1. (Preferred) Direct Workload Identity Federation: We’ll explore this one!
  2. Workload Identity Federation through a Service Account: In this case, you will need to impersonate your service account with Workload Identity Pool.
  3. Service Account Key JSON: ALERT BAD PRACTICE ZONE !!
    So here you will need to download your service account key and it to your github action as a secret.

List of Actions:

> Let’s do Auth.

> Tip top a GitAction file.

> Drive through console.

> Result zone, See it working!

> What have we achieved?

Let’s do Auth!

Out of the 3 ways to accomplish auth, we’ll gonna use (Preferred) Direct Workload Identity Federation which is found to be the most secure and preferred way of authentication. In this, you will be exchanging temporary OIDC token with GitHub using GCP Secret Manager.

Setup your Workload Identity Pool, be smart enough to replace your GCP Project ID.

gcloud iam workload-identity-pools create "github" \
--project="" \
--location="global" \
--display-name="GitHub Actions Pool"

Here, create a OIDC provider inside my workload identity pool for exchanging tokens. You’ll need these attribute mapping for linking your Github repo with provider.

"google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner"

Here we are creating provider as OIDC .Replace your assertion.repository_owner to your github owner. Rest be the same!

gcloud iam workload-identity-pools providers create-oidc "my-repo" \
--project="" \
--location="global" \
--workload-identity-pool="github" \
--display-name="My GitHub repo Provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
--attribute-condition="assertion.repository_owner == 'my-github-owner" \
--issuer-uri="https://token.actions.githubusercontent.com"

Get your workload identity pool provider. You will need this in next step!

gcloud iam workload-identity-pools providers describe "my-repo" \
--project="" \
--location="global" \
--workload-identity-pool="github" \
--format="value(name)"

Here be smart enough to add yourself Security Admin as you’ll gonna create a secret and bind it with a member. First, go first create a secret using command

gcloud secrets create secret-id \
--replication-policy="automatic"

We need to bind secret to the Workload Identity Pool, a role “roles/secretmanager.secretAccessor”, now why we doing this is because GitActions will receive temporary tokens from here.

So from above steps pick up your Workload Identity Pool and add in this command, replacing PROJECT_NUM with your GCP project number, ORG_NAME with your GitHub organisation and REPO_NAME with your GitHub repo name.

--member="principalSet://iam.googleapis.com/projects/{$PROJECT_NUM}/locations/global/workloadIdentityPools/github/attribute.repository/{$ORG_NAME}/{$REPO_NAME}"

Mess it up in below command and run it!

gcloud secrets add-iam-policy-binding "my-secret" \
--project="" \
--role="roles/secretmanager.secretAccessor" \
--member="principalSet://iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/github/attribute.repository/{$ORGNAME}/{$REPONAME}"

Next, if you want to deploy your application to GKE. You gonna need a service account. Replace with your appropriate details.

gcloud iam service-accounts create SERVICE_ACCOUNT_NAME \
--description="DESCRIPTION" \
--display-name="DISPLAY_NAME"

Spice this step with your fine grained permissions. I’m gonna use fluent IAM roles with the service account like Kubernetes Engine Developer(roles/container.developer), Artifact Registry Admin(roles/artifactregistry.admin) and Artifact Registry Writer(roles/artifactregistry.writer).

gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
--role=roles/container.developer

gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
--role=roles/artifactregistry.admin

gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
--role=roles/artifactregistry.writer

Now Impersonate your service account with your Workload Identity Pool provider. Let’s do this via console.

Hit up on Grant Access

Now grab your Service Account and select principal to repository and your values.

Finally, you are ready with the authentication part. Rest is seemless!

Tip top a GitAction file.

Before this I assume you have your Dockerfile pushed in your Github repo and other necessary files. Add up your service account and workload identity provider here for authentication.

- id: 'auth'
name: 'Authenticate to GCP'
uses: 'google-github-actions/auth@v0.3.1'
with:
create_credentials_file: 'true'
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/github/providers/my-repo'
service_account: 'gitactions@{$PROJECT_ID}.iam.gserviceaccount.com'

Replace appropriate details and your githubAction yaml is ready.

Replacing keywords :

PROJECT_ID(GCP Project ID), GKE_CLUSTER(GKE Cluster Name), GKE_ZONE(GKE Cluster Location), DEPLOYMENT_NAME(GKE Deployment Name), IMAGE_NAME(Name of Docker Image), REPOSITORY(Artifact Registry path), GAR_LOCATION(Artifact Registry Location), REPO_NAME(Your GithubRepo Name),workload_identity_provider(Your workload identity provider with repo), service_account(your GCP SA).

name: Build and Deploy to GKE
on:
push:
branches: [ "main" ]

env:
PROJECT_ID: ''
GKE_CLUSTER: ''
GKE_ZONE: ''
DEPLOYMENT_NAME: ''
IMAGE_NAME: ''
REPOSITORY: ''
GAR_LOCATION: ''
REPO_NAME: ''

jobs:
setup-build-publish-deploy:
name: Setup, Build, Publish, and Deploy
runs-on: ubuntu-latest
environment: dev

permissions:
contents: 'read'
id-token: 'write'

steps:
- name: Checkout
uses: actions/checkout@v4

# In case, you encounter gke-auth-plugin error, uncomment below line of code.
#- name: Install gke-gcloud-auth-plugin
# run: |
# sudo apt-get update
# sudo apt-get install apt-transport-https ca-certificates gnupg curl
# curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
# echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
# sudo apt-get update && sudo apt-get install google-cloud-cli
# sudo apt-get install google-cloud-cli-gke-gcloud-auth-plugin -y

# In case, you wish to use Service Account Key for authentication.
#- id: 'auth'
# uses: 'google-github-actions/auth@v2'
# with:
# credentials_json: '${{ secrets.creds }}'

# For authentication using Workload Identity Federation
- id: 'auth'
name: 'Authenticate to GCP'
uses: 'google-github-actions/auth@v0.3.1'
with:
create_credentials_file: 'true'
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/github/providers/{{ env.REPO_NAME }}'
service_account: 'gitactions@${{ env.PROJECT_ID}}.iam.gserviceaccount.com'

# For Authenticating into GCP Artifact Registry
- name: Authenticating Artifact Registry
run: |-
gcloud auth configure-docker {{ env.GAR_LOCATION }}-docker.pkg.dev

# Build your DockerFile, you can replace below command according to your Dockerfile Location.
- name: Building Docker Image
run: |-
cd ../"$REPO_NAME"
ls -la
docker build -t "$REPOSITORY/$IMAGE_NAME:latest" .

# Push your Docker Image to Artifact Registry
- name: Push
run: |-
docker push "$REPOSITORY/$IMAGE_NAME:latest"

# Getting into GKE Cluster, In case of public cluster you can comment use_internal_ip
- name: Authenticating into GKE Cluster
uses: google-github-actions/get-gke-credentials@v2
with:
project_id: ${{ env.PROJECT_ID}}
cluster_name: ${{ env.GKE_CLUSTER }}
location: ${{ env.GKE_ZONE }}
use_internal_ip: 'true'

# Deploying your application to GKE.
- name: Deploying to GKE
run: |-
gcloud config set project ${{ env.PROJECT_ID}}
gcloud container clusters get-credentials ${{ env.GKE_CLUSTER }} --zone ${{ env.GKE_ZONE }} --project ${{ env.PROJECT_ID }}
cd ../"$REPO_NAME"
kubectl apply -f deployment.yaml
kubectl get pods -o wide
kubectl get services -o wide

Note as soon as you push your code to Github via “main” branch. Workflow will get trigger. Now, let’s create Github actions workflow.

Drive through console.

Now, go to your Github repo and click on actions. Next click on “set up a workflow yourself”.

Paste your GitActions yaml here, click on commit changes and push your code directly via “main” branch.

Result zone, See it working!

Now, your workflow has been triggered. Click on “Actions” tab again and you will see your Workflow running.

If everything is in place in your code. You will see Workflow getting executed step by step and your build will be success.

What have we achieved ?

Throughout this blog, we’ve explored the key components of our CI/CD pipeline, from Workload Identity Federation for secure authentication to Docker for containerization, and GitActions for automated workflows. .

Our journey has underscored the importance of automation, scalability, and security in modern software delivery. By embracing DevOps principles and adopting cloud-native tools and services, organizations can accelerate their digital transformation journey and stay ahead in today’s competitive landscape.

Thanks for being till here!
Queries ? I’ll be more happy to read them in the comments or reach me out on LinkedIn.

--

--