diff --git a/.github/workflows/terraform-standard-iac-pipeline-aws-global-bootstrap.yaml b/.github/workflows/terraform-standard-iac-pipeline-aws-global-bootstrap.yaml index 6009611a..ad7470f2 100644 --- a/.github/workflows/terraform-standard-iac-pipeline-aws-global-bootstrap.yaml +++ b/.github/workflows/terraform-standard-iac-pipeline-aws-global-bootstrap.yaml @@ -12,16 +12,12 @@ on: inputs: deploy_action: type: choice - options: [init, plan, apply, destroy] + options: [plan, apply, destroy] default: plan - deploy_dry_run: - type: choice - options: ['true', 'false'] - default: 'true' env: TF_WORKDIR: iac-template/terraform-hcl-standard/aws-cloud - DRY_RUN: ${{ github.event.inputs.deploy_dry_run || 'true' }} + DEPLOY_ACTION: ${{ github.event.inputs.deploy_action || 'plan' }} jobs: bootstrap: @@ -51,33 +47,37 @@ jobs: run: make init - name: Plan - if: env.DRY_RUN == 'true' + if: env.DEPLOY_ACTION == 'plan' working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }} run: make plan - name: Apply - if: env.DRY_RUN == 'false' + if: env.DEPLOY_ACTION == 'apply' working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }} run: make apply + - name: Destroy + if: env.DEPLOY_ACTION == 'destroy' + working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }} + run: make destroy + - name: Save Outputs - if: env.DRY_RUN == 'false' + if: env.DEPLOY_ACTION == 'apply' working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }} run: terraform output -json > ../../outputs_${{ matrix.target }}.json - uses: actions/upload-artifact@v4 - if: env.DRY_RUN == 'false' + if: env.DEPLOY_ACTION == 'apply' with: name: outputs-${{ matrix.target }} - path: iac-template/terraform-standard/outputs_${{ matrix.target }}.json + path: iac-template/terraform-hcl-standard/aws-cloud/outputs_${{ matrix.target }}.json aggregate: name: "Aggregate Bootstrap Outputs" runs-on: ubuntu-latest needs: bootstrap - # ❗ Job-level 不能用 env.DRY_RUN,要用 github.event.inputs.* - if: ${{ github.event.inputs.deploy_dry_run == 'false' }} + if: ${{ github.event.inputs.deploy_action == 'apply' }} steps: - uses: actions/download-artifact@v4 diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/Makefile b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/Makefile index ad1144db..a9cfaa1b 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/Makefile +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/Makefile @@ -1,16 +1,20 @@ +table_name ?= +region ?= + +TF_VARS := $(if $(table_name),-var="table_name=$(table_name)") $(if $(region),-var="region=$(region)") + init: terraform init --upgrade terraform init -migrate-state apply: init - terraform apply \ - -var="table_name=svc-plus-iac-state-dynamodb-lock" \ - -var="region=ap-northeast-1" \ - -auto-approve + terraform apply $(TF_VARS) -auto-approve + plan: init - terraform plan -var="table_name=svc-plus-iac-state-dynamodb-lock" -var="region=ap-northeast-1" + terraform plan $(TF_VARS) + output: init terraform output -destroy: init - terraform destroy -var="table_name=svc-plus-iac-state-dynamodb-lock" -var="region=ap-northeast-1" +destroy: init + terraform destroy $(TF_VARS) diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/locals.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/locals.tf new file mode 100644 index 00000000..8fb00ad0 --- /dev/null +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/locals.tf @@ -0,0 +1,8 @@ +locals { + bootstrap = yamldecode(file("${path.root}/../config/accounts/bootstrap.yaml")) + + dynamodb_table_name = coalesce(var.table_name, local.bootstrap.state.dynamodb_table_name) + region = coalesce(var.region, local.bootstrap.region) + environment = try(local.bootstrap.environment, "bootstrap") + tags = try(local.bootstrap.tags, {}) +} diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/main.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/main.tf index 65f040ea..64f52268 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/main.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/main.tf @@ -1,5 +1,5 @@ resource "aws_dynamodb_table" "terraform_locks" { - name = var.table_name + name = local.dynamodb_table_name billing_mode = "PAY_PER_REQUEST" hash_key = "LockID" @@ -9,8 +9,11 @@ resource "aws_dynamodb_table" "terraform_locks" { type = "S" } - tags = { - Name = var.table_name - Environment = "bootstrap" - } + tags = merge( + { + Name = local.dynamodb_table_name + Environment = local.environment + }, + local.tags + ) } diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/provider.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/provider.tf index e546cc02..79476f2d 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/provider.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/provider.tf @@ -10,5 +10,5 @@ terraform { } provider "aws" { - region = var.region + region = local.region } diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/variables.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/variables.tf index 5c755078..a2efe3b3 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/variables.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-dynamodb/variables.tf @@ -1,9 +1,11 @@ variable "table_name" { description = "DynamoDB table name for Terraform state lock" type = string + default = null } variable "region" { description = "AWS region" type = string + default = null } diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/Makefile b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/Makefile index 21b082d9..e93f6131 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/Makefile +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/Makefile @@ -1,29 +1,19 @@ -account_name ?= dev -region ?= ap-northeast-1 -role_name ?= TerraformDeployRole-Dev -terraform_user_name ?= sit-ci-runner +account_name ?= +region ?= +role_name ?= + terraform_user_name ?= + +TF_VARS := $(if $(account_name),-var="account_name=$(account_name)") $(if $(region),-var="region=$(region)") $(if $(role_name),-var="role_name=$(role_name)") $(if $(terraform_user_name),-var="terraform_user_name=$(terraform_user_name)") init: terraform init --upgrade terraform init -migrate-state apply: init - terraform apply -auto-approve \ - -var="account_name=$(account_name)" \ - -var="region=$(region)" \ - -var="role_name=$(role_name)" \ - -var="terraform_user_name=$(terraform_user_name)" + terraform apply -auto-approve $(TF_VARS) terraform output plan: init - terraform plan \ - -var="account_name=$(account_name)" \ - -var="region=$(region)" \ - -var="role_name=$(role_name)" \ - -var="terraform_user_name=$(terraform_user_name)" + terraform plan $(TF_VARS) output: init terraform output destroy: init - terraform destroy \ - -var="account_name=$(account_name)" \ - -var="region=$(region)" \ - -var="role_name=$(role_name)" \ - -var="terraform_user_name=$(terraform_user_name)" + terraform destroy $(TF_VARS) diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/locals.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/locals.tf new file mode 100644 index 00000000..71700a86 --- /dev/null +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/locals.tf @@ -0,0 +1,16 @@ +locals { + bootstrap = yamldecode(file("${path.root}/../config/accounts/bootstrap.yaml")) + + config_account_name = coalesce(var.account_name, local.bootstrap.account_name) + config_region = coalesce(var.region, local.bootstrap.region) + config_role_name = coalesce(var.role_name, local.bootstrap.iam.role_name) + config_terraform_user = coalesce(var.terraform_user_name, local.bootstrap.iam.terraform_user_name) + environment = coalesce(try(local.bootstrap.environment, null), try(local.bootstrap.iam.environment, null), "bootstrap") + extra_tags = try(local.bootstrap.tags, {}) +} + +locals { + account = yamldecode( + file("${path.root}/../config/accounts/${local.config_account_name}.yaml") + ) +} diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/main.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/main.tf index 785d7a1f..d8870a18 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/main.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/main.tf @@ -1,21 +1,15 @@ -locals { - account = yamldecode( - file("${path.root}/../config/accounts/${var.account_name}.yaml") - ) -} - # # IAM Role: Terraform Deploy Role # ---------------------------------------- resource "aws_iam_role" "terraform_deploy_role" { - name = var.role_name + name = local.config_role_name assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { - AWS = "arn:aws:iam::${local.account.account_id}:user/${var.terraform_user_name}" + AWS = "arn:aws:iam::${local.account.account_id}:user/${local.config_terraform_user}" } Action = "sts:AssumeRole" }] @@ -23,10 +17,11 @@ resource "aws_iam_role" "terraform_deploy_role" { tags = merge( { - Name = var.role_name - Environment = local.account.environment + Name = local.config_role_name + Environment = coalesce(try(local.account.environment, null), local.environment) }, - local.account.tags + try(local.account.tags, {}), + local.extra_tags ) } @@ -41,14 +36,14 @@ resource "aws_iam_role_policy_attachment" "attach_admin" { # IAM User for Terraform (AK/SK) # ---------------------------------------- resource "aws_iam_user" "terraform_user" { - name = var.terraform_user_name + name = local.config_terraform_user } # # IAM User Policy: 最小权限 # ---------------------------------------- resource "aws_iam_user_policy" "terraform_user_policy" { - name = "${var.terraform_user_name}-iac-policy" + name = "${local.config_terraform_user}-iac-policy" user = aws_iam_user.terraform_user.name policy = jsonencode({ @@ -69,7 +64,7 @@ resource "aws_iam_user_policy" "terraform_user_policy" { Action = [ "s3:ListBucket" ], - Resource = "arn:aws:s3:::svc-plus-iac-state" + Resource = "arn:aws:s3:::${local.bootstrap.state.bucket_name}" }, { Effect = "Allow", @@ -78,7 +73,7 @@ resource "aws_iam_user_policy" "terraform_user_policy" { "s3:PutObject", "s3:DeleteObject" ], - Resource = "arn:aws:s3:::svc-plus-iac-state/*" + Resource = "arn:aws:s3:::${local.bootstrap.state.bucket_name}/*" }, # DynamoDB: state lock table @@ -91,7 +86,7 @@ resource "aws_iam_user_policy" "terraform_user_policy" { "dynamodb:UpdateItem", "dynamodb:DescribeTable" ], - Resource = "arn:aws:dynamodb:${var.region}:${local.account.account_id}:table/svc-plus-iac-state-dynamodb-lock" + Resource = "arn:aws:dynamodb:${local.config_region}:${local.account.account_id}:table/${local.bootstrap.state.dynamodb_table_name}" } ] }) diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/provider.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/provider.tf index e546cc02..1e8577b9 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/provider.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/provider.tf @@ -10,5 +10,5 @@ terraform { } provider "aws" { - region = var.region + region = local.config_region } diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/variables.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/variables.tf index 4865cf44..92a16ee5 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/variables.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-iam/variables.tf @@ -1,19 +1,23 @@ variable "region" { description = "AWS region" type = string + default = null } variable "account_name" { type = string description = "Which account configuration to load (e.g., dev)" + default = null } variable "role_name" { type = string description = "IAM role name to create (e.g., TerraformDeployRole-Dev)" + default = null } variable "terraform_user_name" { type = string description = "IAM username for Terraform IAC runner" + default = null } diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/Makefile b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/Makefile index 8366cda3..e678d15d 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/Makefile +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/Makefile @@ -1,15 +1,21 @@ +bucket_name ?= +region ?= + +TF_VARS := $(if $(bucket_name),-var="bucket_name=$(bucket_name)") $(if $(region),-var="region=$(region)") + init: terraform init --upgrade terraform init -migrate-state apply: init - terraform apply -var="bucket_name=svc-plus-iac-state" -var=region=ap-northeast-1 -auto-approve + terraform apply $(TF_VARS) -auto-approve terraform output plan: init - terraform plan -var="bucket_name=svc-plus-iac-state" -var=region=ap-northeast-1 + terraform plan $(TF_VARS) output: init terraform output + destroy: init - terraform destroy -var="bucket_name=svc-plus-iac-state" -var=region=ap-northeast-1 + terraform destroy $(TF_VARS) diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/locals.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/locals.tf new file mode 100644 index 00000000..d0243421 --- /dev/null +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/locals.tf @@ -0,0 +1,8 @@ +locals { + bootstrap = yamldecode(file("${path.root}/../config/accounts/bootstrap.yaml")) + + bucket_name = coalesce(var.bucket_name, local.bootstrap.state.bucket_name) + region = coalesce(var.region, local.bootstrap.region) + environment = try(local.bootstrap.environment, "bootstrap") + tags = try(local.bootstrap.tags, {}) +} diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/main.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/main.tf index 0e8dbdc4..cfca8b11 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/main.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/main.tf @@ -1,5 +1,5 @@ resource "aws_s3_bucket" "state" { - bucket = var.bucket_name + bucket = local.bucket_name } resource "aws_s3_bucket_versioning" "versioning" { @@ -19,3 +19,15 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "sse" { } } } + +resource "aws_s3_bucket_tagging" "default" { + bucket = aws_s3_bucket.state.id + + tag_set = [for k, v in merge({ + Name = local.bucket_name + Environment = local.environment + }, local.tags) : { + key = k + value = v + }] +} diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/provider.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/provider.tf index e546cc02..79476f2d 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/provider.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/provider.tf @@ -10,5 +10,5 @@ terraform { } provider "aws" { - region = var.region + region = local.region } diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/variables.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/variables.tf index 1d3e689b..e08efbea 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/variables.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap-s3/variables.tf @@ -1,9 +1,11 @@ variable "bucket_name" { description = "S3 bucket name for Terraform state" type = string + default = null } variable "region" { description = "AWS region" type = string + default = null } diff --git a/iac-template/terraform-hcl-standard/aws-cloud/config/accounts/bootstrap.yaml b/iac-template/terraform-hcl-standard/aws-cloud/config/accounts/bootstrap.yaml new file mode 100644 index 00000000..0493b968 --- /dev/null +++ b/iac-template/terraform-hcl-standard/aws-cloud/config/accounts/bootstrap.yaml @@ -0,0 +1,16 @@ +region: ap-northeast-1 +environment: bootstrap + +account_name: dev + +state: + bucket_name: svc-plus-iac-state + dynamodb_table_name: svc-plus-iac-state-dynamodb-lock + +iam: + role_name: TerraformDeployRole-Dev + terraform_user_name: sit-ci-runner + +tags: + Owner: Platform + Project: modern-container-app