CircleCI
ตัวอย่าง CircleCI config สำหรับ Terraform pipeline
Basic .circleci/config.yml
.circleci/config.yml
version: 2.1
orbs:
terraform: circleci/[email protected]
aws-cli: circleci/aws-[email protected]
jobs:
terraform-fmt:
docker:
- image: hashicorp/terraform:1.9.8
steps:
- checkout
- run:
name: Format Check
command: terraform fmt -check -recursive
terraform-validate:
docker:
- image: hashicorp/terraform:1.9.8
steps:
- checkout
- run:
name: Init
command: terraform init -backend=false
- run:
name: Validate
command: terraform validate
terraform-plan:
docker:
- image: hashicorp/terraform:1.9.8
steps:
- checkout
- aws-cli/setup:
role-arn: $AWS_ROLE_ARN
- run:
name: Init
command: terraform init
- run:
name: Plan
command: terraform plan -out=tfplan
- persist_to_workspace:
root: .
paths:
- tfplan
- .terraform/
terraform-apply:
docker:
- image: hashicorp/terraform:1.9.8
steps:
- checkout
- attach_workspace:
at: .
- aws-cli/setup:
role-arn: $AWS_ROLE_ARN
- run:
name: Apply
command: terraform apply -auto-approve tfplan
workflows:
terraform:
jobs:
- terraform-fmt
- terraform-validate
- terraform-plan:
requires:
- terraform-fmt
- terraform-validate
- hold-for-approval:
type: approval
requires:
- terraform-plan
filters:
branches:
only: main
- terraform-apply:
requires:
- hold-for-approval
filters:
branches:
only: main
ใช้ CircleCI Orb
CircleCI Terraform Orb มี jobs สำเร็จรูป:
version: 2.1
orbs:
terraform: circleci/[email protected]
workflows:
terraform:
jobs:
- terraform/fmt:
checkout: true
- terraform/validate:
checkout: true
path: .
- terraform/plan:
checkout: true
path: .
persist-workspace: true
- terraform/apply:
checkout: false
attach-workspace: true
path: .
filters:
branches:
only: main
OIDC Authentication
ใช้ CircleCI OIDC token แทน AWS access key:
1. ตั้งค่า IAM
data "circleci_oidc_token" "this" {}
resource "aws_iam_openid_connect_provider" "circleci" {
url = "https://oidc.circleci.com/org/${var.circleci_org_id}"
client_id_list = [var.circleci_org_id]
thumbprint_list = ["..."]
}
resource "aws_iam_role" "circleci" {
name = "CircleCITerraform"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.circleci.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"oidc.circleci.com/org/${var.circleci_org_id}:aud" = var.circleci_org_id
}
}
}]
})
}
2. ใช้ใน Job
jobs:
terraform-apply:
docker:
- image: hashicorp/terraform:1.9.8
steps:
- checkout
- run:
name: Assume AWS Role
command: |
aws sts assume-role-with-web-identity \
--role-arn $AWS_ROLE_ARN \
--role-session-name circleci-${CIRCLE_BUILD_NUM} \
--web-identity-token $CIRCLE_OIDC_TOKEN
Multi-Environment
workflows:
deploy-dev:
jobs:
- terraform-apply:
name: deploy-dev
working-directory: envs/dev
context: aws-dev
filters:
branches:
only: develop
deploy-prod:
jobs:
- terraform-apply:
name: deploy-prod
working-directory: envs/prod
context: aws-prod
filters:
branches:
only: main
Caching
jobs:
terraform-init:
docker:
- image: hashicorp/terraform:1.9.8
steps:
- checkout
- restore_cache:
keys:
- tf-plugins-{{ checksum ".terraform.lock.hcl" }}
- run:
name: Setup plugin cache
command: |
mkdir -p ~/.terraform.d/plugin-cache
echo 'plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"' > ~/.terraformrc
- run: terraform init
- save_cache:
key: tf-plugins-{{ checksum ".terraform.lock.hcl" }}
paths:
- ~/.terraform.d/plugin-cache
Approval Gate
workflows:
deploy-prod:
jobs:
- terraform-plan
- approve-prod-deploy:
type: approval
requires:
- terraform-plan
filters:
branches:
only: main
- terraform-apply:
requires:
- approve-prod-deploy
→ Workflow pause ที่ approval — manual click ใน UI ก่อน apply
Notification
jobs:
terraform-apply:
docker:
- image: hashicorp/terraform:1.9.8
steps:
- run: terraform apply -auto-approve
- run:
name: Notify Slack
when: always
command: |
if [ "$CIRCLE_JOB_STATUS" == "success" ]; then
MESSAGE="✅ Terraform applied successfully"
else
MESSAGE="❌ Terraform apply failed"
fi
curl -X POST $SLACK_WEBHOOK \
-H 'Content-Type: application/json' \
-d "{\"text\":\"$MESSAGE\"}"
Run Linting + Security Scan
jobs:
lint:
docker:
- image: ghcr.io/terraform-linters/tflint:v0.53.0
steps:
- checkout
- run: tflint --init
- run: tflint --recursive
security:
docker:
- image: bridgecrew/checkov:latest
steps:
- checkout
- run: checkov -d . --framework terraform
Best Practices
✅ DO:
- ใช้ OIDC แทน access keys
- Cache provider plugins
- Approval gate สำหรับ prod
- Notify on success/failure
- Run lint + security scan parallel กับ plan
❌ DON'T:
- ห้าม hardcode secrets ใน config.yml
- ห้าม skip approval gate ใน prod
- ห้ามรัน apply โดยไม่มี plan ก่อน
ตัวอย่าง Complete Pipeline
version: 2.1
orbs:
terraform: circleci/[email protected]
aws-cli: circleci/aws-[email protected]
executors:
terraform:
docker:
- image: hashicorp/terraform:1.9.8
jobs:
fmt-check:
executor: terraform
steps:
- checkout
- run: terraform fmt -check -recursive
validate:
executor: terraform
steps:
- checkout
- run: terraform init -backend=false
- run: terraform validate
lint:
docker:
- image: ghcr.io/terraform-linters/tflint:v0.53.0
steps:
- checkout
- run: tflint --init
- run: tflint --recursive
security-scan:
docker:
- image: bridgecrew/checkov:latest
steps:
- checkout
- run: checkov -d . --framework terraform
plan:
executor: terraform
steps:
- checkout
- aws-cli/setup
- run: terraform init
- run: terraform plan -out=tfplan
- persist_to_workspace:
root: .
paths: [tfplan, .terraform]
apply:
executor: terraform
steps:
- checkout
- attach_workspace:
at: .
- aws-cli/setup
- run: terraform apply -auto-approve tfplan
workflows:
pr:
jobs:
- fmt-check
- validate
- lint
- security-scan
- plan:
requires: [fmt-check, validate, lint, security-scan]
deploy:
when:
equal: [main, << pipeline.git.branch >>]
jobs:
- fmt-check
- validate
- lint
- security-scan
- plan:
requires: [fmt-check, validate, lint, security-scan]
- approve:
type: approval
requires: [plan]
- apply:
requires: [approve]
สรุป
- CircleCI ใช้
.circleci/config.yml - Workflow: fmt → validate → lint → security → plan → approve → apply
- Use CircleCI Terraform Orb ลดงานเขียน config
- OIDC กับ AWS — temp credentials, no secrets
- Approval gate สำหรับ prod
- Cache plugins + persist_to_workspace ระหว่าง jobs
ต่อไป → GitLab CI