#Workload Identity #Google Cloud #Terraform #GuiHub Actions
Google Cloud와 GitHub Actions (Terraform)를 함께 사용하는 Direct Workload Identity를 만드는 bash 스크립트를 소개합니다.
소개
개요
이번 컨텐츠에서 소개하는 것은 Google Cloud와 GitHub Actions (Terraform) 간의 협력에 필요한 Direct Workload Identity 리소스를 만드는 bash 스크립트입니다.
이번에 소개하는 것은 Workload Identity 풀에 필요한 권한 (IAM role) 를 직접 부여하는 형식 Workload Identity 리소스를 만드는 스크립트입니다.
이 방법은 서비스 계정을 지불하거나 서비스 계정을 차용할 수 있는 권한이 필요하지 않기 때문에 기존보다 안전한 협력이 가능하며 Google Cloud 및 GitHub 공식 문서에서도 권장됩니다.
제한사항
권장 형식이지만 Direct Workload Identity에는 사용 가능한 제품 및 기능에 제한이 있습니다.
지원되지 않는 제품 및 기능을 관리하려면 기존 형식(서비스 계정 권한을 차용하는 형식)의 Workload Identity를 사용하십시오.
전제 조건
이 bash 스크립트는 Debian GNU/Linux 12 (bookworm)위에 개발되어 동작 확인되고 있습니다.
또한 다음 소프트웨어가 설치되어 있다고 가정합니다. 괄호 안은 개발시 버전입니다.
- gcloud( Google Cloud SDK 486.0.0)
스크립트를 실행할 때는 대상 프로젝트에 대해 gcloud CLI를 인증해야 합니다.
- 참고 : 사용자 계정을 사용하여 권한 부여
- 참고 : 서비스 계정을 사용하여 승인
면책사항
이번 컨텐츠에서 소개하는 프로그램의 소스 코드는 자신의 책임하에 사용, 인용, 변경, 재배포할 수 있습니다.
다만, 동 소스 코드에 의해 발생한 불이익이나 트러블에 대해서는, 당사는 일절의 책임을 지지 않습니다.
소스 코드
상기를 면책사항을 이해한 후, 이용해 주십시오.
init.sh
#!/bin/bash# 오류 처리 : 오류가 발생하면 스크립트 종료set -e# 변수 설정PROJECT_ID = "" # 프로젝트 ID (ex: gha-demo-prj)PROJECT_NUMBER = "" # 프로젝트 번호 (ex: 1234567890)ORGANIZATION_ID = "" # 프로젝트 조직 ID (ex: 0123456789)WORKLOAD_IDENTITY_POOL = "" # Workload Identity 풀 이름(ex: gha-demo-pool)WORKLOAD_IDENTITY_PROVIDER = "" # Workload Identity 공급자 이름(ex: gha-demo-provider)GITHUB_REPO = "" # GitHub 리포지토리 이름(ex: gha-demo-org/gha-demo-repo)# 로그 출력 함수log() {echo " [INFO] $1 "}log_error() {echo " [ERROR] $1 " >&2}# 1. IAM Credential API 사용if! gcloud services list --enabled --filter =" name:iamcredentials.googleapis.com " --format =" value(name) " | grep " iamcredentials.googleapis.com " > /dev/null 2 >& 1 ; thenlog " IAM Credential API 사용 중... "gcloud services enable iamcredentials.googleapis.com --project =" $PROJECT_ID "elselog " IAM Credential API가 이미 활성화되어 있습니다. "fi# 2. Workload Identity 풀 만들기if! gcloud iam workload-identity-pools describe $WORKLOAD_IDENTITY_POOL --location =" global " --project =" $PROJECT_ID " > /dev/null 2 >& 1 ; thenlog " Workload Identity 풀 생성 중: $WORKLOAD_IDENTITY_POOL "gcloud iam workload-identity-pools create $WORKLOAD_IDENTITY_POOL \--project =" $PROJECT_ID " \--location =" global " \--display-name =" $WORKLOAD_IDENTITY_POOL "elselog " Workload Identity 풀이 이미 있습니다. $WORKLOAD_IDENTITY_POOL "fi# 3. Workload Identity 공급자 만들기if! gcloud iam workload-identity-pools providers describe $WORKLOAD_IDENTITY_PROVIDER --workload-identity-pool =" $WORKLOAD_IDENTITY_POOL " --location =" global " --project =" $PROJECT_ID " > /dev/null 2 >& 1 ; thenlog " Workload Identity 공급자 생성 중: $WORKLOAD_IDENTITY_PROVIDER "gcloud iam workload-identity-pools providers create-oidc $WORKLOAD_IDENTITY_PROVIDER \--project =" $PROJECT_ID " \--location =" global " \--workload-identity-pool =" $WORKLOAD_IDENTITY_POOL " \--display-name =" $WORKLOAD_IDENTITY_PROVIDER " \--issuer-uri =" https://token.actions.githubusercontent.com " \--attribute-mapping =" google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository " \--attribute-condition =" assertion.repository==' $GITHUB_REPO ' "elselog " Workload Identity 공급자가 이미 존재합니다. $WORKLOAD_IDENTITY_PROVIDER "fi# 4. 조직 수준에서 역할 부여log " 조직 수준에서 역할 부여 확인 "for role in " roles/resourcemanager.organizationAdmin " " roles/owner " ; doif! gcloud organizations get-iam-policy $ORGANIZATION_ID --flatten =" bindings[].members " --filter =" bindings.members:principalSet://iam.googleapis.com/projects/ $PROJECT_NUMBER /locations/global/workloadIdentityPools/ $WORKLOAD_IDENTITY_POOL AND bindings.role: $role " --format =" value(bindings.role) " | grep " $role " > /dev/null 2 >& 1 ; thenlog " Workload Identity 풀에 $role 을 부여하는 중: $WORKLOAD_IDENTITY_POOL "gcloud organizations add-iam-policy-binding $ORGANIZATION_ID \--member =" principalSet://iam.googleapis.com/projects/ $PROJECT_NUMBER /locations/global/workloadIdentityPools/ $WORKLOAD_IDENTITY_POOL /attribute.repository/ $GITHUB_REPO " \--role =" $role "elselog " $role이 이미 Workload Identity 풀에 부여되었습니다. $WORKLOAD_IDENTITY_POOL "fidonelog " Direct Workload Identity 설정이 완료되었습니다. "
스크립트 사용법
인증
먼저 실행 대상 프로젝트에 gcloud CLI 인증을 통과합니다.
# 실행 대상 프로젝트 확인$gcloud config list[ core ]account = test-user@demo.g-gen.co.jpdisable_usage_reporting = Trueproject = gha-demo-prjYour active configuration is: [ gha-demo-prj ]# gcloud CLI 인증$gcloud auth login~~중략~~You are now logged in as [ test-user@demo.g-gen.co.jp ] .Your current project is [ gha-demo-prj ] .
변수 설정
7~12행 변수에 환경 정보를 입력합니다.
※ 본 스크립트에서는 서비스 계정의 변수 정의가 없습니다.
실행
스크립트에 실행 권한을 부여하고 실행합니다.
※ 본 스크립트에서는 다음 리소스를 작성하지 않습니다.
- 서비스 계정
- 서비스 계정 대여를 위한 IAM Policy
- Workload Identity 풀과 서비스 계정 연결
# 실행 권한 부여$ chmod +x init.sh$ ls -l-rwxr-xr-x 1 test-user test-user 3784 Nov 12 14:27 init.sh
# 스크립트 실행$./init.sh[ INFO ] IAM Credential API가 이미 활성화되어 있습니다.[ INFO ] Workload Identity 풀 생성 중 : gha-demo-poolCreated workload identity pool [ gha-demo-pool ] .[ INFO ] Workload Identity 공급자 생성 중 : gha-demo-providerCreated workload identity pool provider [ gha-demo-provider ] .[ INFO ] 조직 수준에서 역할 부여 확인[ INFO ] roles / resourcemanager.organizationAdmin을 Workload Identity 풀에 부여하는 중 : gha-demo-poolUpdated IAM policy for organization [ 0123456789 ] .~~중략~~[ INFO ] roles / owner를 Workload Identity 풀에 부여 중 : gha-demo-poolUpdated IAM policy for organization [ 0123456789 ] .~~중략~~[ INFO ] Workload Identity 설정이 완료되었습니다.
구성
이 스크립트로 작성된 Workload Identity를 사용하여 Google Cloud 프로젝트에 대한 terraform planGitHub terraform applyActions를 사용하여 자동화합니다.

