Skip to content

Instantly share code, notes, and snippets.

@amasucci
Created March 14, 2023 16:50
Show Gist options
  • Save amasucci/d5235e7cdaa964637c39b20e1351c0e8 to your computer and use it in GitHub Desktop.
Save amasucci/d5235e7cdaa964637c39b20e1351c0e8 to your computer and use it in GitHub Desktop.
Terraform Automation with GitHub Workflows and Workload Identity Federation
export PROJECT_ID="INSERT-PROJECT-ID"
export PROJECT_NUMBER="INSERT-PROJECT-NUMBER"
export STATE_BUCKET="INSERT-STATE-BUCKET-NAME"
gcloud storage buckets create gs://$STATE_BUCKET --project=$PROJECT_ID --default-storage-class=STANDARD --location=EUROPE-WEST1 --uniform-bucket-level-access
gcloud iam workload-identity-pools create github \
--project=$PROJECT_ID \
--location="global" \
--description="GitHub pool" \
--display-name="GitHub pool"
gcloud iam workload-identity-pools providers create-oidc "github" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="github" \
--display-name="GitHub provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.workflow_ref=assertion.job_workflow_ref,attribute.event_name=assertion.event_name" \
--issuer-uri="https://token.actions.githubusercontent.com"
gcloud iam service-accounts create tf-plan \
--project=$PROJECT_ID \
--description="SA use to run Terraform Plan" \
--display-name="Terraform Planner"
gcloud iam service-accounts create tf-apply \
--project=$PROJECT_ID \
--description="SA use to run Terraform Apply" \
--display-name="Terraform Applier"
gcloud storage buckets add-iam-policy-binding gs://${STATE_BUCKET} \
--member=serviceAccount:tf-plan@${PROJECT_ID}.iam.gserviceaccount.com \
--role=roles/storage.objectAdmin
gcloud storage buckets add-iam-policy-binding gs://${STATE_BUCKET} \
--member=serviceAccount:tf-apply@${PROJECT_ID}.iam.gserviceaccount.com \
--role=roles/storage.objectAdmin
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member=serviceAccount:tf-apply@${PROJECT_ID}.iam.gserviceaccount.com \
--role=roles/iam.serviceAccountAdmin
gcloud iam service-accounts add-iam-policy-binding "tf-plan@${PROJECT_ID}.iam.gserviceaccount.com" \
--project="${PROJECT_ID}" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/github/attribute.event_name/pull_request"
gcloud iam service-accounts add-iam-policy-binding "tf-apply@${PROJECT_ID}.iam.gserviceaccount.com" \
--project="${PROJECT_ID}" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/github/attribute.workflow_ref/outofdevops/workload-identity-federation/.github/workflows/terraform.yaml@refs/heads/main"
resource "google_service_account" "service_account" {
account_id = "my-sa-1"
display_name = "Service Account One"
}
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "4.57.0"
}
}
backend "gcs" {}
}
provider "google" {
project = "YOUR-PROJECT-ID"
}
name: Terraform Workflow Demo
on:
push:
branches: [ "main" ]
paths:
- 'terraform/**'
- '.github/workflows/terraform.yaml'
pull_request:
branches: [ "main" ]
paths:
- 'terraform/**'
- '.github/workflows/terraform.yaml'
jobs:
terraform:
permissions:
contents: 'read'
id-token: 'write'
pull-requests: 'write' #Needed to comment on the PR
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- id: 'plannerAuth'
if: github.ref != 'refs/heads/main'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v1.0.0'
with:
workload_identity_provider: 'projects/32549352397/locations/global/workloadIdentityPools/github/providers/github'
service_account: 'tf-plan@blue-project-378421.iam.gserviceaccount.com'
- id: 'applierAuth'
if: github.ref == 'refs/heads/main'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v1.0.0'
with:
workload_identity_provider: 'projects/32549352397/locations/global/workloadIdentityPools/github/providers/github'
service_account: 'tf-apply@blue-project-378421.iam.gserviceaccount.com'
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.4.0
- id: fmt
name: Terraform fmt
working-directory: terraform
run: terraform fmt -check
- id: init
name: Terraform Init
working-directory: terraform
run: terraform init -input=false -backend-config="prefix=terraform-demo" -backend-config="bucket=tf-state-storage-2023"
- id: validate
name: Terraform Validate
run: terraform validate -no-color
- id: plan
name: Terraform Plan
working-directory: terraform
run: terraform plan -no-color
continue-on-error: true
- uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: Terraform Plan Status
if: steps.plan.outcome == 'failure'
run: exit 1
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
working-directory: terraform
run: terraform apply -auto-approve -input=false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment