This repository contains the setup for deploying a Spring Boot application Docker image to Google Cloud Platform's Artifact Registry using GitHub Actions and Workload Identity Federation. There are two options for setting up the infrastructure:
Option 1: Using GCP CLI Commands Option 2: Using Terraform
Before starting the setup, ensure you have the following prerequisites in place:
-
Google Cloud Platform (GCP) Account
- Active billing account enabled
- Owner or Editor role permissions
- Project created in GCP Console
-
GitHub Account
- Repository with Spring Boot application code
- Permissions to add GitHub Actions workflows
- Access to repository settings
# Download and install from:
https://cloud.google.com/sdk/docs/install
# Verify installation
gcloud --version
# Initialize and authenticate
gcloud init
gcloud auth login
# Download and install from:
https://docs.docker.com/get-docker/
# Verify installation
docker --version
# Download and install from:
https://developer.hashicorp.com/terraform/downloads
# Verify installation
terraform --version
# Windows single line command
gcloud services enable iamcredentials.googleapis.com && gcloud services enable iam.googleapis.com && gcloud services enable artifactregistry.googleapis.com
# Expanded version
gcloud services enable iamcredentials.googleapis.com
gcloud services enable iam.googleapis.com
gcloud services enable artifactregistry.googleapis.com
- Set your project ID:
# List your projects
gcloud projects list
# Set the active project
gcloud config set project YOUR_PROJECT_ID
- Verify project configuration:
# Check current project
gcloud config get-value project
# Check enabled APIs
gcloud services list --enabled
Ensure your GCP account has the following roles:
roles/iam.workloadIdentityPoolAdmin
roles/iam.serviceAccountAdmin
roles/artifactregistry.admin
# Grant necessary roles (if needed)
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="user:[email protected]" \
--role="roles/iam.workloadIdentityPoolAdmin"
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="user:[email protected]" \
--role="roles/iam.serviceAccountAdmin"
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="user:[email protected]" \
--role="roles/artifactregistry.admin"
- Dockerfile in repository root
- Valid Spring Boot application
.github/workflows
directory (will be created during setup)
Make note of the following values that you'll need during setup:
YOUR_PROJECT_ID
: Your GCP project IDYOUR_PROJECT_NUMBER
: Your GCP project number (can be found in project settings)YOUR_GITHUB_USERNAME
: Your GitHub usernameYOUR_REPO_NAME
: Your GitHub repository nameYOUR_REPOSITORY
: Name for your Artifact Registry repositoryYOUR_IMAGE_NAME
: Name for your Docker image
Run these commands to ensure everything is properly installed and configured:
# Check GCloud SDK
gcloud --version
# Check Docker
docker --version
# Check Terraform (if using Option 2)
terraform --version
# Verify GCP authentication
gcloud auth list
# Check project configuration
gcloud config get-value project
Once all prerequisites are met, you can proceed with either:
- Option 1: Setup using GCP CLI Commands
- Option 2: Setup using Terraform
Choose the option that best fits your infrastructure management preferences.
- Google Cloud Platform account with billing enabled
- GitHub repository with Spring Boot application
- gcloud CLI installed and initialized
- Docker installed locally (for testing)
- Required GCP APIs enabled:
# Windows single line gcloud services enable iamcredentials.googleapis.com && gcloud services enable iam.googleapis.com && gcloud services enable artifactregistry.googleapis.com
# Expanded version gcloud services enable iamcredentials.googleapis.com gcloud services enable iam.googleapis.com gcloud services enable artifactregistry.googleapis.com
# Verify current project
gcloud config get-value project
# Set project if needed
gcloud config set project YOUR_PROJECT_ID
# Windows single line
gcloud iam workload-identity-pools create github-pool-new --project=YOUR_PROJECT_ID --location=global --display-name="GitHub Actions Pool"
# Expanded version
gcloud iam workload-identity-pools create "github-pool-new" \
--project="YOUR_PROJECT_ID" \
--location="global" \
--display-name="GitHub Actions Pool"
# Windows single line
gcloud iam workload-identity-pools describe github-pool-new --project=YOUR_PROJECT_ID --location=global
# Expanded version
gcloud iam workload-identity-pools describe "github-pool-new" \
--project="YOUR_PROJECT_ID" \
--location="global"
# Windows single line
gcloud iam workload-identity-pools providers create-oidc github-provider --project=YOUR_PROJECT_ID --location=global --workload-identity-pool=github-pool-new --display-name="GitHub provider" --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" --issuer-uri="https://token.actions.githubusercontent.com" --attribute-condition="assertion.repository=='YOUR_GITHUB_USERNAME/YOUR_REPO_NAME'"
# Expanded version
gcloud iam workload-identity-pools providers create-oidc "github-provider" \
--project="YOUR_PROJECT_ID" \
--location="global" \
--workload-identity-pool="github-pool-new" \
--display-name="GitHub provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-condition="assertion.repository=='YOUR_GITHUB_USERNAME/YOUR_REPO_NAME'"
# Windows single line
gcloud iam service-accounts create github-actions --project=YOUR_PROJECT_ID --display-name="GitHub Actions Service Account"
# Expanded version
gcloud iam service-accounts create github-actions \
--project="YOUR_PROJECT_ID" \
--display-name="GitHub Actions Service Account"
# Windows single line
gcloud artifacts repositories create YOUR_REPOSITORY --project=YOUR_PROJECT_ID --repository-format=docker --location=asia-south1 --description="Docker repository for Spring Boot applications"
# Expanded version
gcloud artifacts repositories create YOUR_REPOSITORY \
--project="YOUR_PROJECT_ID" \
--repository-format=docker \
--location=asia-south1 \
--description="Docker repository for Spring Boot applications"
# Windows single line
gcloud artifacts repositories add-iam-policy-binding YOUR_REPOSITORY --project=YOUR_PROJECT_ID --location=asia-south1 --member="serviceAccount:github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com" --role="roles/artifactregistry.writer"
# Get your project number (needed for next step)
gcloud projects describe YOUR_PROJECT_ID --format="value(projectNumber)"
# Windows single line - Use project number from previous step
gcloud iam service-accounts add-iam-policy-binding github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com --project=YOUR_PROJECT_ID --role="roles/iam.workloadIdentityUser" --member="principalSet://iam.googleapis.com/projects/YOUR_PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool-new/attribute.repository/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME"
Create a new directory for your Terraform configuration and create the following files:
main.tf
:
# Configure the Google Cloud provider
provider "google" {
project = var.project_id
region = "asia-south1"
}
# Variables
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "github_repo" {
description = "GitHub repository in format 'owner/repository'"
type = string
}
# Workload Identity Pool
resource "google_iam_workload_identity_pool" "github_pool" {
workload_identity_pool_id = "github-pool-new"
display_name = "GitHub Actions Pool"
description = "Identity pool for GitHub Actions"
}
# Workload Identity Provider
resource "google_iam_workload_identity_pool_provider" "github_provider" {
workload_identity_pool_id = google_iam_workload_identity_pool.github_pool.workload_identity_pool_id
workload_identity_pool_provider_id = "github-provider"
display_name = "GitHub provider"
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.repository" = "assertion.repository"
}
oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
}
# Service Account
resource "google_service_account" "github_actions" {
account_id = "github-actions"
display_name = "GitHub Actions Service Account"
description = "Service account for GitHub Actions"
}
# Artifact Registry Repository
resource "google_artifact_registry_repository" "docker_repo" {
location = "asia-south1"
repository_id = var.repository_name
description = "Docker repository for container images"
format = "DOCKER"
}
# IAM binding for Artifact Registry
resource "google_artifact_registry_repository_iam_member" "github_actions_writer" {
location = google_artifact_registry_repository.docker_repo.location
repository = google_artifact_registry_repository.docker_repo.name
role = "roles/artifactregistry.writer"
member = "serviceAccount:${google_service_account.github_actions.email}"
}
# IAM binding for Workload Identity User
resource "google_service_account_iam_member" "workload_identity_user" {
service_account_id = google_service_account.github_actions.name
role = "roles/iam.workloadIdentityUser"
member = "principalSet://iam.googleapis.com/projects/${var.project_id}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.github_pool.workload_identity_pool_id}/attribute.repository/${var.github_repo}"
}
# Outputs
output "workload_identity_provider" {
description = "Workload Identity Provider resource name"
value = "projects/${var.project_id}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.github_pool.workload_identity_pool_id}/providers/${google_iam_workload_identity_pool_provider.github_provider.workload_identity_pool_provider_id}"
}
output "service_account_email" {
description = "Service Account email"
value = google_service_account.github_actions.email
}
output "artifact_registry_repository" {
description = "Artifact Registry repository path"
value = "${google_artifact_registry_repository.docker_repo.location}-docker.pkg.dev/${var.project_id}/${google_artifact_registry_repository.docker_repo.repository_id}"
}
terraform.tfvars
:
project_id = "YOUR_PROJECT_ID"
github_repo = "YOUR_GITHUB_USERNAME/YOUR_REPO_NAME"
repository_name = "YOUR_REPOSITORY"
# Initialize Terraform
terraform init
# Preview changes
terraform plan
# Apply configuration
terraform apply
Use the outputs from Terraform to update your GitHub Actions workflow file (same as in Option 1).
To destroy all resources created by Terraform:
# Preview what will be destroyed
terraform plan -destroy
# Destroy resources
terraform destroy
-
Terraform State Issues:
- Ensure you're in the correct directory
- Check if
terraform.tfstate
exists - Verify state is not corrupted
-
Provider Authentication:
- Ensure you're authenticated with GCP
- Check if required APIs are enabled
- Verify project permissions
-
Resource Dependencies:
- Let Terraform handle the deletion order
- If stuck, check for external dependencies
- Manual cleanup might be needed for locked resources
Create .github/workflows/build-push.yml
with the following content:
name: Build and Push to GCP
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
env:
PROJECT_ID: YOUR_PROJECT_ID
GAR_LOCATION: asia-south1
REPOSITORY: YOUR_REPOSITORY
IMAGE_NAME: YOUR_IMAGE_NAME
permissions:
contents: read
id-token: write
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Google Auth
id: auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/YOUR_PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool-new/providers/github-provider'
service_account: 'github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com'
token_format: 'access_token'
- name: Docker Auth
uses: 'docker/login-action@v3'
with:
registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev'
username: 'oauth2accesstoken'
password: '${{ steps.auth.outputs.access_token }}'
- name: Build and Push Container
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:latest
-
Replace placeholders:
YOUR_PROJECT_ID
: Your GCP project IDYOUR_PROJECT_NUMBER
: Your GCP project numberYOUR_REPOSITORY
: Your Artifact Registry repository nameYOUR_GITHUB_USERNAME
: Your GitHub usernameYOUR_REPO_NAME
: Your GitHub repository nameYOUR_IMAGE_NAME
: Desired name for your Docker image
-
Critical Points:
- Use project number (not project ID) in workload identity provider path
- Ensure all GCP APIs are enabled before starting
- Verify resource creation after each step
- Maintain exact case sensitivity in repository names
- Check Workload Identity Pool:
gcloud iam workload-identity-pools describe github-pool-new --project=YOUR_PROJECT_ID --location=global
- Verify Service Account:
gcloud iam service-accounts describe github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com
- Check Artifact Registry Repository:
gcloud artifacts repositories list --project=YOUR_PROJECT_ID --location=asia-south1
Follow these steps to clean up all created resources. Execute them in order to properly remove all dependencies.
# Windows single line
gcloud artifacts repositories delete YOUR_REPOSITORY --project=YOUR_PROJECT_ID --location=asia-south1 --quiet
# Expanded version
gcloud artifacts repositories delete YOUR_REPOSITORY \
--project=YOUR_PROJECT_ID \
--location=asia-south1 \
--quiet
# Windows single line
gcloud iam service-accounts remove-iam-policy-binding github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com --project=YOUR_PROJECT_ID --role="roles/iam.workloadIdentityUser" --member="principalSet://iam.googleapis.com/projects/YOUR_PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool-new/attribute.repository/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME"
# Windows single line
gcloud iam service-accounts delete github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com --project=YOUR_PROJECT_ID --quiet
# Expanded version
gcloud iam service-accounts delete github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com \
--project=YOUR_PROJECT_ID \
--quiet
# Windows single line
gcloud iam workload-identity-pools providers delete github-provider --project=YOUR_PROJECT_ID --location=global --workload-identity-pool=github-pool-new --quiet
# Expanded version
gcloud iam workload-identity-pools providers delete github-provider \
--project=YOUR_PROJECT_ID \
--location=global \
--workload-identity-pool=github-pool-new \
--quiet
# Windows single line
gcloud iam workload-identity-pools delete github-pool-new --project=YOUR_PROJECT_ID --location=global --quiet
# Expanded version
gcloud iam workload-identity-pools delete github-pool-new \
--project=YOUR_PROJECT_ID \
--location=global \
--quiet
# Check if Artifact Registry repository is deleted
gcloud artifacts repositories list --project=YOUR_PROJECT_ID --location=asia-south1
# Check if Service Account is deleted
gcloud iam service-accounts list --project=YOUR_PROJECT_ID
# Check if Workload Identity Pool is deleted
gcloud iam workload-identity-pools list --project=YOUR_PROJECT_ID --location=global
-
Pool Already Exists Error:
- Use
gcloud iam workload-identity-pools list
to check existing pools - Create pool with a different name if needed
- Use
-
Invalid Argument for Identity Pool:
- Verify project number is used (not project ID)
- Check repository name case sensitivity
- Ensure all slashes and quotes are correct
-
Docker Authentication Error:
- Verify
token_format: 'access_token'
is set in workflow - Check service account permissions
- Ensure Artifact Registry API is enabled
- Verify
-
Permission Denied:
- Verify APIs are enabled
- Check service account roles
- Ensure correct project is set
-
Resource Busy During Deletion:
- Wait a few minutes and try again
- Check for dependencies in the GCP Console
- Ensure no active workloads are using the resources
-
Dependencies Still Exist During Deletion:
- Follow the deletion order as specified
- Check for any custom IAM bindings
- Look for any custom configurations in GCP Console