From 06aa6d5e20d43a4af955e65b7d513c92a595e942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Aufort?= Date: Tue, 28 Oct 2025 13:56:47 +0100 Subject: [PATCH 1/6] feat: add Q rules file --- .amazonq/rules/terraform_aws.md | 85 +++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 .amazonq/rules/terraform_aws.md diff --git a/.amazonq/rules/terraform_aws.md b/.amazonq/rules/terraform_aws.md new file mode 100644 index 0000000..0765cb4 --- /dev/null +++ b/.amazonq/rules/terraform_aws.md @@ -0,0 +1,85 @@ +# Terraform AWS Best Practices + +When generating or modifying Terraform code for AWS, follow these best practices: + +## Structure and Organization + +- Use a modular structure with reusable modules +- Prefer to use existing OSS Terraform modules on the internet + - As a priority, use official Terraform registry: https://registry.terraform.io/namespaces/terraform-aws-modules + - Else, use CloudPosse registry: https://registry.terraform.io/namespaces/cloudposse +- Separate environments (dev, staging, prod) with separate workspaces or directories +- Use variables for all configurable parameters +- Prefer relative paths for local modules +- In Terraform root modules, put the Terraform providers' configuration in a file called providers.tf +- Terraform version constraints' and providers' constraints must stay in file called versions.tf +- Try to regroup resources by the Cloud Provider's services to avoid having all the code in one main.tf file + +## Security + +- Never use hard-coded credentials in code +- Use IAM roles with the principle of least privilege +- Enable default encryption for all services that support it (S3, RDS, etc.) +- Use restrictive security groups for network resources +- Prefer private VPCs with VPC endpoints over public access + +## State Management + +- Use a remote backend to store Terraform state +- Enable versioning on the S3 state bucket +- Use state locking with S3 file lock (not DynamoDB) +- Do not include sensitive data in outputs + +## Naming and Tagging + +- Use a consistent naming scheme for all resources +- Do not add resource type as a suffix or prefix to resource names (for instance, use "my-app" instead of "my-app-vpc") +- Systematically apply tags for: + - Environment (dev, staging, prod) + - Owner + - Project + - Cost Center + - Managed By: “terraform” + - Root Module URL: + +## Performance and Costs + +- Use on-demand instances for development and reserved instances for production +- Configure lifecycle policies for S3 buckets +- Use Auto Scaling Groups to scale resources on demand +- Configure CloudWatch alarms to monitor costs + +## Code Best Practices + +- Always use fixed versions for providers and modules to avoid regressions between two `terraform plan` commands (do not use Terraform version constraint ~>) +- Document code as much as possible with README.md, variable descriptions, output descriptions, and comments (do not over-comment either when datasource/resource are self explaining) +- Use validations for input variables +- Prefer conditional resources over count for optional resources +- Use for_each over count for multiple resources + +## Networking + +- Use private subnets for resources that do not require direct Internet access +- Configure NAT Gateways only in environments that require them +- Use Transit Gateways for multi-account/multi-VPC architectures + +## Deployment + +- Always use terraform plan before applying changes +- Integrate Terraform into CI/CD pipelines for production environments +- Use blue/green approaches for critical updates + +## Non-regression + +- To avoid regressions, it is best to fix dependency versions. +- For Terraform OSS modules, use a fixed version (preferably the latest available on the Terraform registry) in the module version field + +## Testing + +- Each validator for Terraform input variables must be tested, but only failed cases. +- For each module generated, an example must be provided. +- For each example, there must be a test that runs it. + +## Use of MCP + +- Check each generated code to ensure that everything is correct (syntax, Terraform arguments) using the MCP server `terraform-mcp-server`. Before generating any code, ensure that interaction with this MCP server is working properly. From d46da345c061dd272406b2952d386d63f9226297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Aufort?= Date: Tue, 28 Oct 2025 14:48:03 +0100 Subject: [PATCH 2/6] feat: add Terraform MCP configuration --- .amazonq/mcp.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .amazonq/mcp.json diff --git a/.amazonq/mcp.json b/.amazonq/mcp.json new file mode 100644 index 0000000..ccbae5c --- /dev/null +++ b/.amazonq/mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "terraform": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "hashicorp/terraform-mcp-server:0.3" + ] + } + } +} From 2f2a886aad60fb98422a9a78e8b4e13b8fd6e390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Aufort?= Date: Tue, 28 Oct 2025 16:51:52 +0100 Subject: [PATCH 3/6] feat: add excalidraw VPC schema --- vpc_architecture.excalidraw.png | Bin 0 -> 79269 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 vpc_architecture.excalidraw.png diff --git a/vpc_architecture.excalidraw.png b/vpc_architecture.excalidraw.png new file mode 100644 index 0000000000000000000000000000000000000000..fc30e205b146d2d71537a44082b701129148235b GIT binary patch literal 79269 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%0D9Fwp0uEBIJqV0^1y>bm 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
    wS z25ELInX?c4Ya)s=vMTlER%TCz?h!`*HSgA@Wu~c3QBLp!?9z;UEz9uEKfFe0;Oz2vcC^0W0G0v?KXKOKNRrVR|kvY_SO}soGaIrFYmT&3V{&go`6M zu;Xb;57Qyc1%DkZ$O%i=XS=n&wGosN&WC0vBA6xIHAd3!H^z8qSTSY$=$=-TiAIp5n&LG!V+2AJ0|hhZQ9wC;+0v<$XJlWwG3M2>ixhSn;Z6=*hl}y({KKa4z| zC+o4m6M^IqtciV=4^wb1mo9R9{1Tpeoz9WU@o1k6^yH^UJgU_kgIV6?Kew<|8R$h| X6I7mK{F&-F;GfJLMTs171Hb Date: Wed, 5 Nov 2025 13:30:19 +0100 Subject: [PATCH 4/6] feat: update Q rules, MCPs and live coding 6 --- .amazonq/mcp.json | 24 ++++++++++++++++++++++++ .amazonq/rules/terraform_aws.md | 2 ++ README.md | 12 ++++++++++-- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/.amazonq/mcp.json b/.amazonq/mcp.json index ccbae5c..9504274 100644 --- a/.amazonq/mcp.json +++ b/.amazonq/mcp.json @@ -8,6 +8,30 @@ "--rm", "hashicorp/terraform-mcp-server:0.3" ] + }, + "github": { + "type": "stdio", + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server:0.20.1" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_AWS_Q_PERSONAL_ACCESS_TOKEN}" + } + }, + "awslabs.iam-mcp-server": { + "command": "uvx", + "args": ["awslabs.iam-mcp-server@latest"], + "env": { + "AWS_PROFILE": "ippon-data-lab", + "AWS_REGION": "eu-west-3", + "FASTMCP_LOG_LEVEL": "ERROR" + } } } } diff --git a/.amazonq/rules/terraform_aws.md b/.amazonq/rules/terraform_aws.md index 0765cb4..29475cd 100644 --- a/.amazonq/rules/terraform_aws.md +++ b/.amazonq/rules/terraform_aws.md @@ -26,6 +26,7 @@ When generating or modifying Terraform code for AWS, follow these best practices ## State Management - Use a remote backend to store Terraform state +- Store the configuration of the Terraform backend in backend.tf Terraform file - Enable versioning on the S3 state bucket - Use state locking with S3 file lock (not DynamoDB) - Do not include sensitive data in outputs @@ -56,6 +57,7 @@ When generating or modifying Terraform code for AWS, follow these best practices - Use validations for input variables - Prefer conditional resources over count for optional resources - Use for_each over count for multiple resources +- Always add .terraform.lock.hcl files to Terraform root modules to be consistent between multiple deployments ## Networking diff --git a/README.md b/README.md index 72bf392..de9d683 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,16 @@ Create the Terraform code from the schema. git checkout -b live-coding-6 ``` -Prompt enrichi via des Q rules "prod-ready" (Terraform / GitHub Actions / ...) et de multiples serveurs MCP pour déployer jusqu'en production : +Les prompts sont enrichis via des Q rules "prod-ready" et de multiples serveurs MCP pour déployer jusqu'en production. + +Prompt pour configurer un OIDC provider : + +``` +Could you configure an IAM provider with OIDC in my AWS account with profile ippon-data-lab so that I can use it from GitHub please? You can store the Terraform state in aws-q-academy-terraform-states S3 bucket and use the same profile to store the state on S3. +``` + +Prompt pour déployer un VPC : ``` -Create a VPC with 3 private and 3 public subnets with Terraform and deploy it thanks to GitHub Actions. +Create a VPC with 3 private and 3 public subnets with Terraform in folder vpc-demo and deploy it thanks to GitHub Actions. ``` From 50589aa237e0af9f67d14f4385c29d1639c87777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Aufort?= Date: Wed, 5 Nov 2025 13:39:01 +0100 Subject: [PATCH 5/6] feat: add Q rules for GitHub Actions --- .amazonq/rules/github_actions.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .amazonq/rules/github_actions.md diff --git a/.amazonq/rules/github_actions.md b/.amazonq/rules/github_actions.md new file mode 100644 index 0000000..deb0e35 --- /dev/null +++ b/.amazonq/rules/github_actions.md @@ -0,0 +1,7 @@ +# GitHub Actions Best Practices + +When generating or modifying GitHub actions template files, follow these best practices: + +## Code Best Practices + +- When you use setup-* actions, try to get the latest versions of the software you need to install From 4b9175548eb5e6b9b6a35f1ae01c58fa624d00ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Aufort?= Date: Wed, 5 Nov 2025 13:32:47 +0100 Subject: [PATCH 6/6] feat: test live coding 6 --- .amazonq/agents/default.json | 71 ++++++++++++++++++++ .amazonq/rules/common.md | 7 ++ .amazonq/rules/github_actions.md | 1 + .amazonq/rules/terraform_aws.md | 3 +- .github/workflows/vpc-demo-deploy.yml | 95 +++++++++++++++++++++++++++ .gitignore | 4 +- .pre-commit-config.yaml | 20 ++++++ README.md | 2 +- github-oidc/.terraform.lock.hcl | 25 +++++++ github-oidc/README.md | 59 +++++++++++++++++ github-oidc/backend.tf | 9 +++ github-oidc/iam.tf | 49 ++++++++++++++ github-oidc/outputs.tf | 14 ++++ github-oidc/providers.tf | 4 ++ github-oidc/terraform.tfvars.example | 4 ++ github-oidc/variables.tf | 21 ++++++ github-oidc/versions.tf | 10 +++ vpc-demo/.terraform.lock.hcl | 25 +++++++ vpc-demo/README.md | 47 +++++++++++++ vpc-demo/backend.tf | 8 +++ vpc-demo/outputs.tf | 29 ++++++++ vpc-demo/providers.tf | 13 ++++ vpc-demo/variables.tf | 35 ++++++++++ vpc-demo/versions.tf | 10 +++ vpc-demo/vpc.tf | 20 ++++++ 25 files changed, 581 insertions(+), 4 deletions(-) create mode 100644 .amazonq/agents/default.json create mode 100644 .amazonq/rules/common.md create mode 100644 .github/workflows/vpc-demo-deploy.yml create mode 100644 .pre-commit-config.yaml create mode 100644 github-oidc/.terraform.lock.hcl create mode 100644 github-oidc/README.md create mode 100644 github-oidc/backend.tf create mode 100644 github-oidc/iam.tf create mode 100644 github-oidc/outputs.tf create mode 100644 github-oidc/providers.tf create mode 100644 github-oidc/terraform.tfvars.example create mode 100644 github-oidc/variables.tf create mode 100644 github-oidc/versions.tf create mode 100644 vpc-demo/.terraform.lock.hcl create mode 100644 vpc-demo/README.md create mode 100644 vpc-demo/backend.tf create mode 100644 vpc-demo/outputs.tf create mode 100644 vpc-demo/providers.tf create mode 100644 vpc-demo/variables.tf create mode 100644 vpc-demo/versions.tf create mode 100644 vpc-demo/vpc.tf diff --git a/.amazonq/agents/default.json b/.amazonq/agents/default.json new file mode 100644 index 0000000..06dcefc --- /dev/null +++ b/.amazonq/agents/default.json @@ -0,0 +1,71 @@ +{ + "name": "q_ide_default", + "description": "Default agent configuration", + "prompt": "", + "mcpServers": { + "terraform": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "hashicorp/terraform-mcp-server:0.3" + ] + } + }, + "tools": [ + "fs_read", + "execute_bash", + "fs_write", + "report_issue", + "use_aws", + "@terraform", + "fsRead", + "fsWrite", + "fsReplace", + "listDirectory", + "fileSearch", + "executeBash", + "codeReview", + "displayFindings" + ], + "toolAliases": {}, + "allowedTools": [ + "fs_read", + "report_issue", + "use_aws", + "execute_bash", + "fs_write", + "fsRead", + "listDirectory", + "fileSearch", + "codeReview", + "displayFindings" + ], + "toolsSettings": { + "use_aws": { + "alwaysAllow": [ + { + "preset": "readOnly" + } + ] + }, + "execute_bash": { + "alwaysAllow": [ + { + "preset": "readOnly" + } + ] + } + }, + "resources": [ + "file://AmazonQ.md", + "file://README.md", + "file://.amazonq/rules/**/*.md" + ], + "hooks": { + "agentSpawn": [], + "userPromptSubmit": [] + }, + "useLegacyMcpJson": true +} \ No newline at end of file diff --git a/.amazonq/rules/common.md b/.amazonq/rules/common.md new file mode 100644 index 0000000..ca754ae --- /dev/null +++ b/.amazonq/rules/common.md @@ -0,0 +1,7 @@ +# Common best Practices + +## Linting and testing + +- Use pre-commit to lint the code +- Run "pre-commit run -a" before commiting/pushing to git remote +- Use common hooks from https://github.com/pre-commit/pre-commit-hooks diff --git a/.amazonq/rules/github_actions.md b/.amazonq/rules/github_actions.md index deb0e35..2add436 100644 --- a/.amazonq/rules/github_actions.md +++ b/.amazonq/rules/github_actions.md @@ -5,3 +5,4 @@ When generating or modifying GitHub actions template files, follow these best pr ## Code Best Practices - When you use setup-* actions, try to get the latest versions of the software you need to install +- Use pre-commit to run the hooks of the repository in the CI and install required software so hooks can work diff --git a/.amazonq/rules/terraform_aws.md b/.amazonq/rules/terraform_aws.md index 29475cd..1012535 100644 --- a/.amazonq/rules/terraform_aws.md +++ b/.amazonq/rules/terraform_aws.md @@ -76,8 +76,9 @@ When generating or modifying Terraform code for AWS, follow these best practices - To avoid regressions, it is best to fix dependency versions. - For Terraform OSS modules, use a fixed version (preferably the latest available on the Terraform registry) in the module version field -## Testing +## Linting and testing +- Use pre-commit to lint the code with the following hooks: terraform_fmt, terraform_validate, terraform_docs, terraform_docs, terraform_trivy - Each validator for Terraform input variables must be tested, but only failed cases. - For each module generated, an example must be provided. - For each example, there must be a test that runs it. diff --git a/.github/workflows/vpc-demo-deploy.yml b/.github/workflows/vpc-demo-deploy.yml new file mode 100644 index 0000000..68f72a4 --- /dev/null +++ b/.github/workflows/vpc-demo-deploy.yml @@ -0,0 +1,95 @@ +name: VPC Demo - Terraform Deploy + +on: + push: + branches: + - main + paths: + - 'vpc-demo/**' + pull_request: + branches: + - main + paths: + - 'vpc-demo/**' + workflow_dispatch: + +permissions: + id-token: write + contents: read + pull-requests: write + +jobs: + terraform: + name: Terraform + runs-on: ubuntu-latest + defaults: + run: + working-directory: vpc-demo + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.13.5 + terraform_wrapper: false + + - name: Install terraform-docs + run: | + curl -sSLo /tmp/terraform-docs.tar.gz https://github.com/terraform-docs/terraform-docs/releases/download/v0.19.0/terraform-docs-v0.19.0-linux-amd64.tar.gz + tar -xzf /tmp/terraform-docs.tar.gz -C /tmp + sudo mv /tmp/terraform-docs /usr/local/bin/ + + - name: Install trivy + run: | + curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin + + - name: Run pre-commit + uses: pre-commit/action@v3.0.1 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-role + aws-region: eu-west-3 + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan + id: plan + run: terraform plan -no-color -var="aws_profile=" -out=tfplan + continue-on-error: true + + - name: Comment PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const output = `#### Terraform Plan 📖\`${{ steps.plan.outcome }}\` + +
    Show Plan + + \`\`\`terraform + ${{ steps.plan.outputs.stdout }} + \`\`\` + +
    `; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }) + + - name: Terraform Apply + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + run: terraform apply -auto-approve -var="aws_profile=" tfplan diff --git a/.gitignore b/.gitignore index 6349e36..208a18b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,8 @@ crash.log crash.*.log # Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject # to change depending on the environment. *.tfvars *.tfvars.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..bad13ce --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: + - repo: https://github.com/antonbabenko/pre-commit-terraform + rev: v1.96.2 + hooks: + - id: terraform_fmt + - id: terraform_validate + - id: terraform_docs + args: + - --hook-config=--path-to-file=README.md + - --hook-config=--add-to-existing-file=true + - --hook-config=--create-file-if-not-exists=true + - id: terraform_trivy + args: + - --args=--severity=HIGH,CRITICAL + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-merge-conflict + - id: end-of-file-fixer + - id: trailing-whitespace diff --git a/README.md b/README.md index de9d683..a632dee 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ git checkout -b live-coding-5 Prompt enrichi via les Q rules, le serveur MCP Terraform et code généré à partir d'un schéma Excalidraw ajouté dans le contexte du prompt suivant : ``` -Create the Terraform code from the schema. +Create the Terraform code from the schema. ``` ## Live coding 6 diff --git a/github-oidc/.terraform.lock.hcl b/github-oidc/.terraform.lock.hcl new file mode 100644 index 0000000..a3fa96c --- /dev/null +++ b/github-oidc/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.82.2" + constraints = "5.82.2" + hashes = [ + "h1:RuPaHbllUB8a2TGTyc149wJfoh6zhIEjUvFYKR6iP2E=", + "zh:0262fc96012fb7e173e1b7beadd46dfc25b1dc7eaef95b90e936fc454724f1c8", + "zh:397413613d27f4f54d16efcbf4f0a43c059bd8d827fe34287522ae182a992f9b", + "zh:436c0c5d56e1da4f0a4c13129e12a0b519d12ab116aed52029b183f9806866f3", + "zh:4d942d173a2553d8d532a333a0482a090f4e82a2238acf135578f163b6e68470", + "zh:624aebc549bfbce06cc2ecfd8631932eb874ac7c10eb8466ce5b9a2fbdfdc724", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9e632dee2dfdf01b371cca7854b1ec63ceefa75790e619b0642b34d5514c6733", + "zh:a07567acb115b60a3df8f6048d12735b9b3bcf85ec92a62f77852e13d5a3c096", + "zh:ab7002df1a1be6432ac0eb1b9f6f0dd3db90973cd5b1b0b33d2dae54553dfbd7", + "zh:bc1ff65e2016b018b3e84db7249b2cd0433cb5c81dc81f9f6158f2197d6b9fde", + "zh:bcad84b1d767f87af6e1ba3dc97fdb8f2ad5de9224f192f1412b09aba798c0a8", + "zh:cf917dceaa0f9d55d9ff181b5dcc4d1e10af21b6671811b315ae2a6eda866a2a", + "zh:d8e90ecfb3216f3cc13ccde5a16da64307abb6e22453aed2ac3067bbf689313b", + "zh:d9054e0e40705df729682ad34c20db8695d57f182c65963abd151c6aba1ab0d3", + "zh:ecf3a4f3c57eb7e89f71b8559e2a71e4cdf94eea0118ec4f2cb37e4f4d71a069", + ] +} diff --git a/github-oidc/README.md b/github-oidc/README.md new file mode 100644 index 0000000..64cd4d8 --- /dev/null +++ b/github-oidc/README.md @@ -0,0 +1,59 @@ +# GitHub OIDC Provider for AWS + +This Terraform configuration creates an IAM OIDC provider for GitHub Actions and an IAM role that can be assumed by your GitHub workflows. + +## Prerequisites + +- AWS CLI configured with profile `ippon-data-lab` +- Terraform 1.10.5 + +## Usage + +1. Copy the example variables file: +```bash +cp terraform.tfvars.example terraform.tfvars +``` + +2. Edit `terraform.tfvars` with your GitHub organization and repository: +```hcl +github_org = "your-org" +github_repo = "aws-q-academy" +``` + +3. Initialize Terraform: +```bash +terraform init +``` + +4. Apply the configuration: +```bash +terraform apply +``` + +## GitHub Actions Workflow + +Use the role in your GitHub Actions workflow: + +```yaml +permissions: + id-token: write + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::721665305066:role/github-actions-role + aws-region: eu-west-3 + + - name: Run AWS commands + run: aws sts get-caller-identity +``` + +## Resources Created + +- IAM OIDC Provider for GitHub Actions +- IAM Role with AdministratorAccess (adjust permissions as needed) diff --git a/github-oidc/backend.tf b/github-oidc/backend.tf new file mode 100644 index 0000000..7e38988 --- /dev/null +++ b/github-oidc/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "aws-q-academy-terraform-states" + key = "github-oidc/terraform.tfstate" + region = "eu-west-3" + use_lockfile = true + profile = "ippon-data-lab" + } +} diff --git a/github-oidc/iam.tf b/github-oidc/iam.tf new file mode 100644 index 0000000..1b1e978 --- /dev/null +++ b/github-oidc/iam.tf @@ -0,0 +1,49 @@ +data "aws_caller_identity" "current" {} + +resource "aws_iam_openid_connect_provider" "github" { + url = "https://token.actions.githubusercontent.com" + client_id_list = ["sts.amazonaws.com"] + thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"] + + tags = { + Name = "github-actions-oidc" + ManagedBy = "terraform" + Environment = "shared" + } +} + +resource "aws_iam_role" "github_actions" { + name = var.role_name + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Federated = aws_iam_openid_connect_provider.github.arn + } + Action = "sts:AssumeRoleWithWebIdentity" + Condition = { + StringEquals = { + "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" + } + StringLike = { + "token.actions.githubusercontent.com:sub" = "repo:${var.github_org}/${var.github_repo}:*" + } + } + } + ] + }) + + tags = { + Name = var.role_name + ManagedBy = "terraform" + Environment = "shared" + } +} + +resource "aws_iam_role_policy_attachment" "github_actions_admin" { + role = aws_iam_role.github_actions.name + policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" +} diff --git a/github-oidc/outputs.tf b/github-oidc/outputs.tf new file mode 100644 index 0000000..2fb36c2 --- /dev/null +++ b/github-oidc/outputs.tf @@ -0,0 +1,14 @@ +output "oidc_provider_arn" { + description = "ARN of the GitHub OIDC provider" + value = aws_iam_openid_connect_provider.github.arn +} + +output "role_arn" { + description = "ARN of the IAM role for GitHub Actions" + value = aws_iam_role.github_actions.arn +} + +output "role_name" { + description = "Name of the IAM role for GitHub Actions" + value = aws_iam_role.github_actions.name +} diff --git a/github-oidc/providers.tf b/github-oidc/providers.tf new file mode 100644 index 0000000..d5759c9 --- /dev/null +++ b/github-oidc/providers.tf @@ -0,0 +1,4 @@ +provider "aws" { + region = var.region + profile = "ippon-data-lab" +} diff --git a/github-oidc/terraform.tfvars.example b/github-oidc/terraform.tfvars.example new file mode 100644 index 0000000..b5a8cae --- /dev/null +++ b/github-oidc/terraform.tfvars.example @@ -0,0 +1,4 @@ +github_org = "your-github-org" +github_repo = "aws-q-academy" +role_name = "github-actions-role" +region = "eu-west-3" diff --git a/github-oidc/variables.tf b/github-oidc/variables.tf new file mode 100644 index 0000000..2c52dd5 --- /dev/null +++ b/github-oidc/variables.tf @@ -0,0 +1,21 @@ +variable "region" { + description = "AWS region" + type = string + default = "eu-west-3" +} + +variable "github_org" { + description = "GitHub organization or username" + type = string +} + +variable "github_repo" { + description = "GitHub repository name" + type = string +} + +variable "role_name" { + description = "IAM role name for GitHub Actions" + type = string + default = "github-actions-role" +} diff --git a/github-oidc/versions.tf b/github-oidc/versions.tf new file mode 100644 index 0000000..78abf9e --- /dev/null +++ b/github-oidc/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.7.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "5.82.2" + } + } +} diff --git a/vpc-demo/.terraform.lock.hcl b/vpc-demo/.terraform.lock.hcl new file mode 100644 index 0000000..505e11b --- /dev/null +++ b/vpc-demo/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "6.19.0" + constraints = ">= 6.0.0, 6.19.0" + hashes = [ + "h1:5qq2jk+G9fymBqnOmtHR30L6TLMlMoZ7TsSXOAYl0qU=", + "zh:221061660f519f09e9fcd3bbe1fc5c63e81d997e8e9e759984c80095403d7fd6", + "zh:2436e7f7de4492998d7badfae37f88b042ce993f3fdb411ba7f7a47ff4cc66a2", + "zh:49e78e889bf5f9378dfacb08040553bf1529171222eda931e31fcdeac223e802", + "zh:5a07c255ac8694aebe3e166cc3d0ae5f64e0502d47610fd42be22fd907cb81fa", + "zh:68180e2839faba80b64a5e9eb03cfcc50c75dcf0adb24c6763f97dade8311835", + "zh:6c7ae7fb8d51fecdd000bdcfec60222c1f0aeac41dacf1c33aa16609e6ccaf43", + "zh:6ebea9b2eb48fc44ee5674797a5f3b093640b054803495c10a1e558ccd8fee2b", + "zh:8010d1ca1ab0f89732da3c56351779b6728707270c935bf5fd7d99fdf69bc1da", + "zh:8ca7544dbe3b2499d0179fd289e536aedac25115855434d76a4dc342409d335a", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:c6ed10fb06f561d6785c10ff0f0134b7bfcb9964f1bc38ed8b263480bc3cebc0", + "zh:d011d703a3b22f7e296baa8ddfd4d550875daa3f551a133988f843d6c8e6ec38", + "zh:eceb5a8e929b4b0f26e437d1181aeebfb81f376902e0677ead9b886bb41e7c08", + "zh:eda96ae2f993df469cf5dfeecd842e922de97b8a8600e7d197d884ca5179ad2f", + "zh:fb229392236c0c76214d157bb1c7734ded4fa1221e9ef7831d67258950246ff3", + ] +} diff --git a/vpc-demo/README.md b/vpc-demo/README.md new file mode 100644 index 0000000..9657756 --- /dev/null +++ b/vpc-demo/README.md @@ -0,0 +1,47 @@ +# VPC Demo + +This Terraform module creates an AWS VPC with 3 private and 3 public subnets across 3 availability zones. + +## Architecture + +- **VPC CIDR**: 10.0.0.0/16 +- **Private Subnets**: 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24 +- **Public Subnets**: 10.0.101.0/24, 10.0.102.0/24, 10.0.103.0/24 +- **NAT Gateway**: Single NAT Gateway for cost optimization +- **Internet Gateway**: Enabled for public subnets + +## Usage + +### Local Deployment + +```bash +terraform init +terraform plan +terraform apply +``` + +### GitHub Actions Deployment + +Push to the repository to trigger automatic deployment via GitHub Actions. + +## Variables + +| Name | Description | Default | +|------|-------------|---------| +| region | AWS region | eu-west-3 | +| aws_profile | AWS profile to use | "" | +| project | Project name | vpc-demo | +| environment | Environment name | dev | +| vpc_cidr | CIDR block for VPC | 10.0.0.0/16 | +| availability_zones | List of availability zones | ["eu-west-3a", "eu-west-3b", "eu-west-3c"] | + +## Outputs + +| Name | Description | +|------|-------------| +| vpc_id | The ID of the VPC | +| vpc_cidr_block | The CIDR block of the VPC | +| private_subnets | List of IDs of private subnets | +| public_subnets | List of IDs of public subnets | +| nat_gateway_ids | List of NAT Gateway IDs | +| internet_gateway_id | The ID of the Internet Gateway | diff --git a/vpc-demo/backend.tf b/vpc-demo/backend.tf new file mode 100644 index 0000000..f10bfd3 --- /dev/null +++ b/vpc-demo/backend.tf @@ -0,0 +1,8 @@ +terraform { + backend "s3" { + bucket = "aws-q-academy-terraform-states" + key = "vpc-demo/terraform.tfstate" + region = "eu-west-3" + use_lockfile = true + } +} diff --git a/vpc-demo/outputs.tf b/vpc-demo/outputs.tf new file mode 100644 index 0000000..f26e8ad --- /dev/null +++ b/vpc-demo/outputs.tf @@ -0,0 +1,29 @@ +output "vpc_id" { + description = "The ID of the VPC" + value = module.vpc.vpc_id +} + +output "vpc_cidr_block" { + description = "The CIDR block of the VPC" + value = module.vpc.vpc_cidr_block +} + +output "private_subnets" { + description = "List of IDs of private subnets" + value = module.vpc.private_subnets +} + +output "public_subnets" { + description = "List of IDs of public subnets" + value = module.vpc.public_subnets +} + +output "nat_gateway_ids" { + description = "List of NAT Gateway IDs" + value = module.vpc.natgw_ids +} + +output "internet_gateway_id" { + description = "The ID of the Internet Gateway" + value = module.vpc.igw_id +} diff --git a/vpc-demo/providers.tf b/vpc-demo/providers.tf new file mode 100644 index 0000000..fa21b68 --- /dev/null +++ b/vpc-demo/providers.tf @@ -0,0 +1,13 @@ +provider "aws" { + region = var.region + profile = var.aws_profile + + default_tags { + tags = { + Environment = var.environment + Project = var.project + ManagedBy = "terraform" + RootModuleURL = "https://github.com/ippontech/aws-q-academy" + } + } +} diff --git a/vpc-demo/variables.tf b/vpc-demo/variables.tf new file mode 100644 index 0000000..18c73e5 --- /dev/null +++ b/vpc-demo/variables.tf @@ -0,0 +1,35 @@ +variable "region" { + description = "AWS region" + type = string + default = "eu-west-3" +} + +variable "aws_profile" { + description = "AWS profile to use" + type = string + default = "" +} + +variable "project" { + description = "Project name" + type = string + default = "vpc-demo" +} + +variable "environment" { + description = "Environment name" + type = string + default = "dev" +} + +variable "vpc_cidr" { + description = "CIDR block for VPC" + type = string + default = "10.0.0.0/16" +} + +variable "availability_zones" { + description = "List of availability zones" + type = list(string) + default = ["eu-west-3a", "eu-west-3b", "eu-west-3c"] +} diff --git a/vpc-demo/versions.tf b/vpc-demo/versions.tf new file mode 100644 index 0000000..06bbda1 --- /dev/null +++ b/vpc-demo/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.7.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "6.19.0" + } + } +} diff --git a/vpc-demo/vpc.tf b/vpc-demo/vpc.tf new file mode 100644 index 0000000..716e35b --- /dev/null +++ b/vpc-demo/vpc.tf @@ -0,0 +1,20 @@ +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "6.5.0" + + name = "${var.project}-${var.environment}" + cidr = var.vpc_cidr + + azs = var.availability_zones + private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] + + enable_nat_gateway = true + single_nat_gateway = true + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "${var.project}-${var.environment}" + } +}