워크 플로우나 Terraform 소스 코드는 다음 항 에 기재된 것을 사용합니다. 면책사항
소스 코드 (Terraform)
o Terraform 디렉토리 구성
1 | . |
2 | ├── .github |
3 | │ └── workflows |
4 | │ └── terraform.yaml |
5 | ├── env |
6 | │ └── demo |
7 | │ ├── backend.tf |
8 | │ ├── locals.tf |
9 | │ ├── main.tf |
10 | │ └── versions.tf |
11 | ├── modules |
12 | │ └── apis |
13 | │ ├── main.tf |
14 | │ ├── outputs.tf |
15 | │ └── variables.tf |
16 | ├── .gitignore |
17 | ├── init.sh |
18 | └── README.md |
o 워크플로 (terraform.yaml)
다음 값을 사용자 환경에서 작성한 자원으로 대체하십시오.
- 38행 : Workload Identity 공급자
Direct Workload Identity에서는 google-github-actions/auth@v2서비스 계정을 정의할 필요가 없습니다.
name : terraform# main 브랜치에 대한 Pull request 및 Mergeon :pull_request :branches :- mainpush :branches :- main# 작업 (GitHUb runners에서 실행)jobs :terraform-workflow :runs-on : ubuntu-latestpermissions :id-token : writecontents : readpull-requests : writestrategy :matrix :# tf_working_dir에 main.tf (호출자)의 디렉토리를 지정합니다.tf_working_dir :- ./env/demosteps :- uses : actions/checkout@v4name : Checkoutid : checkout# Workload Identity 협력# https://cloud.google.com/iam/docs/using-workload-identity-federation#generate-automatic- id : 'auth'name : 'Authenticate to Google Cloud'uses : 'google-github-actions/auth@v2'with :workload_identity_provider : 'projects/1234567890/locations/global/workloadIdentityPools/gha-demo-pool/providers/gha-demo-provider'# https://github.com/marketplace/actions/setup-tfcmt- uses : shmokmt/actions-setup-tfcmt@v2name : Setup tfcmt# https://github.com/marketplace/actions/setup-github-comment- uses : shmokmt/actions-setup-github-comment@v2name : Setup github-comment# https://github.com/actions/setup-node# https://github.com/hashicorp/setup-terraform/issues/84- uses : actions/setup-node@v4with :node-version : '18'- uses : hashicorp/setup-terraform@v3name : Setup terraform- name : Terraform fmtid : fmtrun : |cd ${{ matrix.tf_working_dir }}terraform fmt -recursivecontinue-on-error : true- name : Terraform Initid : initrun : |cd ${{ matrix.tf_working_dir }}terraform init -upgrade- name : Terraform Validateid : validaterun : |cd ${{ matrix.tf_working_dir }}terraform validate# main 브랜치에 pull request 했을 때에 terraform plan 를 실행- name : Terraform Planid : planif : github.event_name == 'pull_request'run : |cd ${{ matrix.tf_working_dir }}export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}tfcmt -var target:${{ matrix.tf_working_dir }} plan - terraform plan --parallelism=50github-comment hide -condition 'Comment.Body contains "No changes."'continue-on-error : true# terraform status로 실패하면 workflow 중지- name: Terraform Plan Statusid: statusif: steps.plan.outcome == 'failure'run: exit 1# main 브랜치에 push 했을 때에 terraform apply 를 실행- name: Terraform Applyid: applyif: github.ref == 'refs/heads/main' && github.event_name == 'push'run: |cd ${{ matrix.tf_working_dir }}export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}tfcmt -var target:${{ matrix.tf_working_dir }} apply -- terraform apply -auto-approve -input=false --parallelism=50
o env/demo 부하(호출측)
#backend.tfterraform {backend " gcs " {bucket = " gha-demo-prj-tfstate "prefix = " terraform/state "}}#locals.tflocals {project_id = " gha-demo-prj "apis = [" artifactregistry.googleapis.com "," cloudapis.googleapis.com "," cloudasset.googleapis.com "," cloudresourcemanager.googleapis.com "," iam.googleapis.com "," iamcredentials.googleapis.com "," servicemanagement.googleapis.com "," serviceusage.googleapis.com "," sts.googleapis.com ",]}#main.tfmodule " apis " {source = " ../../modules/apis "project_id = local.project_idapis = local.apis}#versions.tfterraform {required_version = " ~> 1.9.7 "required_providers {google = {source = " hashicorp/google "version = " ~> 6.6.0 "}}}provider " google " {user_project_override = true}
o modules/apis 부하(모듈)
#main.tfresource " google_project_service " " apis " {for_each = toset ( var.apis )project = var.project_idservice = each.valuedisable_on_destroy = false}resource " null_resource " " delay " {provisioner " local-exec " {command = " 슬립 180 "}depends_on = [ google_project_service.apis ]}#outputs.tfoutput " enabled_apis " {description = " List of enabled APIs for the project "value = [ for service in google_project_service.apis : service.id ]}#variables.tfvariable " apis " {description = " List of APIs to enable "type = list ( string )}variable " project_id " {description = " The ID of the project to create resources in "type = string}
풀 요청 (terraform plan)
Direct Workload Identity 에서도, main 브랜치에의 풀 리퀘스트를 트리거에가 실행 terraform plan되었습니다 .
terraform apply



병합 (terraform apply)
Direct Workload Identity 에서도, main 브랜치에의 병합을 트리거에가 terraform apply실행되었습니다
※ 병합의 경우는, 스킵 terraform plan됩니다.



<병합을 트리거로 terraform apply 자동 실행>