diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml index 13a0f48..947fd75 100644 --- a/.github/workflows/terraform-ci.yml +++ b/.github/workflows/terraform-ci.yml @@ -1,14 +1,85 @@ name: terraform-ci + on: pull_request: + paths: + - "envs/**" + - ".github/workflows/terraform-ci.yml" push: - paths: ["envs/**", ".github/workflows/terraform-ci.yml"] + paths: + - "envs/**" + - ".github/workflows/terraform-ci.yml" + +permissions: + contents: read + id-token: write + pull-requests: write + +concurrency: + group: terraform-${{ github.ref }} + cancel-in-progress: true + jobs: - fmt-validate: + fmt-validate-plan: + name: fmt/validate/plan (envs/dev) runs-on: ubuntu-latest + environment: ci + steps: - uses: actions/checkout@v4 - - uses: hashicorp/setup-terraform@v3 - - run: terraform -chdir=envs/dev fmt -check - - run: terraform -chdir=envs/dev init -backend=false - - run: terraform -chdir=envs/dev validate + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.6.6 + + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ vars.AWS_ROLE_ARN }} + aws-region: ${{ vars.AWS_REGION }} + + - name: Terraform fmt (check) + run: terraform -chdir=envs/dev fmt -check -recursive + + - name: Terraform init + run: terraform -chdir=envs/dev init -no-color + + - name: Terraform validate + run: terraform -chdir=envs/dev validate -no-color + + - name: Terraform plan + run: terraform -chdir=envs/dev plan -no-color -out=plan.tfplan + + - name: Render plan to text + run: terraform -chdir=envs/dev show -no-color plan.tfplan > envs/dev/plan.txt + + - name: Upload plan artifact + uses: actions/upload-artifact@v4 + with: + name: terraform-plan-${{ github.event.pull_request.number || github.run_number }} + path: | + envs/dev/plan.tfplan + envs/dev/plan.txt + retention-days: 7 + + - name: Comment plan (PR only) + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const plan = fs.readFileSync('envs/dev/plan.txt', 'utf8'); + const body = [ + '### Terraform Plan (envs/dev)', + '', + '```', + plan.length > 60000 ? plan.slice(0, 60000) + '\n... (truncado)' : plan, + '```' + ].join('\n'); + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body + }); diff --git a/iam/github-oidc/README.md b/iam/github-oidc/README.md new file mode 100644 index 0000000..30d0d89 --- /dev/null +++ b/iam/github-oidc/README.md @@ -0,0 +1,42 @@ +# github-oidc + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.6.6, < 2.0.0 | +| [aws](#requirement\_aws) | ~> 5.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | ~> 5.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_openid_connect_provider.github](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource | +| [aws_iam_role.github_actions_terraform](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [aws\_region](#input\_aws\_region) | n/a | `string` | `"us-east-1"` | no | +| [github\_repo](#input\_github\_repo) | Formato OWNER/REPO (ej: juandiegocv27/infra-terraform) | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [role\_arn](#output\_role\_arn) | n/a | + diff --git a/iam/github-oidc/main.tf b/iam/github-oidc/main.tf new file mode 100644 index 0000000..d45587f --- /dev/null +++ b/iam/github-oidc/main.tf @@ -0,0 +1,75 @@ +terraform { + required_version = ">= 1.6.6, < 2.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.aws_region +} + +variable "aws_region" { + type = string + default = "us-east-1" +} + +variable "github_repo" { + type = string + description = "Formato OWNER/REPO (ej: juandiegocv27/infra-terraform)" +} + +resource "aws_iam_openid_connect_provider" "github" { + url = "https://token.actions.githubusercontent.com" + client_id_list = ["sts.amazonaws.com"] + thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"] +} + +data "aws_iam_policy_document" "assume_role" { + statement { + effect = "Allow" + actions = ["sts:AssumeRoleWithWebIdentity"] + + principals { + type = "Federated" + identifiers = [aws_iam_openid_connect_provider.github.arn] + } + + # Audience debe ser STS + condition { + test = "StringEquals" + variable = "token.actions.githubusercontent.com:aud" + values = ["sts.amazonaws.com"] + } + + # Permitir PRs, pushes a branches, y uso de Environment "ci" + condition { + test = "StringLike" + variable = "token.actions.githubusercontent.com:sub" + values = [ + "repo:${var.github_repo}:pull_request", + "repo:${var.github_repo}:ref:refs/heads/*", + "repo:${var.github_repo}:environment:ci", + ] + } + } +} + +resource "aws_iam_role" "github_actions_terraform" { + name = "github-actions-terraform-plan" + assume_role_policy = data.aws_iam_policy_document.assume_role.json +} + +# Para arrancar rápido (luego bajás a least-privilege) +resource "aws_iam_role_policy_attachment" "admin" { + role = aws_iam_role.github_actions_terraform.name + policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" +} + +output "role_arn" { + value = aws_iam_role.github_actions_terraform.arn +}