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
+}