├── .cspell.json ├── .editorconfig ├── .envrc.example ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── documentation.yml │ └── feature-request.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── danger.yml │ ├── mega-linter.yml │ ├── tf-docs.yml │ ├── tf-sec.yml │ └── todo.yml ├── .gitignore ├── .jscpd.json ├── .markdownlint.json ├── .mega-linter.yml ├── .shellcheckrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── apps └── start-stop-ec2-instances │ ├── .env.example │ ├── .env.local │ ├── .gitignore │ ├── .node-version │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── serverless.yml │ ├── src │ └── handlers │ │ ├── start.ts │ │ └── stop.ts │ └── tsconfig.json ├── live ├── aws-iam-management │ ├── .envrc.example │ ├── .terraform.lock.hcl │ ├── README.md │ ├── backend.tf │ ├── configs │ │ ├── develop-backend.tfvars │ │ ├── develop.tfvars │ │ ├── sandbox-backend.tfvars │ │ └── sandbox.tfvars │ ├── context.tf │ ├── docs │ │ └── MODULE.md │ ├── iam-role-admin.tf │ ├── iam-role-developer.tf │ ├── iam-role-read-only.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── common-infra │ ├── .terraform.lock.hcl │ ├── README.md │ ├── backend.tf │ ├── configs │ │ ├── develop-backend.tfvars │ │ ├── develop.tfvars │ │ ├── sandbox-backend.tfvars │ │ └── sandbox.tfvars │ ├── context.tf │ ├── docs │ │ └── MODULE.md │ ├── example-rds-instance.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ ├── versions.tf │ └── vpc.tf ├── core-networking │ ├── .envrc.example │ ├── .terraform.lock.hcl │ ├── README.md │ ├── backend.tf │ ├── bastion.tf │ ├── configs │ │ ├── develop-backend.tfvars │ │ ├── develop.tfvars │ │ ├── sandbox-backend.tfvars │ │ └── sandbox.tfvars │ ├── context.tf │ ├── docs │ │ └── MODULE.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ ├── versions.tf │ ├── vpc-endpoints-sg.tf │ ├── vpc-endpoints.tf │ └── vpc.tf ├── services-platform │ ├── .envrc.example │ ├── .terraform.lock.hcl │ ├── README.md │ ├── backend.tf │ ├── configs │ │ ├── develop-backend.tfvars │ │ ├── develop.tfvars │ │ ├── sandbox-backend.tfvars │ │ └── sandbox.tfvars │ ├── context.tf │ ├── docs │ │ └── MODULE.md │ ├── ecr-repositories.tf │ ├── eks-access-entries.tf │ ├── eks.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ ├── versions.tf │ └── vpc.tf └── terraform-backend │ ├── .envrc.example │ ├── .terraform.lock.hcl │ ├── README.md │ ├── backend.tf │ ├── configs │ ├── cross-backend.tfvars │ ├── cross.tfvars │ ├── develop-backend.tfvars │ ├── develop.tfvars │ ├── sandbox-backend.tfvars │ └── sandbox.tfvars │ ├── context.tf │ ├── docs │ └── MODULE.md │ ├── main.tf │ ├── outputs.tf │ ├── tf-backend.tf │ ├── variables.tf │ └── versions.tf ├── modules ├── __template__ │ ├── README.md │ ├── docs │ │ └── MODULE.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── amplify-app │ ├── README.md │ ├── docs │ │ └── MODULE.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── bastion │ ├── README.md │ ├── ami.tf │ ├── docs │ │ ├── MODULE.md │ │ └── secure_bastion_host.png │ ├── ec2.tf │ ├── iam.tf │ ├── outputs.tf │ ├── scripts │ │ ├── connect.sh │ │ └── easy-options │ │ │ ├── README.md │ │ │ ├── easyoptions.sh │ │ │ └── example.sh │ ├── sg.tf │ ├── ssh-key.tf │ ├── ssm.tf │ ├── templates │ │ └── cloud-init.yml.tpl │ ├── variables.tf │ ├── versions.tf │ └── vpc-endpoints.tf ├── docdb │ ├── README.md │ ├── docdb.tf │ ├── docs │ │ └── MODULE.md │ ├── outputs.tf │ ├── secret.tf │ ├── sg.tf │ ├── variables.tf │ └── versions.tf ├── eks │ ├── README.md │ ├── docs │ │ └── MODULE.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ ├── versions.tf │ └── vpc-cni.tf ├── iam-role │ ├── README.md │ ├── docs │ │ └── MODULE.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── mongodb │ ├── README.md │ ├── cluster.tf │ ├── docs │ │ └── MODULE.md │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── msk │ ├── README.md │ ├── docs │ │ └── MODULE.md │ ├── main.tf │ ├── outputs.tf │ ├── security-group-variables.tf │ ├── variables.tf │ └── versions.tf ├── rds-aurora │ ├── README.md │ ├── docs │ │ └── MODULE.md │ ├── outputs.tf │ ├── rds.tf │ ├── variables.tf │ └── versions.tf ├── rds │ ├── README.md │ ├── docs │ │ └── MODULE.md │ ├── outputs.tf │ ├── rds.tf │ ├── ssm.tf │ ├── variables.tf │ └── versions.tf ├── vpc-endpoints │ ├── README.md │ ├── docs │ │ └── MODULE.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf └── vpc │ ├── README.md │ ├── docs │ └── MODULE.md │ ├── outputs.tf │ ├── sg.tf │ ├── ssm.tf │ ├── variables.tf │ ├── versions.tf │ └── vpc.tf ├── scripts ├── README.md ├── connect-to-core-networking-bastion-host.sh ├── connect-to-services-platform-eks-cluster.sh ├── easy-options │ ├── README.md │ ├── easyoptions.sh │ └── example.sh └── generate-services-platform-eks-kubeconfig.sh └── tools └── danger ├── .gitignore ├── .node-version ├── dangerfile.ts ├── package.json ├── pnpm-lock.yaml └── tsconfig.json /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePaths": [ 3 | "**/node_modules/**", 4 | "**/vscode-extension/**", 5 | "**/.git/**", 6 | "**/.pnpm-lock.json", 7 | ".vscode", 8 | "megalinter", 9 | "package-lock.json", 10 | "report" 11 | ], 12 | "language": "en", 13 | "noConfigSearch": true, 14 | "words": ["megalinter", "oxsecurity"], 15 | "version": "0.2" 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.envrc.example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ENVIRONMENT="sandbox" 4 | 5 | AWS_PROFILE="nan-${ENVIRONMENT}-admin" 6 | AWS_REGION="us-west-2" 7 | 8 | export ENVIRONMENT AWS_PROFILE AWS_REGION 9 | 10 | # Check if the AWS session is valid; otherwise, run the SSO login. 11 | if ! aws sts get-caller-identity > /dev/null 2>&1; then 12 | echo "AWS session expired or not found. Logging in with SSO for profile $AWS_PROFILE..." 13 | aws sso login 14 | fi 15 | 16 | KUBECONFIG="$(realpath .kubeconfig/nan-${ENVIRONMENT}-services-platform-cluster)" 17 | 18 | export KUBECONFIG 19 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @ulises-jeremias 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This code of conduct is derived from the Ruby code of conduct. This document provides community guidelines for a safe, respectful, productive, and collaborative place for any person who is willing to contribute to this community. It applies to all “collaborative space”, which is defined as community communications channels. Any violations of the code of conduct may be reported by contacting one or more of the project maintainers either directly. 4 | 5 | - Participants will be tolerant of opposing views. 6 | - Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks. 7 | - When interpreting the words and actions of others, participants should always assume good intentions. 8 | - Behaviour that the project maintainers consider to be harassment will not be tolerated. 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ulises-jeremias # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | description: Report a bug 4 | title: "(short issue description)" 5 | labels: [bug] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the bug 12 | description: What is the problem? A clear and concise description of the bug. 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: expected 17 | attributes: 18 | label: Expected Behavior 19 | description: | 20 | What did you expect to happen? 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: current 25 | attributes: 26 | label: Current Behavior 27 | description: | 28 | What actually happened? 29 | 30 | Please include full errors, uncaught exceptions, stack traces, and relevant logs. 31 | If service/functions responses are relevant, please include wire logs. 32 | validations: 33 | required: true 34 | - type: textarea 35 | id: reproduction 36 | attributes: 37 | label: Reproduction Steps 38 | description: | 39 | Provide a self-contained, concise snippet of code that can be used to reproduce the issue. 40 | For more complex issues provide a repo with the smallest sample that reproduces the bug. 41 | 42 | Avoid including business logic or unrelated code, it makes diagnosis more difficult. 43 | The code sample should be an SSCCE. See http://sscce.org/ for details. In short, please provide a code sample that we can copy/paste, run and reproduce. 44 | validations: 45 | required: true 46 | - type: textarea 47 | id: solution 48 | attributes: 49 | label: Possible Solution 50 | description: | 51 | Suggest a fix/reason for the bug 52 | validations: 53 | required: false 54 | - type: textarea 55 | id: context 56 | attributes: 57 | label: Additional Information/Context 58 | description: | 59 | Anything else that might be relevant for troubleshooting this bug. Providing context helps us come up with a solution that is most useful in the real world. 60 | validations: 61 | required: false 62 | - type: input 63 | id: version 64 | attributes: 65 | label: Version used 66 | description: | 67 | Please provide the version of the repository or tool you are using. 68 | validations: 69 | required: true 70 | - type: input 71 | id: environment 72 | attributes: 73 | label: Environment details (OS name and version, etc.) 74 | validations: 75 | required: true 76 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yml: -------------------------------------------------------------------------------- 1 | name: "📕 Documentation Issue" 2 | description: Report an issue in the Reference documentation or Developer Guide 3 | title: "(short issue description)" 4 | labels: [documentation] 5 | assignees: [] 6 | body: 7 | - type: textarea 8 | id: description 9 | attributes: 10 | label: Describe the issue 11 | description: A clear and concise description of the issue. 12 | validations: 13 | required: true 14 | 15 | - type: textarea 16 | id: links 17 | attributes: 18 | label: Links 19 | description: | 20 | Include links to affected documentation page(s). 21 | validations: 22 | required: true 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature Request 3 | description: Suggest an idea for this project 4 | title: "(short issue description)" 5 | labels: [feature-request] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the feature 12 | description: A clear and concise description of the feature you are proposing. 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: use-case 17 | attributes: 18 | label: Use Case 19 | description: | 20 | Why do you need this feature? For example: "I'm always frustrated when..." 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: solution 25 | attributes: 26 | label: Proposed Solution 27 | description: | 28 | Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. 29 | validations: 30 | required: false 31 | - type: textarea 32 | id: other 33 | attributes: 34 | label: Other Information 35 | description: | 36 | Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. 37 | validations: 38 | required: false 39 | - type: checkboxes 40 | id: ack 41 | attributes: 42 | label: Acknowledgements 43 | options: 44 | - label: I may be able to implement this feature request 45 | required: false 46 | - label: This feature might incur a breaking change 47 | required: false 48 | - type: input 49 | id: version 50 | attributes: 51 | label: Version used 52 | description: | 53 | Please provide the version of the repository or tool you are using. 54 | validations: 55 | required: true 56 | - type: input 57 | id: environment 58 | attributes: 59 | label: Environment details (OS name and version, etc.) 60 | validations: 61 | required: true 62 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please include a summary of the changes and the related issue. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of Change 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] Documentation update 13 | 14 | ## How Has This Been Tested? 15 | 16 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. 17 | 18 | - [ ] Test A 19 | - [ ] Test B 20 | 21 | ## Checklist 22 | 23 | - [ ] My code follows the style guidelines of this project 24 | - [ ] I have performed a self-review of my code 25 | - [ ] I have commented my code, particularly in hard-to-understand areas 26 | - [ ] I have made corresponding changes to the documentation 27 | - [ ] My changes generate no new warnings 28 | - [ ] Any dependent changes have been merged and published in downstream modules 29 | - [ ] I have checked my code and corrected any misspellings 30 | -------------------------------------------------------------------------------- /.github/workflows/danger.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Validation 2 | 3 | concurrency: 4 | group: pull_request_${{ github.event.number }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | pr-review: 14 | name: Danger JS 15 | 16 | if: github.event_name != 'pull_request' || !github.event.pull_request.draft 17 | 18 | runs-on: ubuntu-latest 19 | 20 | permissions: 21 | contents: read 22 | pull-requests: write 23 | 24 | steps: 25 | - name: Begin CI... 26 | uses: actions/checkout@v4 27 | 28 | - uses: actions/setup-node@v4 29 | with: 30 | node-version-file: ./tools/danger/.node-version 31 | 32 | - name: Setup Danger Files 33 | run: | 34 | echo "Setting up Danger files..." 35 | mv tools/danger/* . 36 | 37 | - name: Install dependencies 38 | run: pnpm install --frozen-lockfile 39 | 40 | - name: Danger JS Action 41 | uses: danger/danger-js@12.3.3 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} 45 | -------------------------------------------------------------------------------- /.github/workflows/tf-docs.yml: -------------------------------------------------------------------------------- 1 | name: Terraform Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | tf-docs: 14 | name: Generate Terraform Docs 15 | runs-on: ubuntu-latest 16 | if: github.event_name != 'pull_request' || !github.event.pull_request.draft 17 | permissions: 18 | contents: write 19 | pull-requests: write 20 | steps: 21 | - name: Begin CI... 22 | uses: actions/checkout@v4 23 | with: 24 | # If the event that triggered the workflow is a pull request, then the variable contains ${{ github.event.pull_request.head.ref }} 25 | # If the event that triggered the workflow is a push, then the variable contains ${{ github.ref }} 26 | ref: ${{ github.event.pull_request.head.ref || github.ref }} 27 | 28 | - name: Render terraform docs inside modules 29 | uses: terraform-docs/gh-actions@v1.2.0 30 | with: 31 | working-dir: modules/*,live/* 32 | output-file: docs/MODULE.md 33 | output-method: replace 34 | git-push: "true" 35 | -------------------------------------------------------------------------------- /.github/workflows/tf-sec.yml: -------------------------------------------------------------------------------- 1 | name: Tfsec PR Comment 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | tf-fmt-check: 10 | name: Tfsec PR Comment 11 | 12 | if: github.event_name != 'pull_request' || !github.event.pull_request.draft 13 | 14 | runs-on: ubuntu-latest 15 | 16 | permissions: 17 | contents: read 18 | pull-requests: write 19 | 20 | steps: 21 | - name: Begin CI... 22 | uses: actions/checkout@v3 23 | 24 | - name: Run tfsec 25 | uses: aquasecurity/tfsec-pr-commenter-action@v1.3.1 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /.github/workflows/todo.yml: -------------------------------------------------------------------------------- 1 | name: Todo Checker 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | todo: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Begin CI... 17 | uses: actions/checkout@v3 18 | 19 | - name: TODO to Issue 20 | uses: alstr/todo-to-issue-action@v4 21 | id: todo 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | 11 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 12 | # password, private keys, and other secrets. These should not be part of version 13 | # control as they are data points which are potentially sensitive and subject 14 | # to change depending on the environment. 15 | # *.tfvars 16 | # *.tfvars.json 17 | 18 | # Ignore override files as they are usually used to override resources locally and so 19 | # are not checked in 20 | override.tf 21 | override.tf.json 22 | *_override.tf 23 | *_override.tf.json 24 | 25 | # Include override files you do wish to add to version control using negated pattern 26 | # 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out tfplan 30 | # example: *tfplan* 31 | *tfplan* 32 | 33 | *.pem 34 | 35 | .kubeconfig 36 | .envrc 37 | 38 | megalinter-reports/ 39 | -------------------------------------------------------------------------------- /.jscpd.json: -------------------------------------------------------------------------------- 1 | { 2 | "threshold": 0, 3 | "reporters": ["html", "markdown"], 4 | "ignore": [ 5 | "**/node_modules/**", 6 | "**/.git/**", 7 | "**/.rbenv/**", 8 | "**/.venv/**", 9 | "**/*cache*/**", 10 | "**/.github/**", 11 | "**/.idea/**", 12 | "**/report/**", 13 | "**/*.svg" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD041": false, 3 | "MD042": false, 4 | "MD004": false, 5 | "MD013": false, 6 | "MD033": false, 7 | "MD024": false, 8 | "MD029": false, 9 | "MD053": false, 10 | "MD051": false 11 | } 12 | -------------------------------------------------------------------------------- /.mega-linter.yml: -------------------------------------------------------------------------------- 1 | # Configuration file for MegaLinter 2 | # 3 | # See all available variables at https://megalinter.io/latest/config-file/ and in 4 | # linters documentation 5 | 6 | # all, none, or list of linter keys 7 | APPLY_FIXES: all 8 | 9 | # If you use ENABLE variable, all other languages/formats/tooling-formats will 10 | # be disabled by default 11 | # ENABLE: 12 | 13 | # If you use ENABLE_LINTERS variable, all other linters will be disabled by 14 | # default 15 | # ENABLE_LINTERS: 16 | 17 | # DISABLE: 18 | # - COPYPASTE # Uncomment to disable checks of excessive copy-pastes 19 | # - SPELL # Uncomment to disable checks of spelling mistakes 20 | 21 | SHOW_ELAPSED_TIME: true 22 | 23 | FILEIO_REPORTER: false 24 | 25 | # Uncomment if you want MegaLinter to detect errors but not block CI to pass 26 | # DISABLE_ERRORS: true 27 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | disable=SC1090 # Non-constant source 2 | disable=SC1091 # Not specified as input 3 | disable=SC2034 # Unused variables (they are used but in other scripts) 4 | disable=SC2154 # Referenced, but not assigned 5 | disable=SC2155 # Declare and assign separately to avoid masking return values 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 NaN Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/.env.example: -------------------------------------------------------------------------------- 1 | # Deployment Configuration 2 | SERVICE_NAME=start-stop-ec2-instance 3 | 4 | # EC2 Configuration 5 | EC2_INSTANCE_ID=i-xxxxxxxxxxxxx 6 | START_INSTANCE_CRON="cron(0 4 ? * MON-FRI *)" 7 | STOP_INSTANCE_CRON="cron(0 18 ? * MON-FRI *)" 8 | 9 | SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10 | SLACK_MESSAGE_PREFIX="EC2 Start/Stop" 11 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/.env.local: -------------------------------------------------------------------------------- 1 | # Deployment Configuration 2 | SERVICE_NAME=start-stop-ec2-instance 3 | 4 | # EC2 Configuration 5 | EC2_INSTANCE_ID=i-xxxxxxxxxxxxx 6 | START_INSTANCE_CRON="cron(0 4 ? * MON-FRI *)" 7 | STOP_INSTANCE_CRON="cron(0 18 ? * MON-FRI *)" 8 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | node_modules 3 | .build 4 | 5 | # testing 6 | coverage 7 | 8 | temp 9 | dist 10 | .webpack 11 | .webpackCache 12 | 13 | # Serverless directories 14 | .serverless 15 | 16 | ideas.txt 17 | .DS_Store 18 | .idea/* 19 | 20 | *.log 21 | 22 | .env 23 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/.node-version: -------------------------------------------------------------------------------- 1 | v16.13.2 2 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/README.md: -------------------------------------------------------------------------------- 1 | # Start and Stop EC2 Instance with Serverless Lambda 2 | 3 | We use Serverless Framework to do production ready deployments and local development using 4 | _serverless-offline_. This project is a reference for how to use Serverless Framework to start and stop EC2 instances. 5 | 6 | ## Stages 7 | 8 | > This service is not deployed yet. All the previous existing stages were removed. 9 | > List the stages bellow as soon as they are created in the future. 10 | 11 | ## Aknowledgements 12 | 13 | This project was created using the [Serverless Framework](https://www.serverless.com/) with the following instalation command: 14 | 15 | ```sh 16 | npx serverless install -u https://github.com/nanlabs/devops-reference/tree/main/examples/serverless-start-stop-ec2-instance -n my-project 17 | ``` 18 | 19 | ## Requirements 20 | 21 | **You’ll need to have Node 16.13.2 or later on your local development machine** (but it’s not required on the server). You can use [fnm](https://github.com/Schniz/fnm) to easily switch Node versions between different projects. 22 | 23 | ```sh 24 | git clone https://github.com/nanlabs/devops-reference.git 25 | cd devops-reference/examples/serverless-start-stop-ec2-instance 26 | fnm use 27 | npm install 28 | ``` 29 | 30 | ## Local Development 31 | 32 | This repo has a local development set up that uses the file `.env.local` to configure the local environment. 33 | Run the following command to start the local development server: 34 | 35 | ```sh 36 | npm run sls:offline 37 | ``` 38 | 39 | It will start the following services: 40 | 41 | - AWS Lambda at `http://localhost:3000` 42 | 43 | ## Lambda Deployment 44 | 45 | To deploy the app to AWS, you'll first need to configure your AWS credentials. There are many ways 46 | to set your credentials, for more information refer to the [AWS documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html). 47 | 48 | Once set you can deploy your app using the serverless framework with: 49 | 50 | ```sh 51 | npm run sls:deploy -- --stage 52 | ``` 53 | 54 | ## Recommended Resources 55 | 56 | We recommend the following resources to add local development tools to your project: 57 | 58 | - [LocalStack](https://github.com/nanlabs/devops-reference/tree/main/examples/docker/localstack/) 59 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nan-start-stop-ec2", 3 | "version": "1.0.0", 4 | "description": "Start and Stop EC2 instances", 5 | "main": "index.js", 6 | "scripts": { 7 | "sls": "serverless", 8 | "sls:deploy": "serverless deploy", 9 | "sls:offline": "serverless offline start", 10 | "sls:package": "serverless package", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+ssh://git@github.com/nanlabs/devops-reference.git" 16 | }, 17 | "author": "darioscrivano", 18 | "license": "MIT", 19 | "homepage": "https://github.com/nanlabs/devops-reference#readme", 20 | "devDependencies": { 21 | "@types/aws-lambda": "^8.10.108", 22 | "@types/aws-sdk": "^2.7.0", 23 | "serverless": "^3.23.0", 24 | "serverless-offline": "^14.3.4", 25 | "serverless-plugin-typescript": "^2.1.4", 26 | "typescript": "^4.8.4" 27 | }, 28 | "dependencies": { 29 | "aws-lambda": "^1.0.7", 30 | "aws-sdk": "^2.1380.0", 31 | "axios": "^1.7.4", 32 | "http-errors": "^2.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/serverless.yml: -------------------------------------------------------------------------------- 1 | service: ${env:SERVICE_NAME} 2 | 3 | frameworkVersion: "3" 4 | 5 | plugins: 6 | - serverless-plugin-typescript 7 | - serverless-offline 8 | 9 | useDotenv: true 10 | 11 | package: 12 | individually: true 13 | excludeDevDependencies: true 14 | 15 | custom: 16 | serverless-offline: 17 | httpPort: 3000 18 | port: 3000 19 | useChildProcesses: true 20 | noPrependStageInUrl: true 21 | 22 | provider: 23 | name: aws 24 | runtime: nodejs16.x 25 | stage: local 26 | region: us-east-2 27 | deploymentBucket: 28 | blockPublicAccess: true 29 | versioning: true 30 | serverSideEncryption: AES256 31 | iamRoleStatements: 32 | - Effect: Allow 33 | Action: 34 | - ec2:DescribeInstanceStatus 35 | - ec2:DescribeInstances 36 | Resource: 37 | - "*" 38 | - Effect: Allow 39 | Action: 40 | - ec2:StartInstances 41 | - ec2:StopInstances 42 | Resource: 43 | - Fn::Join: 44 | - ":" 45 | - - "arn:aws:ec2" 46 | - Ref: AWS::Region 47 | - Ref: AWS::AccountId 48 | - "instance/${env:EC2_INSTANCE_ID}" 49 | 50 | functions: 51 | startInstance: 52 | handler: src/handlers/start.handler 53 | timeout: 30 54 | events: 55 | - schedule: ${env:START_INSTANCE_CRON, 'cron(0 0 * * ? *)'} 56 | environment: 57 | EC2_INSTANCE_ID: ${env:EC2_INSTANCE_ID} 58 | SLACK_WEBHOOK_URL: ${env:SLACK_WEBHOOK_URL, ''} 59 | SLACK_MESSAGE_PREFIX: ${env:SLACK_MESSAGE_PREFIX, ''} 60 | stopInstance: 61 | handler: src/handlers/stop.handler 62 | events: 63 | - schedule: ${env:STOP_INSTANCE_CRON, 'cron(0 0 * * ? *)'} 64 | environment: 65 | EC2_INSTANCE_ID: ${env:EC2_INSTANCE_ID} 66 | SLACK_WEBHOOK_URL: ${env:SLACK_WEBHOOK_URL, ''} 67 | SLACK_MESSAGE_PREFIX: ${env:SLACK_MESSAGE_PREFIX, ''} 68 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/src/handlers/start.ts: -------------------------------------------------------------------------------- 1 | import { ScheduledHandler } from "aws-lambda"; 2 | import { EC2 } from "aws-sdk"; 3 | import axios from "axios"; 4 | import fetch from "node-fetch"; 5 | 6 | export const handler: ScheduledHandler = async (event) => { 7 | const ec2 = new EC2({ region: event.region }); 8 | 9 | const instanceId = process.env.EC2_INSTANCE_ID; 10 | 11 | if (!instanceId) { 12 | throw new Error("EC2_INSTANCE_ID is not defined"); 13 | } 14 | 15 | // check if instance is running. If not, start it 16 | const instanceStatus = await ec2 17 | .describeInstanceStatus({ 18 | InstanceIds: [instanceId], 19 | }) 20 | .promise(); 21 | 22 | if ( 23 | instanceStatus?.InstanceStatuses && 24 | instanceStatus?.InstanceStatuses[0]?.InstanceState?.Name === "running" 25 | ) { 26 | console.log("Instance is already running"); 27 | return; 28 | } 29 | 30 | const result = await ec2 31 | .startInstances({ InstanceIds: [instanceId] }) 32 | .promise(); 33 | if (result.$response.error) { 34 | throw result.$response.error; 35 | } 36 | 37 | console.log("Instance started"); 38 | 39 | // wait for instance to be running 40 | await ec2.waitFor("instanceRunning", { InstanceIds: [instanceId] }).promise(); 41 | console.log("Instance is running"); 42 | 43 | // get instance public ip 44 | const instance = await ec2 45 | .describeInstances({ InstanceIds: [instanceId] }) 46 | .promise(); 47 | const publicIp = instance?.Reservations?.[0]?.Instances?.[0]?.PublicIpAddress; 48 | 49 | if (!publicIp) { 50 | console.warn("Instance public IP is not defined"); 51 | return; 52 | } 53 | 54 | console.log(`Instance public IP: ${publicIp}`); 55 | 56 | // send it to Slack 57 | const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL; 58 | const messagePrefix = process.env.SLACK_MESSAGE_PREFIX || ""; 59 | 60 | if (!slackWebhookUrl) { 61 | console.warn("SLACK_WEBHOOK_URL is not defined"); 62 | return; 63 | } 64 | 65 | // use fetch to send a POST request to Slack webhook 66 | await axios.post(slackWebhookUrl, { 67 | text: `${messagePrefix} Instance started. Public IP: ${publicIp}`, 68 | }); 69 | 70 | console.log("Slack message sent"); 71 | }; 72 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/src/handlers/stop.ts: -------------------------------------------------------------------------------- 1 | import { ScheduledHandler } from "aws-lambda"; 2 | import { EC2 } from "aws-sdk"; 3 | import axios from "axios"; 4 | 5 | export const handler: ScheduledHandler = async (event) => { 6 | const ec2 = new EC2({ region: event.region }); 7 | 8 | const instanceId = process.env.EC2_INSTANCE_ID; 9 | 10 | if (!instanceId) { 11 | throw new Error("EC2_INSTANCE_ID is not defined"); 12 | } 13 | 14 | // check if instance is running. If yes, stop it 15 | const instanceStatus = await ec2 16 | .describeInstanceStatus({ 17 | InstanceIds: [instanceId], 18 | }) 19 | .promise(); 20 | 21 | if ( 22 | instanceStatus?.InstanceStatuses?.length === 0 || 23 | (instanceStatus?.InstanceStatuses && 24 | instanceStatus?.InstanceStatuses[0].InstanceState?.Name !== "running") 25 | ) { 26 | console.log("Instance is not running. Nothing to do"); 27 | return; 28 | } 29 | 30 | const result = await ec2 31 | .stopInstances({ InstanceIds: [instanceId] }) 32 | .promise(); 33 | if (result.$response.error) { 34 | throw result.$response.error; 35 | } 36 | 37 | console.log("Instance stopped"); 38 | 39 | // send it to Slack 40 | const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL; 41 | const messagePrefix = process.env.SLACK_MESSAGE_PREFIX || ""; 42 | 43 | if (!slackWebhookUrl) { 44 | console.warn("SLACK_WEBHOOK_URL is not defined"); 45 | return; 46 | } 47 | 48 | // use fetch to send a POST request to Slack webhook 49 | await axios.post(slackWebhookUrl, { 50 | text: `${messagePrefix} Instance stopped`, 51 | }); 52 | 53 | console.log("Slack message sent"); 54 | }; 55 | -------------------------------------------------------------------------------- /apps/start-stop-ec2-instances/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "module": "commonjs", 5 | "lib": ["es2019"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": false, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "esModuleInterop": true, 16 | "noFallthroughCasesInSwitch": false, 17 | "inlineSourceMap": true, 18 | "inlineSources": true, 19 | "experimentalDecorators": true, 20 | "strictPropertyInitialization": false, 21 | "typeRoots": ["./node_modules/@types"] 22 | }, 23 | "exclude": ["node_modules"], 24 | "include": ["src/**/*"], 25 | "baseUrl": "./src" 26 | } 27 | -------------------------------------------------------------------------------- /live/aws-iam-management/.envrc.example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source_up 4 | 5 | # Validate that the config file exists for the environment 6 | if [ ! -f "./configs/${ENVIRONMENT}.tfvars" ] || [ ! -f "./configs/${ENVIRONMENT}-backend.tfvars" ]; then 7 | echo "Error: Configuration file for environment '$ENVIRONMENT' not found." 8 | return 1 9 | fi 10 | 11 | TF_WORKSPACE="${ENVIRONMENT}" 12 | 13 | export TF_WORKSPACE 14 | -------------------------------------------------------------------------------- /live/aws-iam-management/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "5.6.2" 6 | constraints = ">= 5.0.0" 7 | hashes = [ 8 | "h1:ew6CvX7pUxD8I+1Etv25m5Okk1I8Gna/7Uqd+8Z6vxI=", 9 | "zh:25322d7e1f0054550357d5a03fe29168cc179421e5dcf44b28c25a99d8d6e4e7", 10 | "zh:394aa5bff70003e76d1d33ef4fe37c4826918577cf339d35e56ae84d01e86765", 11 | "zh:485b288bf95b5d3014903e386e8ee2d1182e507f746bc988458b9711c7df7171", 12 | "zh:48cf69750681337d64df7e402116a6753a40b6702c49fc9232ff6621947d85af", 13 | "zh:6ab11d052d681b5157e261b9dd9167482acffe2018fffd1204575e9bf6a08522", 14 | "zh:882f22d0e6c16cd5a5f01a0ae817b1e75e928667d21d986b93a4ee74fa62c067", 15 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 16 | "zh:ac3403e3ab5c10869b23626467b919e3f010e7cae6e0acf8515e0cefab0dbff0", 17 | "zh:b959a425c9be83838895e8626037656bf5db81397ad0078595d3b72fd1b816bc", 18 | "zh:bf390951f21a5fe6b96b206c5496fda4d8b95823bd00d1c03a4a53dd215d882a", 19 | "zh:c3534972986cd68a421359f07ab86631ffa8731606936276fce18ec8ae9045f4", 20 | "zh:d4cf29d67ead2c5feb999c2882e5365bd4d04c115e98fb1639b747b682507fea", 21 | "zh:dea669eea5bca9b57dae2975ec783d577d58a39eec769d1c9bd7fc4d50f241d0", 22 | "zh:e7a82063d01eb2be3fd192afbad910150fe8054731db20c1b22c714d9391dbe5", 23 | "zh:fdbbf96948e96dfed614ea4daa4f1706859122a3f978c42c37db8727cb55c94f", 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /live/aws-iam-management/README.md: -------------------------------------------------------------------------------- 1 | # AWS IAM Management 2 | 3 | 🏢 This directory contains the Terraform configuration for managing IAM roles and policies across our AWS accounts. It provides a ready-to-use Terraform module for secure and efficient access management. 4 | 5 | ## Features 6 | 7 | - ✨ Comprehensive Root Terraform module for IAM management. 8 | - 🔒 Utilization of AWS SSO for centralized user access management. 9 | - 📜 Configured IAM roles and policies for various access levels (Admin, Read-Only, etc.). 10 | - 📂 Modular and reusable Terraform code to ensure consistency across multiple AWS accounts. 11 | 12 | ## Prerequisites 13 | 14 | - [Direnv](https://direnv.net/) for loading environment variables. 15 | - [Terraform](https://www.terraform.io/downloads.html) for infrastructure provisioning. 16 | - [TFswitch](https://tfswitch.warrensbox.com/) to switch between Terraform versions easily. 17 | 18 | ## Setup 19 | 20 | 1. **Change Directory:** 21 | 22 | Navigate to the directory containing the Terraform configuration: 23 | 24 | ```sh 25 | cd live/aws-iam-management 26 | ``` 27 | 28 | 2. **Create .envrc file:** 29 | 30 | Create a new `.envrc` file in this directory by copying the `.envrc.example` file: 31 | 32 | ```sh 33 | cp .envrc.example .envrc 34 | ``` 35 | 36 | Then, update the `.envrc` file with the values for your environment! 37 | 38 | 3. **Load Environment Variables:** 39 | 40 | Load the environment variables using `direnv`: 41 | 42 | ```sh 43 | direnv allow 44 | ``` 45 | 46 | 4. **Set Terraform Version:** 47 | 48 | Ensure you are using the correct Terraform version: 49 | 50 | ```sh 51 | tfswitch 52 | ``` 53 | 54 | 5. **Initialize Terraform:** 55 | 56 | Initialize the working directory with the required providers and modules: 57 | 58 | ```sh 59 | terraform init -backend-config="./configs/${ENVIRONMENT}-backend.tfvars" 60 | ``` 61 | 62 | 6. **Workspace Management:** 63 | 64 | Select or create a new workspace tailored to your deployment environment: 65 | 66 | ```sh 67 | # Select an existing workspace 68 | terraform workspace select "${TF_WORKSPACE}" 69 | 70 | # Create a new workspace if it doesn't exist and select it 71 | terraform workspace new "${TF_WORKSPACE}" 72 | ``` 73 | 74 | ## Deploy 75 | 76 | 🚀 **Deployment Instructions:** 77 | 78 | 1. **Plan Your Deployment:** 79 | 80 | Review and verify the deployment plan: 81 | 82 | ```sh 83 | terraform plan -var-file "./configs/${ENVIRONMENT}.tfvars" -out "${ENVIRONMENT}.tfplan" 84 | ``` 85 | 86 | 2. **Execute the Plan:** 87 | 88 | Apply the planned configuration to provision the infrastructure: 89 | 90 | ```sh 91 | terraform apply "${ENVIRONMENT}.tfplan" 92 | ``` 93 | 94 | ## Post Deployment Steps 95 | 96 | After successfully deploying the IAM roles and policies, follow these steps to ensure everything is working as expected: 97 | 98 | ### Connecting to the AWS Management Console via SSO 99 | 100 | To connect to the AWS Management Console using the newly configured IAM roles, follow these steps: 101 | 102 | 1. **Sign in to the AWS SSO portal:** 103 | 104 | Use your AWS SSO credentials to sign in to the AWS SSO portal. 105 | 106 | 2. **Select the AWS account and role:** 107 | 108 | Choose the appropriate AWS account and IAM role to access the AWS Management Console. 109 | 110 | ## Destroy 111 | 112 | To destroy the infrastructure, run the following command: 113 | 114 | ```sh 115 | terraform destroy -var-file "./configs/${ENVIRONMENT}.tfvars" 116 | ``` 117 | 118 | ## Module Documentation 119 | 120 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running the following command from the module directory: 121 | 122 | ```sh 123 | terraform-docs md . > ./docs/MODULE.md 124 | ``` 125 | 126 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 127 | -------------------------------------------------------------------------------- /live/aws-iam-management/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /live/aws-iam-management/configs/develop-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-develop-tfbackend-state" 3 | key = "nan-develop-aws-iam-management.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/aws-iam-management/configs/develop.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "aws-iam-management" 5 | namespace = "nan" 6 | environment = "develop" 7 | tags = { 8 | } 9 | -------------------------------------------------------------------------------- /live/aws-iam-management/configs/sandbox-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-sandbox-tfbackend-state" 3 | key = "nan-sandbox-aws-iam-management.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/aws-iam-management/configs/sandbox.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "aws-iam-management" 5 | namespace = "nan" 6 | environment = "sandbox" 7 | tags = { 8 | } 9 | -------------------------------------------------------------------------------- /live/aws-iam-management/context.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to use for servers, tags, etc" 3 | type = string 4 | default = "name" 5 | } 6 | 7 | variable "namespace" { 8 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" 9 | type = string 10 | default = "development" 11 | } 12 | 13 | variable "environment" { 14 | description = "Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT'" 15 | type = string 16 | default = "development" 17 | } 18 | 19 | variable "stage" { 20 | description = "Stage, e.g. 'build', 'test', 'deploy', 'release'" 21 | type = string 22 | # not required, so no default 23 | default = null 24 | } 25 | 26 | variable "tags" { 27 | description = "Any extra tags to assign to objects" 28 | type = map(any) 29 | default = {} 30 | } 31 | 32 | // Keep labels, tags consistent 33 | module "label" { 34 | source = "cloudposse/label/null" 35 | version = "0.25.0" 36 | 37 | name = var.name 38 | environment = var.environment 39 | namespace = var.namespace 40 | stage = var.stage 41 | 42 | delimiter = "-" 43 | label_order = ["namespace", "environment", "stage", "name", "attributes"] 44 | tags = var.tags 45 | } 46 | -------------------------------------------------------------------------------- /live/aws-iam-management/docs/MODULE.md: -------------------------------------------------------------------------------- 1 | 2 | ## Requirements 3 | 4 | | Name | Version | 5 | |------|---------| 6 | | [terraform](#requirement\_terraform) | >= 1.0 | 7 | | [aws](#requirement\_aws) | >= 5.0.0 | 8 | 9 | ## Providers 10 | 11 | | Name | Version | 12 | |------|---------| 13 | | [aws](#provider\_aws) | 5.6.2 | 14 | 15 | ## Modules 16 | 17 | | Name | Source | Version | 18 | |------|--------|---------| 19 | | [admin\_role](#module\_admin\_role) | ../../modules/iam-role | n/a | 20 | | [developer\_role](#module\_developer\_role) | ../../modules/iam-role | n/a | 21 | | [label](#module\_label) | cloudposse/label/null | 0.25.0 | 22 | | [read\_only\_role](#module\_read\_only\_role) | ../../modules/iam-role | n/a | 23 | 24 | ## Resources 25 | 26 | | Name | Type | 27 | |------|------| 28 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 29 | | [aws_iam_policy_document.developer_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 30 | 31 | ## Inputs 32 | 33 | | Name | Description | Type | Default | Required | 34 | |------|-------------|------|---------|:--------:| 35 | | [environment](#input\_environment) | Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT' | `string` | `"development"` | no | 36 | | [name](#input\_name) | Name to use for servers, tags, etc | `string` | `"name"` | no | 37 | | [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `"development"` | no | 38 | | [region](#input\_region) | AWS region | `string` | `"us-west-2"` | no | 39 | | [stage](#input\_stage) | Stage, e.g. 'build', 'test', 'deploy', 'release' | `string` | `null` | no | 40 | | [tags](#input\_tags) | Any extra tags to assign to objects | `map(any)` | `{}` | no | 41 | 42 | ## Outputs 43 | 44 | | Name | Description | 45 | |------|-------------| 46 | | [admin\_role\_arn](#output\_admin\_role\_arn) | The Amazon Resource Name (ARN) specifying the role | 47 | | [admin\_role\_id](#output\_admin\_role\_id) | The stable and unique string identifying the role | 48 | | [admin\_role\_name](#output\_admin\_role\_name) | The name of the IAM role created | 49 | | [developer\_role\_arn](#output\_developer\_role\_arn) | The Amazon Resource Name (ARN) specifying the role | 50 | | [developer\_role\_id](#output\_developer\_role\_id) | The stable and unique string identifying the role | 51 | | [developer\_role\_name](#output\_developer\_role\_name) | The name of the IAM role created | 52 | | [read\_only\_role\_arn](#output\_read\_only\_role\_arn) | The Amazon Resource Name (ARN) specifying the role | 53 | | [read\_only\_role\_id](#output\_read\_only\_role\_id) | The stable and unique string identifying the role | 54 | | [read\_only\_role\_name](#output\_read\_only\_role\_name) | The name of the IAM role created | 55 | -------------------------------------------------------------------------------- /live/aws-iam-management/iam-role-admin.tf: -------------------------------------------------------------------------------- 1 | module "admin_role" { 2 | source = "../../modules/iam-role" 3 | name = "${module.label.id}-AdminRole" 4 | policy_description = "Administrator access to all AWS resources" 5 | role_description = "IAM role with full access permissions" 6 | principals = { 7 | AWS = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 8 | } 9 | policy_document_count = 0 10 | managed_policy_arns = ["arn:aws:iam::aws:policy/AdministratorAccess"] 11 | } 12 | 13 | output "admin_role_name" { 14 | value = module.admin_role.name 15 | description = "The name of the IAM role created" 16 | } 17 | 18 | output "admin_role_id" { 19 | value = module.admin_role.id 20 | description = "The stable and unique string identifying the role" 21 | } 22 | 23 | output "admin_role_arn" { 24 | value = module.admin_role.arn 25 | description = "The Amazon Resource Name (ARN) specifying the role" 26 | } 27 | -------------------------------------------------------------------------------- /live/aws-iam-management/iam-role-developer.tf: -------------------------------------------------------------------------------- 1 | module "developer_role" { 2 | source = "../../modules/iam-role" 3 | name = "${module.label.id}-DeveloperRole" 4 | policy_description = "Developer access to specific AWS resources" 5 | role_description = "IAM role with permissions to manage resources needed for development" 6 | principals = { 7 | AWS = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 8 | } 9 | policy_documents = [ 10 | data.aws_iam_policy_document.developer_access.json 11 | ] 12 | } 13 | 14 | data "aws_iam_policy_document" "developer_access" { 15 | statement { 16 | sid = "DeveloperAccess" 17 | effect = "Allow" 18 | resources = [ 19 | "arn:aws:s3:::*/*", 20 | ] 21 | actions = [ 22 | "s3:GetObject" 23 | ] 24 | } 25 | } 26 | 27 | output "developer_role_name" { 28 | value = module.developer_role.name 29 | description = "The name of the IAM role created" 30 | } 31 | 32 | output "developer_role_id" { 33 | value = module.developer_role.id 34 | description = "The stable and unique string identifying the role" 35 | } 36 | 37 | output "developer_role_arn" { 38 | value = module.developer_role.arn 39 | description = "The Amazon Resource Name (ARN) specifying the role" 40 | } 41 | -------------------------------------------------------------------------------- /live/aws-iam-management/iam-role-read-only.tf: -------------------------------------------------------------------------------- 1 | module "read_only_role" { 2 | source = "../../modules/iam-role" 3 | name = "${module.label.id}-ReadOnlyRole" 4 | policy_description = "Read-only access to all AWS resources" 5 | role_description = "IAM role with read-only access permissions" 6 | principals = { 7 | AWS = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 8 | } 9 | policy_document_count = 0 10 | managed_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"] 11 | } 12 | 13 | output "read_only_role_name" { 14 | value = module.read_only_role.name 15 | description = "The name of the IAM role created" 16 | } 17 | 18 | output "read_only_role_id" { 19 | value = module.read_only_role.id 20 | description = "The stable and unique string identifying the role" 21 | } 22 | 23 | output "read_only_role_arn" { 24 | value = module.read_only_role.arn 25 | description = "The Amazon Resource Name (ARN) specifying the role" 26 | } 27 | -------------------------------------------------------------------------------- /live/aws-iam-management/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | 4 | default_tags { 5 | tags = merge(module.label.tags, { 6 | ManagedBy = "terraform" 7 | Owner = "NaNLABS" 8 | Project = "[Project Name]" 9 | Repository = "https://github.com/nanlabs/terraform-aws-starter" 10 | RepositoryPath = "live/aws-iam-management" 11 | }) 12 | } 13 | } 14 | 15 | data "aws_caller_identity" "current" {} 16 | -------------------------------------------------------------------------------- /live/aws-iam-management/outputs.tf: -------------------------------------------------------------------------------- 1 | # common outputs 2 | -------------------------------------------------------------------------------- /live/aws-iam-management/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region" 3 | type = string 4 | default = "us-west-2" 5 | } 6 | -------------------------------------------------------------------------------- /live/aws-iam-management/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /live/common-infra/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /live/common-infra/configs/develop-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-develop-tfbackend-state" 3 | key = "nan-develop-common-infra.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/common-infra/configs/develop.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "common-infra" 5 | namespace = "nan" 6 | environment = "develop" 7 | tags = { 8 | } 9 | 10 | # Core Networking settings 11 | 12 | core_networking_ssm_parameter_prefix = "/nan-core-networking-develop" 13 | 14 | # RDS Database settings 15 | 16 | example_db_name = "example" 17 | example_db_master_username = "root" 18 | -------------------------------------------------------------------------------- /live/common-infra/configs/sandbox-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-sandbox-tfbackend-state" 3 | key = "nan-sandbox-common-infra.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/common-infra/configs/sandbox.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "common-infra" 5 | namespace = "nan" 6 | environment = "sandbox" 7 | tags = { 8 | } 9 | 10 | # Core Networking settings 11 | 12 | core_networking_ssm_parameter_prefix = "/nan-core-networking-sandbox" 13 | 14 | # RDS Database settings 15 | 16 | example_db_name = "example" 17 | example_db_master_username = "root" 18 | -------------------------------------------------------------------------------- /live/common-infra/context.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to use for servers, tags, etc" 3 | type = string 4 | default = "name" 5 | } 6 | 7 | variable "namespace" { 8 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" 9 | type = string 10 | default = "development" 11 | } 12 | 13 | variable "environment" { 14 | description = "Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT'" 15 | type = string 16 | default = "development" 17 | } 18 | 19 | variable "stage" { 20 | description = "Stage, e.g. 'build', 'test', 'deploy', 'release'" 21 | type = string 22 | # not required, so no default 23 | default = null 24 | } 25 | 26 | variable "tags" { 27 | description = "Any extra tags to assign to objects" 28 | type = map(any) 29 | default = {} 30 | } 31 | 32 | // Keep labels, tags consistent 33 | module "label" { 34 | source = "cloudposse/label/null" 35 | version = "0.25.0" 36 | 37 | name = var.name 38 | environment = var.environment 39 | namespace = var.namespace 40 | stage = var.stage 41 | 42 | delimiter = "-" 43 | label_order = ["namespace", "environment", "stage", "name", "attributes"] 44 | tags = var.tags 45 | } 46 | -------------------------------------------------------------------------------- /live/common-infra/example-rds-instance.tf: -------------------------------------------------------------------------------- 1 | variable "example_db_name" { 2 | description = "The name of the database to create" 3 | type = string 4 | default = "mydb" 5 | } 6 | 7 | variable "example_db_master_username" { 8 | description = "The username for the master DB user" 9 | type = string 10 | default = "root" 11 | } 12 | 13 | module "exampledb" { 14 | source = "../../modules/rds" 15 | 16 | name = "${module.label.id}-exampledb" 17 | 18 | vpc_id = data.aws_ssm_parameter.vpc_id.value 19 | db_subnet_group = data.aws_ssm_parameter.database_subnet_group.value 20 | vpc_security_group_ids = [module.security_group.security_group_id] 21 | 22 | db_name = var.example_db_name 23 | db_master_username = var.example_db_master_username 24 | db_port = 5432 25 | 26 | db_instance_class = "db.t4g.small" 27 | 28 | allocated_storage = 20 29 | 30 | manage_master_user_password = true 31 | 32 | tags = merge( 33 | module.label.tags, 34 | { 35 | "Name" = "${module.label.id}-exampledb" 36 | } 37 | ) 38 | } 39 | 40 | module "security_group" { 41 | source = "terraform-aws-modules/security-group/aws" 42 | version = "~> 4.0" 43 | 44 | name = "${module.label.id}-exampledb-security-group" 45 | description = "Security group for ${module.label.id}-exampledb" 46 | vpc_id = data.aws_vpc.vpc.id 47 | 48 | ingress_with_cidr_blocks = [ 49 | { 50 | from_port = 5432 51 | to_port = 5432 52 | protocol = "tcp" 53 | description = "RDS DB Instance access from within VPC" 54 | cidr_blocks = data.aws_vpc.main.cidr_block 55 | } 56 | ] 57 | 58 | egress_rules = ["all-all"] 59 | 60 | tags = merge( 61 | module.label.tags, 62 | { 63 | "Name" = "${module.label.id}-exampledb-security-group" 64 | } 65 | ) 66 | } 67 | 68 | output "example_db_instance_address" { 69 | description = "The address of the RDS instance" 70 | value = module.exampledb.db_instance_address 71 | } 72 | 73 | output "example_db_instance_port" { 74 | description = "The database port" 75 | value = module.exampledb.db_instance_port 76 | } 77 | 78 | output "example_db_instance_name" { 79 | description = "The database name" 80 | value = module.exampledb.db_instance_name 81 | } 82 | 83 | output "example_db_instance_master_user_secret_arn" { 84 | description = "The ARN of the secret containing the connection details for the RDS instance" 85 | value = module.exampledb.db_instance_master_user_secret_arn 86 | } 87 | 88 | # get the id of the secret containing the connection details for the RDS instance 89 | # and output it 90 | data "aws_secretsmanager_secret" "db_instance_master_user" { 91 | arn = module.exampledb.db_instance_master_user_secret_arn 92 | } 93 | 94 | output "example_db_instance_master_user_secret_id" { 95 | description = "The ID of the secret containing the connection details for the RDS instance" 96 | value = data.aws_secretsmanager_secret.db_instance_master_user.id 97 | } 98 | 99 | output "example_db_instance_master_user_secret_name" { 100 | description = "The name of the secret containing the connection details for the RDS instance" 101 | value = data.aws_secretsmanager_secret.db_instance_master_user.name 102 | } 103 | -------------------------------------------------------------------------------- /live/common-infra/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | 4 | default_tags { 5 | tags = merge(module.label.tags, { 6 | ManagedBy = "terraform" 7 | Owner = "NaNLABS" 8 | Project = "[Project Name]" 9 | Repository = "https://github.com/nanlabs/terraform-aws-starter" 10 | RepositoryPath = "live/common-infra" 11 | }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /live/common-infra/outputs.tf: -------------------------------------------------------------------------------- 1 | # common outputs 2 | -------------------------------------------------------------------------------- /live/common-infra/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region" 3 | type = string 4 | default = "us-west-2" 5 | } 6 | -------------------------------------------------------------------------------- /live/common-infra/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /live/common-infra/vpc.tf: -------------------------------------------------------------------------------- 1 | variable "core_networking_ssm_parameter_prefix" { 2 | description = "The SSM parameter prefix for core networking parameters" 3 | type = string 4 | } 5 | 6 | variable "bastion_security_group_name" { 7 | description = "The name of the bastion security group" 8 | type = string 9 | } 10 | 11 | locals { 12 | vpc_id = data.aws_ssm_parameter.vpc_id.value 13 | private_subnets = split(",", data.aws_ssm_parameter.private_subnets.value) 14 | public_subnets = split(",", data.aws_ssm_parameter.public_subnets.value) 15 | } 16 | 17 | data "aws_ssm_parameter" "vpc_id" { 18 | name = "${var.core_networking_ssm_parameter_prefix}/vpc_id" 19 | } 20 | 21 | data "aws_ssm_parameter" "private_subnets" { 22 | name = "${var.core_networking_ssm_parameter_prefix}/private_subnets" 23 | } 24 | 25 | data "aws_ssm_parameter" "public_subnets" { 26 | name = "${var.core_networking_ssm_parameter_prefix}/public_subnets" 27 | } 28 | 29 | data "aws_security_group" "default" { 30 | vpc_id = local.vpc_id 31 | 32 | filter { 33 | name = "group-name" 34 | values = ["default"] 35 | } 36 | } 37 | 38 | data "aws_vpc" "vpc" { 39 | id = local.vpc_id 40 | } 41 | 42 | data "aws_security_group" "bastion_security_group" { 43 | name = var.bastion_security_group_name 44 | vpc_id = local.vpc_id 45 | } 46 | -------------------------------------------------------------------------------- /live/core-networking/.envrc.example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source_up 4 | 5 | # Validate that the config file exists for the environment 6 | if [ ! -f "./configs/${ENVIRONMENT}.tfvars" ] || [ ! -f "./configs/${ENVIRONMENT}-backend.tfvars" ]; then 7 | echo "Error: Configuration file for environment '$ENVIRONMENT' not found." 8 | return 1 9 | fi 10 | 11 | TF_WORKSPACE="${ENVIRONMENT}" 12 | 13 | export TF_WORKSPACE 14 | -------------------------------------------------------------------------------- /live/core-networking/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "5.6.2" 6 | constraints = ">= 3.29.0, >= 3.72.0, >= 5.0.0" 7 | hashes = [ 8 | "h1:ew6CvX7pUxD8I+1Etv25m5Okk1I8Gna/7Uqd+8Z6vxI=", 9 | "zh:25322d7e1f0054550357d5a03fe29168cc179421e5dcf44b28c25a99d8d6e4e7", 10 | "zh:394aa5bff70003e76d1d33ef4fe37c4826918577cf339d35e56ae84d01e86765", 11 | "zh:485b288bf95b5d3014903e386e8ee2d1182e507f746bc988458b9711c7df7171", 12 | "zh:48cf69750681337d64df7e402116a6753a40b6702c49fc9232ff6621947d85af", 13 | "zh:6ab11d052d681b5157e261b9dd9167482acffe2018fffd1204575e9bf6a08522", 14 | "zh:882f22d0e6c16cd5a5f01a0ae817b1e75e928667d21d986b93a4ee74fa62c067", 15 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 16 | "zh:ac3403e3ab5c10869b23626467b919e3f010e7cae6e0acf8515e0cefab0dbff0", 17 | "zh:b959a425c9be83838895e8626037656bf5db81397ad0078595d3b72fd1b816bc", 18 | "zh:bf390951f21a5fe6b96b206c5496fda4d8b95823bd00d1c03a4a53dd215d882a", 19 | "zh:c3534972986cd68a421359f07ab86631ffa8731606936276fce18ec8ae9045f4", 20 | "zh:d4cf29d67ead2c5feb999c2882e5365bd4d04c115e98fb1639b747b682507fea", 21 | "zh:dea669eea5bca9b57dae2975ec783d577d58a39eec769d1c9bd7fc4d50f241d0", 22 | "zh:e7a82063d01eb2be3fd192afbad910150fe8054731db20c1b22c714d9391dbe5", 23 | "zh:fdbbf96948e96dfed614ea4daa4f1706859122a3f978c42c37db8727cb55c94f", 24 | ] 25 | } 26 | 27 | provider "registry.terraform.io/hashicorp/template" { 28 | version = "2.2.0" 29 | hashes = [ 30 | "h1:94qn780bi1qjrbC3uQtjJh3Wkfwd5+tTtJHOb7KTg9w=", 31 | "zh:01702196f0a0492ec07917db7aaa595843d8f171dc195f4c988d2ffca2a06386", 32 | "zh:09aae3da826ba3d7df69efeb25d146a1de0d03e951d35019a0f80e4f58c89b53", 33 | "zh:09ba83c0625b6fe0a954da6fbd0c355ac0b7f07f86c91a2a97849140fea49603", 34 | "zh:0e3a6c8e16f17f19010accd0844187d524580d9fdb0731f675ffcf4afba03d16", 35 | "zh:45f2c594b6f2f34ea663704cc72048b212fe7d16fb4cfd959365fa997228a776", 36 | "zh:77ea3e5a0446784d77114b5e851c970a3dde1e08fa6de38210b8385d7605d451", 37 | "zh:8a154388f3708e3df5a69122a23bdfaf760a523788a5081976b3d5616f7d30ae", 38 | "zh:992843002f2db5a11e626b3fc23dc0c87ad3729b3b3cff08e32ffb3df97edbde", 39 | "zh:ad906f4cebd3ec5e43d5cd6dc8f4c5c9cc3b33d2243c89c5fc18f97f7277b51d", 40 | "zh:c979425ddb256511137ecd093e23283234da0154b7fa8b21c2687182d9aea8b2", 41 | ] 42 | } 43 | 44 | provider "registry.terraform.io/hashicorp/tls" { 45 | version = "4.0.4" 46 | hashes = [ 47 | "h1:pe9vq86dZZKCm+8k1RhzARwENslF3SXb9ErHbQfgjXU=", 48 | "zh:23671ed83e1fcf79745534841e10291bbf34046b27d6e68a5d0aab77206f4a55", 49 | "zh:45292421211ffd9e8e3eb3655677700e3c5047f71d8f7650d2ce30242335f848", 50 | "zh:59fedb519f4433c0fdb1d58b27c210b27415fddd0cd73c5312530b4309c088be", 51 | "zh:5a8eec2409a9ff7cd0758a9d818c74bcba92a240e6c5e54b99df68fff312bbd5", 52 | "zh:5e6a4b39f3171f53292ab88058a59e64825f2b842760a4869e64dc1dc093d1fe", 53 | "zh:810547d0bf9311d21c81cc306126d3547e7bd3f194fc295836acf164b9f8424e", 54 | "zh:824a5f3617624243bed0259d7dd37d76017097dc3193dac669be342b90b2ab48", 55 | "zh:9361ccc7048be5dcbc2fafe2d8216939765b3160bd52734f7a9fd917a39ecbd8", 56 | "zh:aa02ea625aaf672e649296bce7580f62d724268189fe9ad7c1b36bb0fa12fa60", 57 | "zh:c71b4cd40d6ec7815dfeefd57d88bc592c0c42f5e5858dcc88245d371b4b8b1e", 58 | "zh:dabcd52f36b43d250a3d71ad7abfa07b5622c69068d989e60b79b2bb4f220316", 59 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /live/core-networking/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /live/core-networking/bastion.tf: -------------------------------------------------------------------------------- 1 | variable "enable_bastion" { 2 | type = bool 3 | description = "Enable bastion host" 4 | default = false 5 | } 6 | 7 | module "bastion" { 8 | count = var.enable_bastion ? 1 : 0 9 | 10 | source = "../../modules/bastion" 11 | name = "${module.label.id}-bastion" 12 | vpc_id = module.vpc.vpc_id 13 | private_subnets = module.vpc.private_subnets 14 | instance_type = "t2.medium" 15 | root_volume_size = 32 16 | tags = merge(module.label.tags, { "Name" = "${module.label.id}-bastion" }) 17 | } 18 | 19 | output "bastion_instance_id" { 20 | value = var.enable_bastion ? module.bastion[0].instance_id : null 21 | } 22 | 23 | output "bastion_instance_profile" { 24 | value = var.enable_bastion ? module.bastion[0].instance_profile : null 25 | } 26 | 27 | output "ssm_parameter_bastion_ssh_key" { 28 | description = "name of the ssm parameter for the bastion ssh key" 29 | value = var.enable_bastion ? module.bastion[0].ssm_parameter_ssh_key : null 30 | } 31 | -------------------------------------------------------------------------------- /live/core-networking/configs/develop-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-develop-tfbackend-state" 3 | key = "nan-develop-core-networking.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/core-networking/configs/develop.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "core-networking" 5 | namespace = "nan" 6 | environment = "develop" 7 | tags = {} 8 | 9 | # Resources settings 10 | 11 | vpc_cidr_block = "10.0.0.0/16" 12 | enable_bastion = true 13 | 14 | cluster_name = "nan-develop-services-platform-cluster" 15 | -------------------------------------------------------------------------------- /live/core-networking/configs/sandbox-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-sandbox-tfbackend-state" 3 | key = "nan-sandbox-core-networking.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/core-networking/configs/sandbox.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "core-networking" 5 | namespace = "nan" 6 | environment = "sandbox" 7 | tags = {} 8 | 9 | # Resources settings 10 | 11 | vpc_cidr_block = "10.0.0.0/16" 12 | enable_bastion = true 13 | 14 | cluster_name = "nan-sandbox-services-platform-cluster" 15 | -------------------------------------------------------------------------------- /live/core-networking/context.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to use for servers, tags, etc" 3 | type = string 4 | default = "name" 5 | } 6 | 7 | variable "namespace" { 8 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" 9 | type = string 10 | default = "development" 11 | } 12 | 13 | variable "environment" { 14 | description = "Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT'" 15 | type = string 16 | default = "development" 17 | } 18 | 19 | variable "stage" { 20 | description = "Stage, e.g. 'build', 'test', 'deploy', 'release'" 21 | type = string 22 | # not required, so no default 23 | default = null 24 | } 25 | 26 | variable "tags" { 27 | description = "Any extra tags to assign to objects" 28 | type = map(any) 29 | default = {} 30 | } 31 | 32 | // Keep labels, tags consistent 33 | module "label" { 34 | source = "cloudposse/label/null" 35 | version = "0.25.0" 36 | 37 | name = var.name 38 | environment = var.environment 39 | namespace = var.namespace 40 | stage = var.stage 41 | 42 | delimiter = "-" 43 | label_order = ["namespace", "environment", "stage", "name", "attributes"] 44 | tags = var.tags 45 | } 46 | -------------------------------------------------------------------------------- /live/core-networking/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | 4 | default_tags { 5 | tags = merge(module.label.tags, { 6 | ManagedBy = "terraform" 7 | Owner = "NaNLABS" 8 | Project = "[Project Name]" 9 | Repository = "https://github.com/nanlabs/terraform-aws-starter" 10 | RepositoryPath = "live/core-networking" 11 | }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /live/core-networking/outputs.tf: -------------------------------------------------------------------------------- 1 | # common outputs 2 | -------------------------------------------------------------------------------- /live/core-networking/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region" 3 | type = string 4 | default = "us-west-2" 5 | } 6 | -------------------------------------------------------------------------------- /live/core-networking/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /live/core-networking/vpc-endpoints-sg.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | # Check if the bastion exists (based on the length of the module.bastion array) 3 | bastion_sg_rule = length(module.bastion) > 0 ? [{ 4 | rule = "https-443-tcp" 5 | source_security_group_id = module.bastion[0].security_group_id 6 | description = "vpc ssm vpce security group ingress rule for bastion host" 7 | }] : [] 8 | 9 | # Define the ingress rules for app security group 10 | app_sg_rule = [{ 11 | rule = "https-443-tcp" 12 | source_security_group_id = module.vpc.app_security_group 13 | description = "vpc ssm vpce security group ingress rule for app security group" 14 | }] 15 | 16 | # Concatenate the rules for app SG and bastion SG (if bastion exists) 17 | final_ssm_vpce_rules = concat(local.app_sg_rule, local.bastion_sg_rule) 18 | } 19 | 20 | module "ssm_vpce_sg" { 21 | source = "terraform-aws-modules/security-group/aws" 22 | version = "4.17.1" 23 | 24 | name = "${module.label.id}-vpc-ssm-vpce-security-group" 25 | description = "Security group for SSM VPC endpoint" 26 | vpc_id = module.vpc.vpc_id 27 | 28 | ingress_with_source_security_group_id = local.final_ssm_vpce_rules 29 | 30 | tags = var.tags 31 | } 32 | 33 | module "ec2messages_vpce_sg" { 34 | source = "terraform-aws-modules/security-group/aws" 35 | version = "4.17.1" 36 | 37 | name = "${module.label.id}-vpc-ec2messages-vpce-security-group" 38 | description = "Security group for EC2 Messages VPC endpoint" 39 | vpc_id = module.vpc.vpc_id 40 | 41 | ingress_with_source_security_group_id = local.final_ssm_vpce_rules 42 | 43 | tags = var.tags 44 | } 45 | 46 | module "ssmmessages_vpce_sg" { 47 | source = "terraform-aws-modules/security-group/aws" 48 | version = "4.17.1" 49 | 50 | name = "${module.label.id}-vpc-ssmmessages-vpce-security-group" 51 | description = "Security group for SSM Messages VPC endpoint" 52 | vpc_id = module.vpc.vpc_id 53 | 54 | ingress_with_source_security_group_id = local.final_ssm_vpce_rules 55 | 56 | tags = var.tags 57 | } 58 | -------------------------------------------------------------------------------- /live/core-networking/vpc-endpoints.tf: -------------------------------------------------------------------------------- 1 | module "vpc_endpoints" { 2 | source = "../../modules/vpc-endpoints" 3 | 4 | vpc_id = module.vpc.vpc_id 5 | 6 | endpoints = { 7 | s3 = { 8 | service = "s3" 9 | service_type = "Gateway" 10 | route_table_ids = module.vpc.public_route_table_ids 11 | security_group_ids = [module.vpc.default_security_group_id] 12 | policy = null 13 | tags = { Name = "${module.label.id}-s3-vpc-endpoint" } 14 | }, 15 | ssm = { 16 | service = "ssm" 17 | service_type = "Interface" 18 | security_group_ids = [module.ssm_vpce_sg.security_group_id] 19 | private_dns_enabled = true 20 | subnet_ids = module.vpc.private_subnets 21 | policy = null 22 | tags = { Name = "${var.name}-ssm-vpc-endpoint" } 23 | }, 24 | ec2messages = { 25 | service = "ec2messages" 26 | service_type = "Interface" 27 | security_group_ids = [module.ec2messages_vpce_sg.security_group_id] 28 | private_dns_enabled = true 29 | subnet_ids = module.vpc.private_subnets 30 | policy = null 31 | tags = { Name = "${var.name}-ec2messages-vpc-endpoint" } 32 | }, 33 | ssmmessages = { 34 | service = "ssmmessages" 35 | service_type = "Interface" 36 | security_group_ids = [module.ssmmessages_vpce_sg.security_group_id] 37 | private_dns_enabled = true 38 | subnet_ids = module.vpc.private_subnets 39 | policy = null 40 | tags = { Name = "${var.name}-ssmmessages-vpc-endpoint" } 41 | } 42 | } 43 | 44 | tags = module.label.tags 45 | } 46 | -------------------------------------------------------------------------------- /live/core-networking/vpc.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_cidr_block" { 2 | description = "CIDR block for the VPC" 3 | type = string 4 | default = "10.0.0.0/16" 5 | } 6 | 7 | variable "cluster_name" { 8 | description = "The name of the EKS cluster" 9 | type = string 10 | } 11 | 12 | locals { 13 | # The usage of the specific kubernetes.io/cluster/* resource tags below are required 14 | # for EKS and Kubernetes to discover and manage networking resources 15 | # https://aws.amazon.com/premiumsupport/knowledge-center/eks-vpc-subnet-discovery/ 16 | tags = merge(var.tags, { "kubernetes.io/cluster/${var.cluster_name}" = "shared" }) 17 | 18 | # required tags to make ALB ingress work https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html 19 | public_subnets_additional_tags = { 20 | "kubernetes.io/role/elb" : 1 21 | } 22 | private_subnets_additional_tags = { 23 | "kubernetes.io/role/internal-elb" : 1 24 | } 25 | } 26 | 27 | module "vpc" { 28 | source = "../../modules/vpc" 29 | name = module.label.id 30 | vpc_cidr_block = var.vpc_cidr_block 31 | enable_nat_gateway = true 32 | single_nat_gateway = true 33 | tags = local.tags 34 | public_subnet_tags = local.public_subnets_additional_tags 35 | private_subnet_tags = local.private_subnets_additional_tags 36 | } 37 | 38 | output "ssm_parameter_vpc_id" { 39 | description = "name of the ssm parameter for the vpc id" 40 | value = module.vpc.ssm_parameter_vpc_id 41 | } 42 | 43 | output "ssm_parameter_public_subnets" { 44 | description = "name of the ssm parameter for the public subnets" 45 | value = module.vpc.ssm_parameter_public_subnets 46 | } 47 | 48 | output "ssm_parameter_private_subnets" { 49 | description = "name of the ssm parameter for the private subnets" 50 | value = module.vpc.ssm_parameter_private_subnets 51 | } 52 | 53 | output "ssm_parameter_database_subnets" { 54 | description = "name of the ssm parameter for the database subnets" 55 | value = module.vpc.ssm_parameter_database_subnets 56 | } 57 | 58 | output "ssm_parameter_app_subnets" { 59 | description = "name of the ssm parameter for the app subnets" 60 | value = module.vpc.ssm_parameter_app_subnets 61 | } 62 | 63 | output "ssm_parameter_app_security_group" { 64 | description = "name of the ssm parameter for the app security group" 65 | value = module.vpc.ssm_parameter_app_security_group 66 | } 67 | -------------------------------------------------------------------------------- /live/services-platform/.envrc.example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source_up 4 | 5 | # Validate that the config file exists for the environment 6 | if [ ! -f "./configs/${ENVIRONMENT}.tfvars" ] || [ ! -f "./configs/${ENVIRONMENT}-backend.tfvars" ]; then 7 | echo "Error: Configuration file for environment '$ENVIRONMENT' not found." 8 | return 1 9 | fi 10 | 11 | TF_WORKSPACE="${ENVIRONMENT}" 12 | 13 | export TF_WORKSPACE 14 | -------------------------------------------------------------------------------- /live/services-platform/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /live/services-platform/configs/develop-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-develop-tfbackend-state" 3 | key = "nan-develop-services-platform.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/services-platform/configs/develop.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "services-platform" 5 | namespace = "nan" 6 | environment = "develop" 7 | tags = { 8 | } 9 | 10 | # Core Networking settings 11 | 12 | core_networking_ssm_parameter_prefix = "/nan-develop-core-networking" 13 | bastion_security_group_name = "nan-develop-core-networking-bastion-ec2-20240819051723215800000003" 14 | 15 | # EKS settings 16 | 17 | kubernetes_version = "1.30" 18 | oidc_provider_enabled = true 19 | 20 | addons = [ 21 | { 22 | addon_name = "kube-proxy" 23 | addon_version = "v1.30.0-eksbuild.3" 24 | }, 25 | { 26 | addon_name = "coredns" 27 | addon_version = "v1.11.1-eksbuild.9" 28 | }, 29 | { 30 | addon_name = "aws-ebs-csi-driver" 31 | addon_version = "v1.32.0-eksbuild.1" 32 | } 33 | ] 34 | 35 | node_groups = [ 36 | { 37 | instance_types = ["t3.xlarge"] 38 | min_size = 1 39 | max_size = 1 40 | desired_size = 1 41 | health_check_type = "EC2" 42 | start_stop_schedule_enabled = true 43 | start_schedule_recurrence_cron = "0 13 * * 1-5" # 8 AM EST (1 PM UTC), weekdays 44 | stop_schedule_recurrence_cron = "0 1 * * *" # 8 PM EST (1 AM UTC) 45 | kubernetes_labels = { 46 | "environment" = "develop" 47 | "tier" = "frontend" 48 | } 49 | }, 50 | { 51 | instance_types = ["t3.xlarge"] 52 | min_size = 1 53 | max_size = 1 54 | desired_size = 1 55 | health_check_type = "EC2" 56 | start_stop_schedule_enabled = true 57 | start_schedule_recurrence_cron = "0 13 * * 1-5" # 8 AM EST (1 PM UTC), weekdays 58 | stop_schedule_recurrence_cron = "0 1 * * *" # 8 PM EST (1 AM UTC) 59 | kubernetes_labels = { 60 | "environment" = "develop" 61 | "tier" = "backend" 62 | } 63 | } 64 | ] 65 | 66 | private_ipv6_enabled = false 67 | enabled_cluster_log_types = ["api"] 68 | cluster_log_retention_period = 30 69 | cluster_encryption_config_enabled = false 70 | cluster_encryption_config_kms_key_id = "" 71 | cluster_encryption_config_kms_key_enable_key_rotation = false 72 | cluster_encryption_config_kms_key_deletion_window_in_days = 10 73 | cluster_encryption_config_kms_key_policy = "" 74 | cluster_encryption_config_resources = ["secrets"] 75 | namespaces = [] 76 | -------------------------------------------------------------------------------- /live/services-platform/configs/sandbox-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-sandbox-tfbackend-state" 3 | key = "nan-sandbox-services-platform.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/services-platform/configs/sandbox.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "services-platform" 5 | namespace = "nan" 6 | environment = "sandbox" 7 | tags = { 8 | } 9 | 10 | # Core Networking settings 11 | 12 | core_networking_ssm_parameter_prefix = "/nan-sandbox-core-networking" 13 | bastion_security_group_name = "nan-sandbox-core-networking-bastion-ec2-20240709184223546200000004" 14 | 15 | # EKS settings 16 | 17 | kubernetes_version = "1.30" 18 | oidc_provider_enabled = true 19 | 20 | addons = [ 21 | { 22 | addon_name = "kube-proxy" 23 | addon_version = "v1.30.0-eksbuild.3" 24 | }, 25 | { 26 | addon_name = "coredns" 27 | addon_version = "v1.11.1-eksbuild.9" 28 | }, 29 | { 30 | addon_name = "aws-ebs-csi-driver" 31 | addon_version = "v1.32.0-eksbuild.1" 32 | } 33 | ] 34 | 35 | node_groups = [ 36 | { 37 | instance_types = ["t3.xlarge"] 38 | min_size = 1 39 | max_size = 1 40 | desired_size = 1 41 | health_check_type = "EC2" 42 | start_stop_schedule_enabled = true 43 | start_schedule_recurrence_cron = "0 13 * * 1-5" # 8 AM EST (1 PM UTC), weekdays 44 | stop_schedule_recurrence_cron = "0 1 * * *" # 8 PM EST (1 AM UTC) 45 | kubernetes_labels = { 46 | "environment" = "sandbox" 47 | "tier" = "frontend" 48 | } 49 | }, 50 | { 51 | instance_types = ["t3.xlarge"] 52 | min_size = 1 53 | max_size = 1 54 | desired_size = 1 55 | health_check_type = "EC2" 56 | start_stop_schedule_enabled = true 57 | start_schedule_recurrence_cron = "0 13 * * 1-5" # 8 AM EST (1 PM UTC), weekdays 58 | stop_schedule_recurrence_cron = "0 1 * * *" # 8 PM EST (1 AM UTC) 59 | kubernetes_labels = { 60 | "environment" = "sandbox" 61 | "tier" = "backend" 62 | } 63 | } 64 | ] 65 | 66 | private_ipv6_enabled = false 67 | enabled_cluster_log_types = ["api"] 68 | cluster_log_retention_period = 30 69 | cluster_encryption_config_enabled = false 70 | cluster_encryption_config_kms_key_id = "" 71 | cluster_encryption_config_kms_key_enable_key_rotation = false 72 | cluster_encryption_config_kms_key_deletion_window_in_days = 10 73 | cluster_encryption_config_kms_key_policy = "" 74 | cluster_encryption_config_resources = ["secrets"] 75 | namespaces = [] 76 | -------------------------------------------------------------------------------- /live/services-platform/context.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to use for servers, tags, etc" 3 | type = string 4 | default = "name" 5 | } 6 | 7 | variable "namespace" { 8 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" 9 | type = string 10 | default = "development" 11 | } 12 | 13 | variable "environment" { 14 | description = "Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT'" 15 | type = string 16 | default = "development" 17 | } 18 | 19 | variable "stage" { 20 | description = "Stage, e.g. 'build', 'test', 'deploy', 'release'" 21 | type = string 22 | # not required, so no default 23 | default = null 24 | } 25 | 26 | variable "tags" { 27 | description = "Any extra tags to assign to objects" 28 | type = map(any) 29 | default = {} 30 | } 31 | 32 | // Keep labels, tags consistent 33 | module "label" { 34 | source = "cloudposse/label/null" 35 | version = "0.25.0" 36 | 37 | name = var.name 38 | environment = var.environment 39 | namespace = var.namespace 40 | stage = var.stage 41 | 42 | delimiter = "-" 43 | label_order = ["namespace", "environment", "stage", "name", "attributes"] 44 | tags = var.tags 45 | } 46 | -------------------------------------------------------------------------------- /live/services-platform/ecr-repositories.tf: -------------------------------------------------------------------------------- 1 | variable "ecr_repositories" { 2 | description = "List of ECR repositories to create" 3 | type = map(object({ 4 | repository_image_tag_mutability = optional(string) 5 | })) 6 | } 7 | 8 | module "ecr" { 9 | source = "terraform-aws-modules/ecr/aws" 10 | version = "2.3.0" 11 | 12 | for_each = var.ecr_repositories 13 | 14 | repository_name = each.key 15 | repository_image_tag_mutability = each.value.repository_image_tag_mutability 16 | 17 | repository_read_write_access_arns = concat([data.aws_caller_identity.current.arn], 18 | module.eks_cluster.eks_cluster_node_group_roles_arns 19 | ) 20 | create_lifecycle_policy = true 21 | repository_lifecycle_policy = jsonencode({ 22 | rules = [ 23 | { 24 | rulePriority = 1, 25 | description = "Keep last 30 images", 26 | selection = { 27 | tagStatus = "tagged", 28 | tagPrefixList = ["v"], 29 | countType = "imageCountMoreThan", 30 | countNumber = 30 31 | }, 32 | action = { 33 | type = "expire" 34 | } 35 | } 36 | ] 37 | }) 38 | 39 | repository_force_delete = true 40 | 41 | tags = module.label.tags 42 | } 43 | 44 | output "ecr_repository_urls" { 45 | value = [ 46 | for repo in module.ecr : repo.repository_url 47 | ] 48 | description = "List of ECR repository URLs" 49 | } 50 | -------------------------------------------------------------------------------- /live/services-platform/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | 4 | default_tags { 5 | tags = merge(module.label.tags, { 6 | ManagedBy = "terraform" 7 | Owner = "NaNLABS" 8 | Project = "[Project Name]" 9 | Repository = "https://github.com/nanlabs/terraform-aws-starter" 10 | RepositoryPath = "live/services-platform" 11 | }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /live/services-platform/outputs.tf: -------------------------------------------------------------------------------- 1 | # common outputs 2 | -------------------------------------------------------------------------------- /live/services-platform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region" 3 | type = string 4 | default = "us-west-2" 5 | } 6 | -------------------------------------------------------------------------------- /live/services-platform/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /live/services-platform/vpc.tf: -------------------------------------------------------------------------------- 1 | variable "core_networking_ssm_parameter_prefix" { 2 | description = "The SSM parameter prefix for core networking parameters" 3 | type = string 4 | } 5 | 6 | variable "bastion_security_group_name" { 7 | description = "The name of the bastion security group" 8 | type = string 9 | } 10 | 11 | locals { 12 | vpc_id = data.aws_ssm_parameter.vpc_id.value 13 | private_subnets = split(",", data.aws_ssm_parameter.private_subnets.value) 14 | public_subnets = split(",", data.aws_ssm_parameter.public_subnets.value) 15 | } 16 | 17 | data "aws_ssm_parameter" "vpc_id" { 18 | name = "${var.core_networking_ssm_parameter_prefix}/vpc_id" 19 | } 20 | 21 | data "aws_ssm_parameter" "private_subnets" { 22 | name = "${var.core_networking_ssm_parameter_prefix}/private_subnets" 23 | } 24 | 25 | data "aws_ssm_parameter" "public_subnets" { 26 | name = "${var.core_networking_ssm_parameter_prefix}/public_subnets" 27 | } 28 | 29 | data "aws_ssm_parameter" "app_security_group" { 30 | name = "${var.core_networking_ssm_parameter_prefix}/app_security_group" 31 | } 32 | 33 | data "aws_security_group" "default" { 34 | vpc_id = local.vpc_id 35 | 36 | filter { 37 | name = "group-name" 38 | values = ["default"] 39 | } 40 | } 41 | 42 | data "aws_vpc" "vpc" { 43 | id = local.vpc_id 44 | } 45 | 46 | data "aws_security_group" "bastion_security_group" { 47 | name = var.bastion_security_group_name 48 | vpc_id = data.aws_vpc.vpc.id 49 | } 50 | 51 | data "aws_security_group" "app_security_group" { 52 | id = data.aws_ssm_parameter.app_security_group.value 53 | vpc_id = data.aws_vpc.vpc.id 54 | } 55 | -------------------------------------------------------------------------------- /live/terraform-backend/.envrc.example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source_up 4 | 5 | # Validate that the config file exists for the environment 6 | if [ ! -f "./configs/${ENVIRONMENT}.tfvars" ] || [ ! -f "./configs/${ENVIRONMENT}-backend.tfvars" ]; then 7 | echo "Error: Configuration file for environment '$ENVIRONMENT' not found." 8 | return 1 9 | fi 10 | 11 | TF_WORKSPACE="${ENVIRONMENT}" 12 | 13 | export TF_WORKSPACE 14 | -------------------------------------------------------------------------------- /live/terraform-backend/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "5.6.2" 6 | constraints = ">= 4.9.0, >= 5.0.0" 7 | hashes = [ 8 | "h1:ew6CvX7pUxD8I+1Etv25m5Okk1I8Gna/7Uqd+8Z6vxI=", 9 | "zh:25322d7e1f0054550357d5a03fe29168cc179421e5dcf44b28c25a99d8d6e4e7", 10 | "zh:394aa5bff70003e76d1d33ef4fe37c4826918577cf339d35e56ae84d01e86765", 11 | "zh:485b288bf95b5d3014903e386e8ee2d1182e507f746bc988458b9711c7df7171", 12 | "zh:48cf69750681337d64df7e402116a6753a40b6702c49fc9232ff6621947d85af", 13 | "zh:6ab11d052d681b5157e261b9dd9167482acffe2018fffd1204575e9bf6a08522", 14 | "zh:882f22d0e6c16cd5a5f01a0ae817b1e75e928667d21d986b93a4ee74fa62c067", 15 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 16 | "zh:ac3403e3ab5c10869b23626467b919e3f010e7cae6e0acf8515e0cefab0dbff0", 17 | "zh:b959a425c9be83838895e8626037656bf5db81397ad0078595d3b72fd1b816bc", 18 | "zh:bf390951f21a5fe6b96b206c5496fda4d8b95823bd00d1c03a4a53dd215d882a", 19 | "zh:c3534972986cd68a421359f07ab86631ffa8731606936276fce18ec8ae9045f4", 20 | "zh:d4cf29d67ead2c5feb999c2882e5365bd4d04c115e98fb1639b747b682507fea", 21 | "zh:dea669eea5bca9b57dae2975ec783d577d58a39eec769d1c9bd7fc4d50f241d0", 22 | "zh:e7a82063d01eb2be3fd192afbad910150fe8054731db20c1b22c714d9391dbe5", 23 | "zh:fdbbf96948e96dfed614ea4daa4f1706859122a3f978c42c37db8727cb55c94f", 24 | ] 25 | } 26 | 27 | provider "registry.terraform.io/hashicorp/local" { 28 | version = "2.4.0" 29 | constraints = ">= 2.0.0" 30 | hashes = [ 31 | "h1:R97FTYETo88sT2VHfMgkPU3lzCsZLunPftjSI5vfKe8=", 32 | "zh:53604cd29cb92538668fe09565c739358dc53ca56f9f11312b9d7de81e48fab9", 33 | "zh:66a46e9c508716a1c98efbf793092f03d50049fa4a83cd6b2251e9a06aca2acf", 34 | "zh:70a6f6a852dd83768d0778ce9817d81d4b3f073fab8fa570bff92dcb0824f732", 35 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 36 | "zh:82a803f2f484c8b766e2e9c32343e9c89b91997b9f8d2697f9f3837f62926b35", 37 | "zh:9708a4e40d6cc4b8afd1352e5186e6e1502f6ae599867c120967aebe9d90ed04", 38 | "zh:973f65ce0d67c585f4ec250c1e634c9b22d9c4288b484ee2a871d7fa1e317406", 39 | "zh:c8fa0f98f9316e4cfef082aa9b785ba16e36ff754d6aba8b456dab9500e671c6", 40 | "zh:cfa5342a5f5188b20db246c73ac823918c189468e1382cb3c48a9c0c08fc5bf7", 41 | "zh:e0e2b477c7e899c63b06b38cd8684a893d834d6d0b5e9b033cedc06dd7ffe9e2", 42 | "zh:f62d7d05ea1ee566f732505200ab38d94315a4add27947a60afa29860822d3fc", 43 | "zh:fa7ce69dde358e172bd719014ad637634bbdabc49363104f4fca759b4b73f2ce", 44 | ] 45 | } 46 | 47 | provider "registry.terraform.io/hashicorp/time" { 48 | version = "0.11.2" 49 | constraints = ">= 0.7.1" 50 | hashes = [ 51 | "h1:bC4b7n4g30ciIn5w6b66mXSTIo2CH6XQbp+gBdDvlYs=", 52 | "zh:02588b5b8ba5d31e86d93edc93b306bcbf47c789f576769245968cc157a9e8c5", 53 | "zh:088a30c23796133678d1d6614da5cf5544430570408a17062288b58c0bd67ac8", 54 | "zh:0df5faa072d67616154d38021934d8a8a316533429a3f582df3b4b48c836cf89", 55 | "zh:12edeeaef96c47f694bd1ba7ead6ccdb96028b25df352eea4bc5e40de7a59177", 56 | "zh:1e859504a656a6e988f07b908e6ffe946b28bfb56889417c0a07ea9605a3b7b0", 57 | "zh:64a6ae0320d4956c4fdb05629cfcebd03bcbd2206e2d733f2f18e4a97f4d5c7c", 58 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 59 | "zh:924d137959193bf7aee6ebf241fbb9aec46d6eef828c5cf8d3c588770acae7b2", 60 | "zh:b3cc76281a4faa9c2293a2460fc6962f6539e900994053f85185304887dddab8", 61 | "zh:cbb40c791d4a1cdba56cffa43a9c0ed8e69930d49aa6bd931546b18c36e3b720", 62 | "zh:d227d43594f8cb3d24f1fdd71382f14502cbe2a6deaddbc74242656bb5b38daf", 63 | "zh:d4840641c46176bb9d70ba3aff09de749282136c779996b546c81e5ff701bbf6", 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /live/terraform-backend/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /live/terraform-backend/configs/cross-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-cross-tfbackend-state" 3 | key = "nan-cross-tfbackend.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/terraform-backend/configs/cross.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "tfbackend" 5 | namespace = "nan" 6 | environment = "cross" 7 | tags = { 8 | } 9 | -------------------------------------------------------------------------------- /live/terraform-backend/configs/develop-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-develop-tfbackend-state" 3 | key = "nan-develop-tfbackend.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/terraform-backend/configs/develop.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "tfbackend" 5 | namespace = "nan" 6 | environment = "develop" 7 | tags = { 8 | } 9 | -------------------------------------------------------------------------------- /live/terraform-backend/configs/sandbox-backend.tfvars: -------------------------------------------------------------------------------- 1 | region = "us-west-2" 2 | bucket = "nan-sandbox-tfbackend-state" 3 | key = "nan-sandbox-tfbackend.tfstate" 4 | encrypt = "true" 5 | profile = "" 6 | -------------------------------------------------------------------------------- /live/terraform-backend/configs/sandbox.tfvars: -------------------------------------------------------------------------------- 1 | # General settings 2 | 3 | region = "us-west-2" 4 | name = "tfbackend" 5 | namespace = "nan" 6 | environment = "sandbox" 7 | tags = { 8 | } 9 | -------------------------------------------------------------------------------- /live/terraform-backend/context.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to use for servers, tags, etc" 3 | type = string 4 | default = "name" 5 | } 6 | 7 | variable "namespace" { 8 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" 9 | type = string 10 | default = "development" 11 | } 12 | 13 | variable "environment" { 14 | description = "Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT'" 15 | type = string 16 | default = "development" 17 | } 18 | 19 | variable "stage" { 20 | description = "Stage, e.g. 'build', 'test', 'deploy', 'release'" 21 | type = string 22 | # not required, so no default 23 | default = null 24 | } 25 | 26 | variable "tags" { 27 | description = "Any extra tags to assign to objects" 28 | type = map(any) 29 | default = {} 30 | } 31 | 32 | // Keep labels, tags consistent 33 | module "label" { 34 | source = "cloudposse/label/null" 35 | version = "0.25.0" 36 | 37 | name = var.name 38 | environment = var.environment 39 | namespace = var.namespace 40 | stage = var.stage 41 | 42 | delimiter = "-" 43 | label_order = ["namespace", "environment", "stage", "name", "attributes"] 44 | tags = var.tags 45 | } 46 | -------------------------------------------------------------------------------- /live/terraform-backend/docs/MODULE.md: -------------------------------------------------------------------------------- 1 | 2 | ## Requirements 3 | 4 | | Name | Version | 5 | |------|---------| 6 | | [terraform](#requirement\_terraform) | >= 1.0 | 7 | | [aws](#requirement\_aws) | >= 5.0.0 | 8 | 9 | ## Providers 10 | 11 | | Name | Version | 12 | |------|---------| 13 | | [local](#provider\_local) | 2.4.0 | 14 | 15 | ## Modules 16 | 17 | | Name | Source | Version | 18 | |------|--------|---------| 19 | | [label](#module\_label) | cloudposse/label/null | 0.25.0 | 20 | | [terraform\_state\_backend](#module\_terraform\_state\_backend) | cloudposse/tfstate-backend/aws | 1.4.1 | 21 | 22 | ## Resources 23 | 24 | | Name | Type | 25 | |------|------| 26 | | [local_file.terraform_backend_config](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | 27 | 28 | ## Inputs 29 | 30 | | Name | Description | Type | Default | Required | 31 | |------|-------------|------|---------|:--------:| 32 | | [environment](#input\_environment) | Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT' | `string` | `"development"` | no | 33 | | [name](#input\_name) | Name to use for servers, tags, etc | `string` | `"name"` | no | 34 | | [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `"development"` | no | 35 | | [region](#input\_region) | AWS region | `string` | `"us-west-2"` | no | 36 | | [stage](#input\_stage) | Stage, e.g. 'build', 'test', 'deploy', 'release' | `string` | `null` | no | 37 | | [tags](#input\_tags) | Any extra tags to assign to objects | `map(any)` | `{}` | no | 38 | 39 | ## Outputs 40 | 41 | No outputs. 42 | -------------------------------------------------------------------------------- /live/terraform-backend/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | 4 | default_tags { 5 | tags = merge(module.label.tags, { 6 | ManagedBy = "terraform" 7 | Owner = "NaNLABS" 8 | Project = "[Project Name]" 9 | Repository = "https://github.com/nanlabs/terraform-aws-starter" 10 | RepositoryPath = "live/terraform-backend" 11 | }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /live/terraform-backend/outputs.tf: -------------------------------------------------------------------------------- 1 | # common outputs 2 | -------------------------------------------------------------------------------- /live/terraform-backend/tf-backend.tf: -------------------------------------------------------------------------------- 1 | # You cannot create a new backend by simply defining this and then 2 | # immediately proceeding to "terraform apply". The S3 backend must 3 | # be bootstrapped according to the simple yet essential procedure in 4 | # https://github.com/cloudposse/terraform-aws-tfstate-backend#usage 5 | module "terraform_state_backend" { 6 | source = "cloudposse/tfstate-backend/aws" 7 | version = "1.4.1" 8 | 9 | name = var.name 10 | namespace = var.namespace 11 | environment = var.environment 12 | stage = var.stage 13 | attributes = ["state"] 14 | 15 | terraform_state_file = "${module.label.id}.tfstate" 16 | 17 | bucket_enabled = true 18 | dynamodb_enabled = false 19 | 20 | force_destroy = true 21 | } 22 | 23 | resource "local_file" "terraform_backend_config" { 24 | filename = "${path.module}/configs/${var.environment}-backend.tfvars" 25 | file_permission = "0644" 26 | content = < ./docs/MODULE.md` from the module directory. 37 | 38 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 39 | -------------------------------------------------------------------------------- /modules/__template__/docs/MODULE.md: -------------------------------------------------------------------------------- 1 | 2 | ## Requirements 3 | 4 | | Name | Version | 5 | |------|---------| 6 | | [terraform](#requirement\_terraform) | >= 1.0 | 7 | | [aws](#requirement\_aws) | >= 5.0.0 | 8 | 9 | ## Providers 10 | 11 | | Name | Version | 12 | |------|---------| 13 | | [aws](#provider\_aws) | >= 5.0.0 | 14 | 15 | ## Modules 16 | 17 | No modules. 18 | 19 | ## Resources 20 | 21 | | Name | Type | 22 | |------|------| 23 | | [aws_s3_bucket.bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | 24 | | [aws_s3_bucket_lifecycle_configuration.lifecycle](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | 25 | | [aws_s3_bucket_logging.bucket_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | 26 | | [aws_s3_bucket_ownership_controls.ownership](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource | 27 | | [aws_s3_bucket_public_access_block.public_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | 28 | | [aws_s3_bucket_server_side_encryption_configuration.sse](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | 29 | | [aws_s3_bucket_versioning.versioning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | 30 | 31 | ## Inputs 32 | 33 | | Name | Description | Type | Default | Required | 34 | |------|-------------|------|---------|:--------:| 35 | | [acl](#input\_acl) | Canned ACL to apply to the bucket | `string` | `"private"` | no | 36 | | [bucket\_name](#input\_bucket\_name) | The name of the S3 bucket | `string` | n/a | yes | 37 | | [enable\_lifecycle\_rule](#input\_enable\_lifecycle\_rule) | Enable lifecycle rule | `bool` | `true` | no | 38 | | [enable\_versioning](#input\_enable\_versioning) | Enable versioning on the S3 bucket | `bool` | `false` | no | 39 | | [force\_destroy](#input\_force\_destroy) | Force bucket deletion | `bool` | `false` | no | 40 | | [kms\_key\_id](#input\_kms\_key\_id) | KMS key for bucket encryption | `string` | `"alias/aws/s3"` | no | 41 | | [lifecycle\_expiration\_days](#input\_lifecycle\_expiration\_days) | Number of days after which to expire objects | `number` | `90` | no | 42 | | [lifecycle\_storage\_class](#input\_lifecycle\_storage\_class) | Storage class for lifecycle transition | `string` | `"GLACIER"` | no | 43 | | [lifecycle\_transition\_days](#input\_lifecycle\_transition\_days) | Number of days after which to transition objects | `number` | `30` | no | 44 | | [logging\_bucket](#input\_logging\_bucket) | Bucket for storing logs | `string` | n/a | yes | 45 | | [name](#input\_name) | Name to be used on all the resources as identifier | `string` | `""` | no | 46 | | [tags](#input\_tags) | Any extra tags to assign to objects | `map(any)` | `{}` | no | 47 | 48 | ## Outputs 49 | 50 | | Name | Description | 51 | |------|-------------| 52 | | [bucket\_arn](#output\_bucket\_arn) | The ARN of the S3 bucket | 53 | | [bucket\_id](#output\_bucket\_id) | The ID of the S3 bucket | 54 | | [logging\_bucket](#output\_logging\_bucket) | The logging bucket for the S3 bucket | 55 | -------------------------------------------------------------------------------- /modules/__template__/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "bucket" { 2 | bucket = "${var.name}-${var.bucket_name}" 3 | 4 | force_destroy = var.force_destroy 5 | 6 | tags = merge({ 7 | Name = "${var.name}-s3-bucket" 8 | }, var.tags) 9 | } 10 | 11 | resource "aws_s3_bucket_ownership_controls" "ownership" { 12 | bucket = aws_s3_bucket.bucket.id 13 | 14 | rule { 15 | object_ownership = "BucketOwnerEnforced" 16 | } 17 | } 18 | 19 | resource "aws_s3_bucket_public_access_block" "public_access" { 20 | bucket = aws_s3_bucket.bucket.id 21 | 22 | block_public_acls = true 23 | block_public_policy = true 24 | restrict_public_buckets = true 25 | ignore_public_acls = true 26 | } 27 | 28 | resource "aws_s3_bucket_versioning" "versioning" { 29 | bucket = aws_s3_bucket.bucket.id 30 | 31 | versioning_configuration { 32 | status = var.enable_versioning ? "Enabled" : "Suspended" 33 | } 34 | } 35 | 36 | resource "aws_s3_bucket_server_side_encryption_configuration" "sse" { 37 | bucket = aws_s3_bucket.bucket.id 38 | 39 | rule { 40 | apply_server_side_encryption_by_default { 41 | sse_algorithm = "aws:kms" 42 | kms_master_key_id = var.kms_key_id 43 | } 44 | } 45 | } 46 | 47 | resource "aws_s3_bucket_lifecycle_configuration" "lifecycle" { 48 | bucket = aws_s3_bucket.bucket.id 49 | 50 | rule { 51 | id = "default" 52 | status = var.enable_lifecycle_rule ? "Enabled" : "Disabled" 53 | 54 | transition { 55 | days = var.lifecycle_transition_days 56 | storage_class = var.lifecycle_storage_class 57 | } 58 | 59 | expiration { 60 | days = var.lifecycle_expiration_days 61 | } 62 | } 63 | } 64 | 65 | resource "aws_s3_bucket_logging" "bucket_logging" { 66 | bucket = aws_s3_bucket.bucket.id 67 | target_bucket = var.logging_bucket 68 | target_prefix = "${var.name}-${var.bucket_name}/logs/" 69 | } 70 | -------------------------------------------------------------------------------- /modules/__template__/outputs.tf: -------------------------------------------------------------------------------- 1 | output "bucket_id" { 2 | description = "The ID of the S3 bucket" 3 | value = aws_s3_bucket.bucket.id 4 | } 5 | 6 | output "bucket_arn" { 7 | description = "The ARN of the S3 bucket" 8 | value = aws_s3_bucket.bucket.arn 9 | } 10 | 11 | output "logging_bucket" { 12 | description = "The logging bucket for the S3 bucket" 13 | value = aws_s3_bucket_logging.bucket_logging.target_bucket 14 | } 15 | -------------------------------------------------------------------------------- /modules/__template__/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to be used on all the resources as identifier" 3 | type = string 4 | default = "" 5 | } 6 | 7 | variable "tags" { 8 | description = "Any extra tags to assign to objects" 9 | type = map(any) 10 | default = {} 11 | } 12 | 13 | variable "bucket_name" { 14 | description = "The name of the S3 bucket" 15 | type = string 16 | } 17 | 18 | variable "force_destroy" { 19 | description = "Force bucket deletion" 20 | type = bool 21 | default = false 22 | } 23 | 24 | variable "acl" { 25 | description = "Canned ACL to apply to the bucket" 26 | type = string 27 | default = "private" 28 | } 29 | 30 | variable "enable_versioning" { 31 | description = "Enable versioning on the S3 bucket" 32 | type = bool 33 | default = false 34 | } 35 | 36 | variable "kms_key_id" { 37 | description = "KMS key for bucket encryption" 38 | type = string 39 | default = "alias/aws/s3" 40 | } 41 | 42 | variable "enable_lifecycle_rule" { 43 | description = "Enable lifecycle rule" 44 | type = bool 45 | default = true 46 | } 47 | 48 | variable "lifecycle_transition_days" { 49 | description = "Number of days after which to transition objects" 50 | type = number 51 | default = 30 52 | } 53 | 54 | variable "lifecycle_storage_class" { 55 | description = "Storage class for lifecycle transition" 56 | type = string 57 | default = "GLACIER" 58 | } 59 | 60 | variable "lifecycle_expiration_days" { 61 | description = "Number of days after which to expire objects" 62 | type = number 63 | default = 90 64 | } 65 | 66 | variable "logging_bucket" { 67 | description = "Bucket for storing logs" 68 | type = string 69 | } 70 | -------------------------------------------------------------------------------- /modules/__template__/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/amplify-app/README.md: -------------------------------------------------------------------------------- 1 | # AWS Amplify App Module 2 | 3 | Terraform module to bootstrap an AWS Amplify App. 4 | 5 | ## Usage 6 | 7 | ```hcl 8 | module "app" { 9 | source = "../../modules/amplify-app" 10 | 11 | name = "example-amplify-app" 12 | 13 | # https://docs.aws.amazon.com/amplify/latest/userguide/getting-started.html 14 | # The GitHub PAT needs to have the scope `admin:repo_hook` 15 | # Refer to "Setting up the Amplify GitHub App for AWS CloudFormation, CLI, and SDK deployments" 16 | # in https://docs.aws.amazon.com/amplify/latest/userguide/setting-up-GitHub-access.html 17 | github_personal_access_token_secret_path = "/nanlabs/github-personal-access-token" 18 | 19 | platform = "WEB" 20 | 21 | repository = "https://github.com/nanlabs/react-boilerplate 22 | 23 | iam_service_role_enabled = true 24 | 25 | # https://docs.aws.amazon.com/amplify/latest/userguide/ssr-CloudWatch-logs.html 26 | iam_service_role_actions = [ 27 | "logs:CreateLogStream", 28 | "logs:CreateLogGroup", 29 | "logs:DescribeLogGroups", 30 | "logs:PutLogEvents" 31 | ] 32 | 33 | enable_auto_branch_creation = false 34 | 35 | enable_branch_auto_build = true 36 | 37 | enable_branch_auto_deletion = true 38 | 39 | enable_basic_auth = false 40 | 41 | auto_branch_creation_patterns = [ 42 | "*", 43 | "*/**" 44 | ] 45 | 46 | auto_branch_creation_config = { 47 | # Enable auto build for the created branches 48 | enable_auto_build = true 49 | } 50 | 51 | # The build spec for React 52 | build_spec = <<-EOT 53 | version: 1 54 | frontend: 55 | phases: 56 | preBuild: 57 | commands: 58 | - npm install 59 | build: 60 | commands: 61 | - npm run build 62 | artifacts: 63 | baseDirectory: dist 64 | files: 65 | - '**/*' 66 | cache: 67 | paths: 68 | - node_modules/**/* 69 | EOT 70 | 71 | custom_rules = [ 72 | { 73 | source = "/<*>" 74 | status = "404" 75 | target = "/index.html" 76 | } 77 | ] 78 | 79 | environment_variables = { 80 | ENV = "test" 81 | } 82 | 83 | environments = { 84 | main = { 85 | branch_name = "main" 86 | enable_auto_build = true 87 | backend_enabled = false 88 | enable_performance_mode = true 89 | enable_pull_request_preview = false 90 | framework = "React" 91 | stage = "PRODUCTION" 92 | } 93 | dev = { 94 | branch_name = "dev" 95 | enable_auto_build = true 96 | backend_enabled = false 97 | enable_performance_mode = false 98 | enable_pull_request_preview = true 99 | framework = "React" 100 | stage = "DEVELOPMENT" 101 | } 102 | } 103 | 104 | domains = { 105 | "test.net" = { 106 | enable_auto_sub_domain = true 107 | wait_for_verification = false 108 | sub_domain = [ 109 | { 110 | branch_name = "main" 111 | prefix = "" 112 | }, 113 | { 114 | branch_name = "dev" 115 | prefix = "dev" 116 | } 117 | ] 118 | } 119 | "test.io" = { 120 | enable_auto_sub_domain = true 121 | wait_for_verification = false 122 | sub_domain = [ 123 | { 124 | branch_name = "main" 125 | prefix = "" 126 | }, 127 | { 128 | branch_name = "dev" 129 | prefix = "dev" 130 | } 131 | ] 132 | } 133 | } 134 | } 135 | ``` 136 | 137 | ## Module Documentation 138 | 139 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running `terraform-docs md . > ./docs/MODULE.md` from the module directory. 140 | 141 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 142 | -------------------------------------------------------------------------------- /modules/amplify-app/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_ssm_parameter" "github_pat" { 2 | name = var.github_personal_access_token_secret_path 3 | with_decryption = true 4 | } 5 | 6 | module "amplify_app" { 7 | source = "cloudposse/amplify-app/aws" 8 | version = "1.1.0" 9 | 10 | name = var.name 11 | 12 | access_token = data.aws_ssm_parameter.github_pat.value 13 | description = var.description 14 | repository = var.repository 15 | platform = var.platform 16 | oauth_token = var.oauth_token 17 | auto_branch_creation_config = var.auto_branch_creation_config 18 | auto_branch_creation_patterns = var.auto_branch_creation_patterns 19 | basic_auth_credentials = var.basic_auth_credentials 20 | build_spec = var.build_spec 21 | enable_auto_branch_creation = var.enable_auto_branch_creation 22 | enable_basic_auth = var.enable_basic_auth 23 | enable_branch_auto_build = var.enable_branch_auto_build 24 | enable_branch_auto_deletion = var.enable_branch_auto_deletion 25 | environment_variables = var.environment_variables 26 | custom_rules = var.custom_rules 27 | iam_service_role_enabled = var.iam_service_role_enabled 28 | iam_service_role_arn = var.iam_service_role_arn 29 | iam_service_role_actions = var.iam_service_role_actions 30 | environments = var.environments 31 | domains = var.domains 32 | 33 | tags = var.tags 34 | } 35 | -------------------------------------------------------------------------------- /modules/amplify-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "name" { 2 | description = "Amplify App name" 3 | value = module.amplify_app.name 4 | } 5 | 6 | output "arn" { 7 | description = "Amplify App ARN" 8 | value = module.amplify_app.arn 9 | } 10 | 11 | output "default_domain" { 12 | description = "Amplify App domain (non-custom)" 13 | value = module.amplify_app.default_domain 14 | } 15 | 16 | output "backend_environments" { 17 | description = "Created backend environments" 18 | value = module.amplify_app.backend_environments 19 | } 20 | 21 | output "branch_names" { 22 | description = "The names of the created Amplify branches" 23 | value = module.amplify_app.branch_names 24 | } 25 | 26 | output "webhooks" { 27 | description = "Created webhooks" 28 | value = module.amplify_app.webhooks 29 | } 30 | 31 | output "domain_associations" { 32 | description = "Created domain associations" 33 | value = module.amplify_app.domain_associations 34 | } 35 | -------------------------------------------------------------------------------- /modules/amplify-app/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/bastion/ami.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "ubuntu" { 2 | most_recent = true 3 | 4 | filter { 5 | name = "name" 6 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 7 | } 8 | 9 | filter { 10 | name = "virtualization-type" 11 | values = ["hvm"] 12 | } 13 | 14 | owners = ["099720109477"] # Canonical 15 | } 16 | -------------------------------------------------------------------------------- /modules/bastion/docs/secure_bastion_host.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanlabs/terraform-aws-starter/b1b62ef4ebd3e72b02b1b4c6c083896d48f27c6b/modules/bastion/docs/secure_bastion_host.png -------------------------------------------------------------------------------- /modules/bastion/ec2.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | _ssh_key_name = ( 3 | length(var.key_name) > 0 ? var.key_name : aws_key_pair.ec2_ssh[0].key_name 4 | ) 5 | } 6 | 7 | // Script to configure the server - this is where most of the magic occurs! 8 | data "template_file" "user_data" { 9 | template = file("${path.module}/templates/cloud-init.yml.tpl") 10 | } 11 | 12 | // EC2 instance for the server - tune instance_type to fit your performance and budget requirements 13 | module "bastion" { 14 | source = "terraform-aws-modules/ec2-instance/aws" 15 | version = "~> 3.0" 16 | 17 | name = var.name 18 | 19 | # instance 20 | key_name = local._ssh_key_name 21 | ami = var.ami != "" ? var.ami : data.aws_ami.ubuntu.image_id 22 | instance_type = var.instance_type 23 | iam_instance_profile = aws_iam_instance_profile.bastion_instance_profile.id 24 | user_data = data.template_file.user_data.rendered 25 | 26 | # network 27 | subnet_id = element(var.private_subnets, 0) 28 | vpc_security_group_ids = [module.ec2_security_group.security_group_id] 29 | 30 | tags = var.tags 31 | 32 | root_block_device = [ 33 | { 34 | encrypted = true 35 | volume_type = var.root_volume_type 36 | volume_size = var.root_volume_size 37 | }, 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /modules/bastion/iam.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "bastion_host_iam_role" { 2 | name = "${var.name}-bastion-host-iam-role" 3 | managed_policy_arns = [ 4 | "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", 5 | "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" 6 | ] 7 | assume_role_policy = jsonencode({ 8 | "Version" : "2012-10-17", 9 | "Statement" : [ 10 | { 11 | "Action" : "sts:AssumeRole", 12 | "Principal" : { 13 | "Service" : "ec2.amazonaws.com" 14 | }, 15 | "Effect" : "Allow", 16 | "Sid" : "" 17 | } 18 | ] 19 | }) 20 | } 21 | 22 | resource "aws_iam_instance_profile" "bastion_instance_profile" { 23 | name = "${var.name}-bastion-instance-profile" 24 | role = aws_iam_role.bastion_host_iam_role.name 25 | } 26 | 27 | resource "aws_iam_role_policy" "bastion_host_iam_role" { 28 | name = "${var.name}-bastion-host-iam-role" 29 | role = aws_iam_role.bastion_host_iam_role.id 30 | policy = jsonencode({ 31 | "Version" : "2012-10-17", 32 | "Statement" : [ 33 | { 34 | "Effect" : "Allow", 35 | "Action" : ["s3:ListBucket"], 36 | "Resource" : ["arn:aws:s3:::*"] 37 | }, 38 | { 39 | "Effect" : "Allow", 40 | "Action" : [ 41 | "s3:PutObject", 42 | "s3:GetObject", 43 | "s3:DeleteObject" 44 | ], 45 | "Resource" : ["arn:aws:s3:::*/*"] 46 | }, 47 | { 48 | "Effect" : "Allow", 49 | "Action" : [ 50 | "cloudwatch:PutMetricData", 51 | "ec2:DescribeVolumes", 52 | "ec2:DescribeTags", 53 | "logs:PutLogEvents", 54 | "logs:DescribeLogStreams", 55 | "logs:DescribeLogGroups", 56 | "logs:CreateLogStream", 57 | "logs:CreateLogGroup" 58 | ], 59 | "Resource" : "*" 60 | }, 61 | { 62 | "Effect" : "Allow", 63 | "Action" : [ 64 | "ssm:GetParameter" 65 | ], 66 | "Resource" : "arn:aws:ssm:*:*:parameter/AmazonCloudWatch-*" 67 | }, 68 | { 69 | "Effect" : "Allow", 70 | "Action" : [ 71 | "secretsmanager:GetSecretValue" 72 | ], 73 | "Resource" : "arn:aws:secretsmanager:*:*:secret:*" 74 | }, 75 | { 76 | "Effect" : "Allow", 77 | "Action" : [ 78 | "eks:ListClusters", 79 | "eks:DescribeCluster", 80 | "eks:ListNodegroups", 81 | "eks:DescribeNodegroup", 82 | "eks:AccessKubernetesApi" 83 | ], 84 | "Resource" : "*" 85 | } 86 | ] 87 | }) 88 | } 89 | 90 | resource "aws_iam_policy" "ec2_instance_connect_policy" { 91 | name = "${var.name}-ec2-instance-connect-policy" 92 | policy = jsonencode({ 93 | "Version" : "2012-10-17", 94 | "Statement" : [ 95 | { 96 | "Effect" : "Allow", 97 | "Action" : "ec2-instance-connect:SendSSHPublicKey", 98 | "Resource" : "*" 99 | } 100 | ] 101 | }) 102 | } 103 | 104 | resource "aws_iam_role_policy_attachment" "bastion_host_instance_connect_policy_attachment" { 105 | role = aws_iam_role.bastion_host_iam_role.name 106 | policy_arn = aws_iam_policy.ec2_instance_connect_policy.arn 107 | } 108 | -------------------------------------------------------------------------------- /modules/bastion/outputs.tf: -------------------------------------------------------------------------------- 1 | output "instance_id" { 2 | value = module.bastion.id 3 | } 4 | 5 | output "instance_profile" { 6 | value = aws_iam_instance_profile.bastion_instance_profile.name 7 | } 8 | 9 | output "ssm_parameter_ssh_key" { 10 | value = try(aws_ssm_parameter.ssh_key[0].name, null) 11 | } 12 | 13 | output "security_group_id" { 14 | value = module.ec2_security_group.security_group_id 15 | } 16 | -------------------------------------------------------------------------------- /modules/bastion/scripts/easy-options/README.md: -------------------------------------------------------------------------------- 1 | # EasyOptions 2 | 3 | EasyOptions allows you to write the help text for your program _only once_, and have the described options _automatically parsed_ from command line into easily readable variables, _without complicated API_. EasyOptions was developed after discontentment with the existing solutions for option parsing in Bash. It was conceived with the following guidelines in mind: 4 | 5 | - Avoid duplication of source code documentation, help text and options specification. 6 | - Have the option values parsed into easily readable variables. 7 | - Have the non-option arguments available on a simple, separate array. 8 | - Usage as simple as one single line of code. 9 | 10 | EasyOptions is going to parse all of your options and arguments automatically once sourced. You specify what options are supported by your program by simply writing a help text, using special double-hash comments. This help text also works at the same time as source code documentation and options specification. All client scripts have an automatic `--help` option, which is going to display such documentation. You can see more details, specially about the options specification, in the help text of EasyOptions itself. 11 | 12 | ## Usage 13 | 14 | For using EasyOptions in your script, simply document it using double-hash comments like this: 15 | 16 | ```bash 17 | ## Program Name v1.0 18 | ## Copyright (C) Someone 19 | ## Licensed under XYZ 20 | ## 21 | ## This program does something with the arguments. Usage: 22 | ## @script.name [option] ARGUMENTS... 23 | ## 24 | ## Options: 25 | ## -h, --help All client scripts have this, it can be omitted. 26 | ## -o, --some-option This is a boolean option. Long version is 27 | ## mandatory, and can be specified before or 28 | ## after short version. 29 | ## --some-boolean This is a boolean option without a short version. 30 | ## --some-value=VALUE This is a parameter option. When calling your script 31 | ## the equal sign is optional and blank space can be 32 | ## used instead. Short version is not available in this 33 | ## format. 34 | ``` 35 | 36 | The above comments work both as source code documentation and as help text, as well as define the options supported by your script. There is no duplication of the options specification. The string `@script.name` will be replaced with the actual script name. Now you only need to call EasyOptions in your script and _that's it_! 37 | 38 | ### Using Arguments 39 | 40 | After writing your documentation, you simply source this script. Then all command line options will get parsed into the corresponding variables. You can then check their values for reacting to them. Regular arguments will be available in the `$arguments` array. You can source `easyoptions.sh` for a pure Bash implementation. Here is an example for parsing the comments above: 41 | 42 | ```bash 43 | ROOT=$(dirname "$0") 44 | source "${ROOT}/easyoptions.sh" || exit # Bash implementation, slower 45 | 46 | # Boolean and parameter options 47 | [[ -n "$some_option" ]] && echo "Option specified: --some-option" 48 | [[ -n "$some_boolean" ]] && echo "Option specified: --some-boolean" 49 | [[ -n "$some_value" ]] && echo "Option specified: --some-value is $some_value" 50 | 51 | # Arguments 52 | for argument in "${arguments[@]}"; do 53 | echo "Argument specified: $argument" 54 | done 55 | ``` 56 | 57 | If using the pure Bash implementation, then for better speed you may want to define the options in source code yourself, so they do not need to be parsed from the documentation. The side effect is that when changing them, you will need to update both the documentation and the source code. You define the options statically like this: 58 | 59 | ```bash 60 | options=(o=option some-boolean some-value=?) 61 | ``` 62 | -------------------------------------------------------------------------------- /modules/bastion/scripts/easy-options/example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## EasyOptions Example 4 | ## Copyright (C) Someone 5 | ## Licensed under XYZ 6 | ## 7 | ## This program is an example of EasyOptions. It just prints the options and 8 | ## arguments provided in command line. Usage: 9 | ## 10 | ## @script.name [option] ARGUMENTS... 11 | ## 12 | ## Options: 13 | ## -h, --help All client scripts have this, it can be omitted. 14 | ## -o, --some-option This is a boolean option. Long version is 15 | ## mandatory, and can be specified before or 16 | ## after short version. 17 | ## --some-boolean This is a boolean option without a short version. 18 | ## --some-value=VALUE This is a parameter option. When calling your script 19 | ## the equal sign is optional and blank space can be 20 | ## used instead. Short version is not available in this 21 | ## format. 22 | 23 | ROOT=$(dirname "$0") 24 | source "${ROOT}/easyoptions.sh" || exit # Bash implementation, slower 25 | 26 | # Boolean and parameter options 27 | [[ -n "$some_option" ]] && echo "Option specified: --some-option" 28 | [[ -n "$some_boolean" ]] && echo "Option specified: --some-boolean" 29 | [[ -n "$some_value" ]] && echo "Option specified: --some-value is $some_value" 30 | 31 | # Arguments 32 | for argument in "${arguments[@]}"; do 33 | echo "Argument specified: $argument" 34 | done 35 | -------------------------------------------------------------------------------- /modules/bastion/sg.tf: -------------------------------------------------------------------------------- 1 | module "ec2_security_group" { 2 | source = "terraform-aws-modules/security-group/aws" 3 | version = "4.17.1" 4 | 5 | name = "${var.name}-ec2" 6 | description = "Allow traffic on all ports and ip ranges" 7 | vpc_id = var.vpc_id 8 | 9 | egress_rules = ["all-all"] 10 | 11 | tags = var.tags 12 | } 13 | 14 | module "ssm_vpce_sg" { 15 | source = "terraform-aws-modules/security-group/aws" 16 | version = "4.17.1" 17 | 18 | count = var.create_vpc_endpoints ? 1 : 0 19 | 20 | name = "${var.name}-vpc-ssm-vpce-security-group" 21 | description = "Security group for SSM VPC endpoint" 22 | vpc_id = var.vpc_id 23 | 24 | ingress_with_source_security_group_id = concat([ 25 | { 26 | rule = "https-443-tcp" 27 | source_security_group_id = module.ec2_security_group.security_group_id 28 | description = "vpc ssm vpce security group ingress rule for bastion host" 29 | } 30 | ]) 31 | 32 | tags = var.tags 33 | } 34 | 35 | module "ec2messages_vpce_sg" { 36 | source = "terraform-aws-modules/security-group/aws" 37 | version = "4.17.1" 38 | 39 | count = var.create_vpc_endpoints ? 1 : 0 40 | 41 | name = "${var.name}-vpc-ec2messages-vpce-security-group" 42 | description = "Security group for EC2 Messages VPC endpoint" 43 | vpc_id = var.vpc_id 44 | 45 | ingress_with_source_security_group_id = [ 46 | { 47 | rule = "https-443-tcp" 48 | source_security_group_id = module.ec2_security_group.security_group_id 49 | description = "vpc ec2 messages vpce security group ingress rule for bastion host" 50 | } 51 | ] 52 | 53 | tags = var.tags 54 | } 55 | 56 | module "ssmmessages_vpce_sg" { 57 | source = "terraform-aws-modules/security-group/aws" 58 | version = "4.17.1" 59 | 60 | count = var.create_vpc_endpoints ? 1 : 0 61 | 62 | name = "${var.name}-vpc-ssmmessages-vpce-security-group" 63 | description = "Security group for SSM Messages VPC endpoint" 64 | vpc_id = var.vpc_id 65 | 66 | ingress_with_source_security_group_id = [ 67 | { 68 | rule = "https-443-tcp" 69 | source_security_group_id = module.ec2_security_group.security_group_id 70 | description = "vpc ssm messages vpce security group ingress rule for bastion host" 71 | } 72 | ] 73 | 74 | tags = var.tags 75 | } 76 | -------------------------------------------------------------------------------- /modules/bastion/ssh-key.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | disable_key_creation = length(var.key_name) > 0 3 | } 4 | 5 | // Create EC2 ssh key pair 6 | resource "tls_private_key" "ec2_ssh" { 7 | count = local.disable_key_creation ? 0 : 1 8 | 9 | algorithm = "RSA" 10 | rsa_bits = 4096 11 | } 12 | 13 | resource "aws_key_pair" "ec2_ssh" { 14 | count = local.disable_key_creation ? 0 : 1 15 | 16 | key_name = "${var.name}-ec2-ssh-key" 17 | public_key = tls_private_key.ec2_ssh[0].public_key_openssh 18 | } 19 | -------------------------------------------------------------------------------- /modules/bastion/ssm.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ssm_parameter" "ssh_key" { 2 | count = local.disable_key_creation ? 0 : 1 3 | 4 | name = "/${var.name}/bastion_ssh" 5 | type = "SecureString" 6 | value = tls_private_key.ec2_ssh[0].private_key_pem 7 | } 8 | -------------------------------------------------------------------------------- /modules/bastion/templates/cloud-init.yml.tpl: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | 3 | apt: 4 | sources: 5 | docker.list: 6 | source: deb [arch=amd64] https://download.docker.com/linux/ubuntu $RELEASE stable 7 | keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 8 | 9 | package_update: true 10 | package_upgrade: true 11 | 12 | packages: 13 | - apt-transport-https 14 | - ca-certificates 15 | - curl 16 | - gnupg-agent 17 | - software-properties-common 18 | - docker-ce 19 | - docker-ce-cli 20 | - containerd.io 21 | - unzip 22 | 23 | # Enable ipv4 forwarding, required on CIS hardened machines 24 | write_files: 25 | - path: /etc/sysctl.d/enabled_ipv4_forwarding.conf 26 | content: | 27 | net.ipv4.conf.all.forwarding=1 28 | - path: /etc/ssh/sshd_config 29 | append: true 30 | content: | 31 | ClientAliveInterval 600 32 | ClientAliveCountMax 0 33 | 34 | # create the docker group 35 | groups: 36 | - docker 37 | 38 | # Add default auto created user to docker group 39 | system_info: 40 | default_user: 41 | groups: [docker] 42 | 43 | runcmd: 44 | - sudo apt-get remove -y awscli 45 | - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.17.14.zip" -o "awscliv2.zip" 46 | - unzip awscliv2.zip 47 | - sudo ./aws/install 48 | - rm -rf awscliv2.zip aws 49 | - curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" 50 | - sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl 51 | - rm kubectl 52 | -------------------------------------------------------------------------------- /modules/bastion/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_id" { 2 | description = "VPC id in which the EC2 instance is to be created." 3 | type = string 4 | } 5 | 6 | variable "private_subnets" { 7 | description = "List of private subnets in which the EC2 instance is to be created." 8 | type = list(string) 9 | } 10 | 11 | variable "ami" { 12 | description = "AMI to use for the instance - will default to latest Ubuntu" 13 | type = string 14 | default = "" 15 | } 16 | 17 | // https://aws.amazon.com/ec2/instance-types/ 18 | variable "instance_type" { 19 | description = "EC2 instance type/size - the default is not part of free tier!" 20 | type = string 21 | default = "t3.nano" 22 | } 23 | 24 | variable "root_volume_size" { 25 | description = "Size of the root volume in GB" 26 | type = number 27 | default = 8 28 | } 29 | 30 | variable "root_volume_type" { 31 | description = "Type of the root volume" 32 | type = string 33 | default = "gp2" 34 | } 35 | 36 | variable "allowed_cidrs" { 37 | description = "Allow these CIDR blocks to instance" 38 | type = string 39 | default = null 40 | } 41 | 42 | variable "key_name" { 43 | description = "SSH key name to use for the instance" 44 | type = string 45 | default = "" 46 | } 47 | 48 | variable "name" { 49 | description = "Name to be used on all the resources as identifier" 50 | type = string 51 | default = "" 52 | } 53 | 54 | variable "create_vpc_endpoints" { 55 | description = "Create VPC endpoints for SSM, EC2 Messages, and SSM Messages" 56 | type = bool 57 | default = true 58 | } 59 | 60 | variable "vpc_endpoint_security_group_ids" { 61 | description = "List of security group IDs to attach to the VPC endpoints. Will be ignored if create_vpc_endpoints is false." 62 | type = list(string) 63 | default = [] 64 | } 65 | 66 | variable "tags" { 67 | description = "Any extra tags to assign to objects" 68 | type = map(any) 69 | default = {} 70 | } 71 | -------------------------------------------------------------------------------- /modules/bastion/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/bastion/vpc-endpoints.tf: -------------------------------------------------------------------------------- 1 | module "vpc_endpoints" { 2 | source = "../../modules/vpc-endpoints" 3 | 4 | vpc_id = var.vpc_id 5 | 6 | count = var.create_vpc_endpoints ? 1 : 0 7 | 8 | endpoints = { 9 | ssm = { 10 | service = "ssm" 11 | service_type = "Interface" 12 | security_group_ids = [module.ssm_vpce_sg[0].security_group_id] 13 | private_dns_enabled = true 14 | subnet_ids = var.private_subnets 15 | policy = null 16 | tags = { Name = "${var.name}-ssm-vpc-endpoint" } 17 | }, 18 | ec2messages = { 19 | service = "ec2messages" 20 | service_type = "Interface" 21 | security_group_ids = [module.ec2messages_vpce_sg[0].security_group_id] 22 | private_dns_enabled = true 23 | subnet_ids = var.private_subnets 24 | policy = null 25 | tags = { Name = "${var.name}-ec2messages-vpc-endpoint" } 26 | }, 27 | ssmmessages = { 28 | service = "ssmmessages" 29 | service_type = "Interface" 30 | security_group_ids = [module.ssmmessages_vpce_sg[0].security_group_id] 31 | private_dns_enabled = true 32 | subnet_ids = var.private_subnets 33 | policy = null 34 | tags = { Name = "${var.name}-ssmmessages-vpc-endpoint" } 35 | } 36 | } 37 | 38 | tags = var.tags 39 | } 40 | -------------------------------------------------------------------------------- /modules/docdb/README.md: -------------------------------------------------------------------------------- 1 | # DocumentDB Module 2 | 3 | Terrform module to bootstrap a DocumentDB cluster. 4 | 5 | ## Usage 6 | 7 | ```hcl 8 | module "db" { 9 | source = "../../modules/docdb" 10 | 11 | name = "examples-docdb-cluster" 12 | 13 | vpc_id = module.vpc.vpc_id 14 | subnet_group = module.vpc.database_subnet_group 15 | 16 | db_name = "db_name" 17 | db_master_username = "db_master_username" 18 | 19 | instance_class = "db.t3.medium" 20 | cluster_size = 2 21 | } 22 | 23 | ``` 24 | 25 | ## Module Documentation 26 | 27 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running `terraform-docs md . > ./docs/MODULE.md` from the module directory. 28 | 29 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 30 | -------------------------------------------------------------------------------- /modules/docdb/docdb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_docdb_cluster" "this" { 2 | cluster_identifier = var.db_name 3 | master_username = local.username 4 | master_password = local.password 5 | backup_retention_period = var.retention_period 6 | preferred_backup_window = var.preferred_backup_window 7 | final_snapshot_identifier = lower(var.db_name) 8 | skip_final_snapshot = var.skip_final_snapshot 9 | apply_immediately = var.apply_immediately 10 | storage_encrypted = var.storage_encrypted 11 | kms_key_id = var.kms_key_id 12 | snapshot_identifier = var.snapshot_identifier 13 | vpc_security_group_ids = [module.security_group.security_group_id] 14 | db_subnet_group_name = var.subnet_group 15 | db_cluster_parameter_group_name = aws_docdb_cluster_parameter_group.this.name 16 | engine = var.engine 17 | engine_version = var.engine_version 18 | enabled_cloudwatch_logs_exports = var.enabled_cloudwatch_logs_exports 19 | tags = var.tags 20 | } 21 | 22 | resource "aws_docdb_cluster_instance" "this" { 23 | count = var.cluster_size 24 | identifier = "${var.name}-${var.db_name}-${count.index + 1}" 25 | cluster_identifier = join("", aws_docdb_cluster.this.*.id) 26 | apply_immediately = var.apply_immediately 27 | instance_class = var.instance_class 28 | tags = var.tags 29 | engine = var.engine 30 | } 31 | 32 | # https://docs.aws.amazon.com/documentdb/latest/developerguide/db-cluster-parameter-group-create.html 33 | resource "aws_docdb_cluster_parameter_group" "this" { 34 | name = "${var.name}-${var.db_name}-parameter-group" 35 | description = "DB cluster parameter group." 36 | family = var.cluster_family 37 | parameter { 38 | name = "tls" 39 | value = var.tls_enabled ? "enabled" : "disabled" 40 | } 41 | tags = var.tags 42 | } 43 | -------------------------------------------------------------------------------- /modules/docdb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "master_username" { 2 | value = local.username 3 | description = "Username for the master DB user." 4 | } 5 | 6 | output "master_password" { 7 | value = local.password 8 | description = "password for the master DB user." 9 | sensitive = true 10 | } 11 | 12 | output "cluster_name" { 13 | value = aws_docdb_cluster.this.*.cluster_identifier 14 | description = "Cluster Identifier." 15 | } 16 | 17 | output "arn" { 18 | value = aws_docdb_cluster.this.*.arn 19 | description = "Amazon Resource Name (ARN) of the cluster." 20 | } 21 | 22 | output "writer_endpoint" { 23 | value = aws_docdb_cluster.this.*.endpoint 24 | description = "Endpoint of the DocumentDB cluster." 25 | } 26 | 27 | output "reader_endpoint" { 28 | value = aws_docdb_cluster.this.*.reader_endpoint 29 | description = "A read-only endpoint of the DocumentDB cluster, automatically load-balanced across replicas." 30 | } 31 | 32 | output "connection_secret_name" { 33 | description = "The name of the AWS Secrets Manager secret created" 34 | value = aws_secretsmanager_secret.secret.name 35 | } 36 | 37 | output "connection_secret_arn" { 38 | description = "The ARN of the AWS Secrets Manager secret created" 39 | value = aws_secretsmanager_secret.secret.arn 40 | } 41 | -------------------------------------------------------------------------------- /modules/docdb/secret.tf: -------------------------------------------------------------------------------- 1 | # Create a random initial password for the RDS Postgres 2 | resource "random_password" "rds_password" { 3 | length = 16 4 | special = true 5 | override_special = "_%@" 6 | } 7 | 8 | locals { 9 | # if var.master_password is not set, use the random password 10 | password = var.master_password != "" ? var.master_password : random_password.rds_password.result 11 | username = var.master_username 12 | } 13 | 14 | resource "aws_secretsmanager_secret" "secret" { 15 | description = "DocumentDB Credentials of ${var.db_name} service" 16 | name = "${var.name}/docdb-connection-secret" 17 | } 18 | 19 | resource "aws_secretsmanager_secret_version" "secret" { 20 | lifecycle { 21 | ignore_changes = [ 22 | secret_string 23 | ] 24 | } 25 | secret_id = aws_secretsmanager_secret.secret.id 26 | secret_string = < ./docs/MODULE.md` from the module directory. 65 | 66 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 67 | -------------------------------------------------------------------------------- /modules/iam-role/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "assume_role" { 2 | count = length(keys(var.principals)) 3 | 4 | statement { 5 | effect = "Allow" 6 | actions = var.assume_role_actions 7 | 8 | principals { 9 | type = element(keys(var.principals), count.index) 10 | identifiers = var.principals[element(keys(var.principals), count.index)] 11 | } 12 | 13 | dynamic "condition" { 14 | for_each = var.assume_role_conditions 15 | content { 16 | test = condition.value.test 17 | variable = condition.value.variable 18 | values = condition.value.values 19 | } 20 | } 21 | } 22 | } 23 | 24 | data "aws_iam_policy_document" "assume_role_aggregated" { 25 | override_policy_documents = data.aws_iam_policy_document.assume_role[*].json 26 | } 27 | 28 | resource "aws_iam_role" "default" { 29 | name = var.name 30 | assume_role_policy = join("", data.aws_iam_policy_document.assume_role_aggregated[*].json) 31 | description = var.role_description 32 | max_session_duration = var.max_session_duration 33 | permissions_boundary = var.permissions_boundary 34 | path = var.path 35 | tags = merge(var.tags, { 36 | "Name" = var.name 37 | }) 38 | } 39 | 40 | data "aws_iam_policy_document" "default" { 41 | count = var.policy_document_count > 0 ? 1 : 0 42 | override_policy_documents = var.policy_documents 43 | } 44 | 45 | resource "aws_iam_policy" "default" { 46 | count = var.policy_document_count > 0 ? 1 : 0 47 | name = var.policy_name != "" && var.policy_name != null ? var.policy_name : var.name 48 | description = var.policy_description 49 | policy = join("", data.aws_iam_policy_document.default.*.json) 50 | path = var.path 51 | tags = merge(var.tags, { 52 | "Name" = var.policy_name != "" && var.policy_name != null ? var.policy_name : var.name 53 | }) 54 | } 55 | 56 | resource "aws_iam_role_policy_attachment" "default" { 57 | count = var.policy_document_count > 0 ? 1 : 0 58 | role = join("", aws_iam_role.default.*.name) 59 | policy_arn = join("", aws_iam_policy.default.*.arn) 60 | } 61 | 62 | resource "aws_iam_role_policy_attachment" "managed" { 63 | for_each = var.managed_policy_arns 64 | role = join("", aws_iam_role.default.*.name) 65 | policy_arn = each.key 66 | } 67 | 68 | resource "aws_iam_instance_profile" "default" { 69 | count = var.instance_profile_enabled ? 1 : 0 70 | name = var.name 71 | role = join("", aws_iam_role.default.*.name) 72 | } 73 | -------------------------------------------------------------------------------- /modules/iam-role/outputs.tf: -------------------------------------------------------------------------------- 1 | output "name" { 2 | value = join("", aws_iam_role.default.*.name) 3 | description = "The name of the IAM role created" 4 | } 5 | 6 | output "id" { 7 | value = join("", aws_iam_role.default.*.unique_id) 8 | description = "The stable and unique string identifying the role" 9 | } 10 | 11 | output "arn" { 12 | value = join("", aws_iam_role.default.*.arn) 13 | description = "The Amazon Resource Name (ARN) specifying the role" 14 | } 15 | 16 | output "policy" { 17 | value = join("", data.aws_iam_policy_document.default.*.json) 18 | description = "Role policy document in json format. Outputs always, independent of `enabled` variable" 19 | } 20 | 21 | output "instance_profile" { 22 | description = "Name of the ec2 profile (if enabled)" 23 | value = join("", aws_iam_instance_profile.default.*.name) 24 | } 25 | -------------------------------------------------------------------------------- /modules/iam-role/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | type = string 3 | description = <<-EOT 4 | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. 5 | This is the only ID element not also included as a `tag`. 6 | The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. 7 | EOT 8 | } 9 | 10 | variable "principals" { 11 | type = map(list(string)) 12 | description = "Map of service name as key and a list of ARNs to allow assuming the role as value (e.g. map(`AWS`, list(`arn:aws:iam:::role/admin`)))" 13 | default = {} 14 | } 15 | 16 | variable "policy_documents" { 17 | type = list(string) 18 | description = "List of JSON IAM policy documents" 19 | default = [] 20 | } 21 | 22 | variable "policy_document_count" { 23 | type = number 24 | description = "Number of policy documents (length of policy_documents list)" 25 | default = 1 26 | } 27 | 28 | variable "managed_policy_arns" { 29 | type = set(string) 30 | description = "List of managed policies to attach to created role" 31 | default = [] 32 | } 33 | 34 | variable "max_session_duration" { 35 | type = number 36 | default = 3600 37 | description = "The maximum session duration (in seconds) for the role. Can have a value from 1 hour to 12 hours" 38 | } 39 | 40 | variable "permissions_boundary" { 41 | type = string 42 | default = "" 43 | description = "ARN of the policy that is used to set the permissions boundary for the role" 44 | } 45 | 46 | variable "role_description" { 47 | type = string 48 | description = "The description of the IAM role that is visible in the IAM role manager" 49 | } 50 | 51 | variable "policy_name" { 52 | type = string 53 | description = "The name of the IAM policy that is visible in the IAM policy manager" 54 | default = null 55 | } 56 | 57 | variable "policy_description" { 58 | type = string 59 | default = "" 60 | description = "The description of the IAM policy that is visible in the IAM policy manager" 61 | } 62 | 63 | variable "assume_role_actions" { 64 | type = list(string) 65 | default = ["sts:AssumeRole", "sts:TagSession"] 66 | description = "The IAM action to be granted by the AssumeRole policy" 67 | } 68 | 69 | variable "assume_role_conditions" { 70 | type = list(object({ 71 | test = string 72 | variable = string 73 | values = list(string) 74 | })) 75 | description = "List of conditions for the assume role policy" 76 | default = [] 77 | } 78 | 79 | variable "instance_profile_enabled" { 80 | type = bool 81 | default = false 82 | description = "Create EC2 Instance Profile for the role" 83 | } 84 | 85 | variable "path" { 86 | type = string 87 | description = "Path to the role and policy. See [IAM Identifiers](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html) for more information." 88 | default = "/" 89 | } 90 | 91 | variable "tags" { 92 | type = map(string) 93 | default = {} 94 | description = <<-EOT 95 | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). 96 | Neither the tag keys nor the tag values will be modified by this module. 97 | EOT 98 | } 99 | -------------------------------------------------------------------------------- /modules/iam-role/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/mongodb/README.md: -------------------------------------------------------------------------------- 1 | # MongoDB Atlas Module 2 | 3 | This module creates a MongoDB Atlas cluster and configures a VPC peering connection between the Atlas cluster and the specified VPC. 4 | 5 | ## Usage 6 | 7 | ```hcl 8 | provider "aws" { 9 | region = "eu-west-1" 10 | } 11 | 12 | provider "mongodbatlas" {} 13 | 14 | 15 | module "atlas_cluster" { 16 | source = "../../modules/mongodb" 17 | 18 | project_name = "my-project" 19 | org_id = "5edf67f9b9614996228111" 20 | 21 | teams = { 22 | Devops : { 23 | users : ["example@mail.io", "user@mail.io"] 24 | role : "GROUP_OWNER" 25 | }, 26 | DevTeam : { 27 | users : ["developer@mail.io"] 28 | role : "GROUP_READ_ONLY" 29 | } 30 | } 31 | 32 | access_lists = { 33 | "example comment" : "52.12.41.46/32", 34 | "second example" : "54.215.4.201/32" 35 | } 36 | 37 | region = "EU_WEST_1" 38 | 39 | cluster_name = "MyCluster" 40 | 41 | instance_type = "M30" 42 | mongodb_major_ver = 4.2 43 | cluster_type = "REPLICASET" 44 | num_shards = 1 45 | replication_factor = 3 46 | provider_backup = true 47 | pit_enabled = false 48 | 49 | vpc_peer = { 50 | vpc_peer1 : { 51 | aws_account_id : "020201234877" 52 | region : "eu-west-1" 53 | vpc_id : "vpc-0240c8a47312svc3e" 54 | route_table_cidr_block : "172.16.0.0/16" 55 | }, 56 | vpc_peer2 : { 57 | aws_account_id : "0205432147877" 58 | region : "eu-central-1" 59 | vpc_id : "vpc-0f0dd82430bhv0e1a" 60 | route_table_cidr_block : "172.17.0.0/16" 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | ## Module Documentation 67 | 68 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running `terraform-docs md . > ./docs/MODULE.md` from the module directory. 69 | 70 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 71 | -------------------------------------------------------------------------------- /modules/mongodb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_id" { 2 | value = mongodbatlas_cluster.cluster.cluster_id 3 | description = "The cluster ID" 4 | } 5 | 6 | output "mongo_db_version" { 7 | value = mongodbatlas_cluster.cluster.mongo_db_version 8 | description = "Version of MongoDB the cluster runs, in major-version.minor-version format" 9 | } 10 | 11 | output "mongo_uri" { 12 | value = mongodbatlas_cluster.cluster.mongo_uri 13 | description = "Base connection string for the cluster" 14 | } 15 | 16 | output "mongo_uri_updated" { 17 | value = mongodbatlas_cluster.cluster.mongo_uri_updated 18 | description = "Lists when the connection string was last updated" 19 | } 20 | 21 | output "mongo_uri_with_options" { 22 | value = mongodbatlas_cluster.cluster.mongo_uri_with_options 23 | description = "connection string for connecting to the Atlas cluster. Includes the replicaSet, ssl, and authSource query parameters in the connection string with values appropriate for the cluster" 24 | } 25 | 26 | output "connection_strings" { 27 | value = mongodbatlas_cluster.cluster.connection_strings 28 | description = "Set of connection strings that your applications use to connect to this cluster" 29 | } 30 | 31 | output "container_id" { 32 | value = mongodbatlas_cluster.cluster.container_id 33 | description = "The Network Peering Container ID" 34 | } 35 | 36 | output "paused" { 37 | value = mongodbatlas_cluster.cluster.paused 38 | description = "Flag that indicates whether the cluster is paused or not" 39 | } 40 | 41 | output "srv_address" { 42 | value = mongodbatlas_cluster.cluster.srv_address 43 | description = "Connection string for connecting to the Atlas cluster. The +srv modifier forces the connection to use TLS/SSL" 44 | } 45 | 46 | output "state_name" { 47 | value = mongodbatlas_cluster.cluster.state_name 48 | description = "Current state of the cluster" 49 | } 50 | -------------------------------------------------------------------------------- /modules/mongodb/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | mongodbatlas = { 4 | source = "mongodb/mongodbatlas" 5 | version = "1.12.1" 6 | } 7 | } 8 | 9 | required_version = ">= 0.12" 10 | } 11 | -------------------------------------------------------------------------------- /modules/msk/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Module: Amazon MSK Cluster 2 | 3 | This Terraform module is used to create an Amazon Managed Streaming for Apache Kafka (MSK) cluster. 4 | 5 | ## Prerequisites 6 | 7 | Before using this module, make sure you have the following prerequisites: 8 | 9 | - An AWS account 10 | - Terraform installed on your local machine. Consider using `tfswitch` 11 | 12 | ## Usage 13 | 14 | To use this module, include the following code in your Terraform configuration: 15 | 16 | ```hcl 17 | module "msk_cluster" { 18 | source = "path/to/module" 19 | cluster_name = "my-msk-cluster" 20 | kafka_version = "2.8.0" 21 | instance_type = "kafka.m5.large" 22 | num_broker_nodes = 3 23 | subnet_ids = ["subnet-12345678", "subnet-87654321"] 24 | security_group_ids = ["sg-12345678", "sg-87654321"] 25 | } 26 | ``` 27 | 28 | Replace `path/to/module` with the actual path to this module. 29 | 30 | ## Module Documentation 31 | 32 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running `terraform-docs md . > ./docs/MODULE.md` from the module directory. 33 | 34 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 35 | -------------------------------------------------------------------------------- /modules/msk/main.tf: -------------------------------------------------------------------------------- 1 | module "kafka" { 2 | source = "cloudposse/msk-apache-kafka-cluster/aws" 3 | version = "2.4.0" 4 | name = var.name 5 | 6 | zone_id = var.zone_id 7 | vpc_id = var.vpc_id 8 | subnet_ids = var.private_subnets 9 | kafka_version = var.kafka_version 10 | broker_per_zone = var.broker_per_zone 11 | broker_instance_type = var.broker_instance_type 12 | broker_volume_size = var.broker_volume_size 13 | public_access_enabled = var.public_access_enabled 14 | broker_dns_records_count = var.broker_dns_records_count 15 | 16 | allowed_security_group_ids = var.allowed_security_group_ids 17 | allowed_cidr_blocks = var.allowed_cidr_blocks 18 | associated_security_group_ids = var.associated_security_group_ids 19 | create_security_group = var.create_security_group 20 | security_group_name = var.security_group_name 21 | security_group_description = var.security_group_description 22 | security_group_create_before_destroy = var.security_group_create_before_destroy 23 | preserve_security_group_id = var.preserve_security_group_id 24 | security_group_create_timeout = var.security_group_create_timeout 25 | security_group_delete_timeout = var.security_group_delete_timeout 26 | allow_all_egress = var.allow_all_egress 27 | additional_security_group_rules = var.additional_security_group_rules 28 | inline_rules_enabled = var.inline_rules_enabled 29 | 30 | client_allow_unauthenticated = var.client_allow_unauthenticated 31 | client_sasl_scram_enabled = var.client_sasl_scram_enabled 32 | client_sasl_iam_enabled = var.client_sasl_iam_enabled 33 | client_tls_auth_enabled = var.client_tls_auth_enabled 34 | client_sasl_scram_secret_association_enabled = var.client_sasl_scram_secret_association_enabled 35 | client_sasl_scram_secret_association_arns = var.client_sasl_scram_secret_association_arns 36 | 37 | certificate_authority_arns = var.certificate_authority_arns 38 | 39 | 40 | cloudwatch_logs_enabled = var.cloudwatch_logs_enabled 41 | cloudwatch_logs_log_group = var.cloudwatch_logs_log_group 42 | 43 | s3_logs_enabled = var.s3_logs_enabled 44 | s3_logs_bucket = var.s3_logs_bucket 45 | s3_logs_prefix = var.s3_logs_prefix 46 | 47 | tags = var.tags 48 | } 49 | -------------------------------------------------------------------------------- /modules/msk/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_name" { 2 | value = module.kafka.cluster_name 3 | description = "The cluster name of the MSK cluster" 4 | } 5 | 6 | output "cluster_arn" { 7 | value = module.kafka.cluster_arn 8 | description = "Amazon Resource Name (ARN) of the MSK cluster" 9 | } 10 | 11 | output "config_arn" { 12 | value = module.kafka.config_arn 13 | description = "Amazon Resource Name (ARN) of the MSK configuration" 14 | } 15 | 16 | output "hostnames" { 17 | value = module.kafka.hostnames 18 | description = "List of MSK Cluster broker DNS hostnames" 19 | } 20 | 21 | output "security_group_id" { 22 | value = module.kafka.security_group_id 23 | description = "The ID of the security group rule for the MSK cluster" 24 | } 25 | 26 | output "security_group_name" { 27 | value = module.kafka.security_group_name 28 | description = "The name of the security group rule for the MSK cluster" 29 | } 30 | 31 | output "security_group_arn" { 32 | value = module.kafka.security_group_arn 33 | description = "The ARN of the created security group" 34 | } 35 | 36 | output "storage_mode" { 37 | value = module.kafka.storage_mode 38 | description = "Storage mode for supported storage tiers" 39 | } 40 | 41 | output "bootstrap_brokers" { 42 | value = module.kafka.bootstrap_brokers 43 | description = "Comma separated list of one or more hostname:port pairs of Kafka brokers suitable to bootstrap connectivity to the Kafka cluster" 44 | } 45 | 46 | output "bootstrap_brokers_tls" { 47 | value = module.kafka.bootstrap_brokers_tls 48 | description = "Comma separated list of one or more DNS names (or IP addresses) and TLS port pairs for access to the Kafka cluster using TLS" 49 | } 50 | 51 | output "bootstrap_brokers_public_tls" { 52 | value = module.kafka.bootstrap_brokers_public_tls 53 | description = "Comma separated list of one or more DNS names (or IP addresses) and TLS port pairs for public access to the Kafka cluster using TLS" 54 | } 55 | 56 | output "bootstrap_brokers_sasl_scram" { 57 | value = module.kafka.bootstrap_brokers_sasl_scram 58 | description = "Comma separated list of one or more DNS names (or IP addresses) and SASL SCRAM port pairs for access to the Kafka cluster using SASL/SCRAM" 59 | } 60 | 61 | output "bootstrap_brokers_public_sasl_scram" { 62 | value = module.kafka.bootstrap_brokers_public_sasl_scram 63 | description = "Comma separated list of one or more DNS names (or IP addresses) and SASL SCRAM port pairs for public access to the Kafka cluster using SASL/SCRAM" 64 | } 65 | 66 | output "bootstrap_brokers_sasl_iam" { 67 | value = module.kafka.bootstrap_brokers_sasl_iam 68 | description = "Comma separated list of one or more DNS names (or IP addresses) and SASL IAM port pairs for access to the Kafka cluster using SASL/IAM" 69 | } 70 | 71 | output "bootstrap_brokers_public_sasl_iam" { 72 | value = module.kafka.bootstrap_brokers_public_sasl_iam 73 | description = "Comma separated list of one or more DNS names (or IP addresses) and SASL IAM port pairs for public access to the Kafka cluster using SASL/IAM" 74 | } 75 | 76 | output "zookeeper_connect_string" { 77 | value = module.kafka.zookeeper_connect_string 78 | description = "Comma separated list of one or more hostname:port pairs to connect to the Apache Zookeeper cluster" 79 | } 80 | 81 | output "zookeeper_connect_string_tls" { 82 | value = module.kafka.zookeeper_connect_string_tls 83 | description = "Comma separated list of one or more hostname:port pairs to connect to the Apache Zookeeper cluster via TLS" 84 | } 85 | 86 | output "broker_endpoints" { 87 | value = module.kafka.broker_endpoints 88 | description = "List of broker endpoints" 89 | } 90 | 91 | output "current_version" { 92 | value = module.kafka.current_version 93 | description = "Current version of the MSK Cluster" 94 | } 95 | 96 | output "latest_revision" { 97 | value = module.kafka.latest_revision 98 | description = "Latest revision of the MSK configuration" 99 | } -------------------------------------------------------------------------------- /modules/msk/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.0" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /modules/rds-aurora/README.md: -------------------------------------------------------------------------------- 1 | # RDS Aurora Module 2 | 3 | Terraform module to bootstrap a RDS Aurora instances and other database resources. 4 | 5 | ## Usage 6 | 7 | ```hcl 8 | module "db" { 9 | source = "../../modules/rds-aurora" 10 | 11 | name = "examples-rds-aurora" 12 | 13 | vpc_id = "vpc-1234567890" 14 | db_subnet_group = "db-subnet-group-1234567890" 15 | 16 | db_name = "db_name" 17 | db_master_username = "db_master_username" 18 | db_port = 5432 19 | 20 | db_instance_class = "db.serverless" 21 | instances = { 22 | one = {} 23 | two = {} 24 | } 25 | } 26 | ``` 27 | 28 | ## Module Documentation 29 | 30 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running `terraform-docs md . > ./docs/MODULE.md` from the module directory. 31 | 32 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 33 | -------------------------------------------------------------------------------- /modules/rds-aurora/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_arn" { 2 | description = "The ARN of the RDS instance" 3 | value = module.db.cluster_arn 4 | } 5 | 6 | output "cluster_endpoint" { 7 | description = "The connection endpoint" 8 | value = module.db.cluster_endpoint 9 | } 10 | 11 | output "cluster_reader_endpoint" { 12 | description = "The address of the RDS instance" 13 | value = module.db.cluster_reader_endpoint 14 | } 15 | 16 | output "cluster_members" { 17 | description = "List of RDS Instances that are a part of this cluster" 18 | value = try(module.db.cluster_members, null) 19 | } 20 | 21 | output "cluster_engine_version_actual" { 22 | description = "The running version of the database" 23 | value = module.db.cluster_engine_version_actual 24 | } 25 | 26 | output "cluster_database_name" { 27 | description = "The database name" 28 | value = module.db.cluster_database_name 29 | } 30 | 31 | output "cluster_resource_id" { 32 | description = "The RDS Resource ID of this instance" 33 | value = module.db.cluster_resource_id 34 | } 35 | 36 | output "cluster_port" { 37 | description = "The database port" 38 | value = module.db.cluster_port 39 | } 40 | 41 | output "cluster_master_username" { 42 | description = "The master username for the database" 43 | value = module.db.cluster_master_username 44 | sensitive = true 45 | } 46 | 47 | output "cluster_instances" { 48 | description = "The RDS Instances for this cluster" 49 | value = module.db.cluster_instances 50 | } 51 | 52 | output "enhanced_monitoring_iam_role_name" { 53 | description = "The name of the IAM role used for enhanced monitoring" 54 | value = module.db.enhanced_monitoring_iam_role_name 55 | } 56 | 57 | output "enhanced_monitoring_iam_role_arn" { 58 | description = "The ARN of the IAM role used for enhanced monitoring" 59 | value = module.db.enhanced_monitoring_iam_role_arn 60 | } 61 | 62 | output "enhanced_monitoring_iam_role_unique_id" { 63 | description = "The unique ID of the IAM role used for enhanced monitoring" 64 | value = module.db.enhanced_monitoring_iam_role_unique_id 65 | } 66 | 67 | output "security_group_id" { 68 | description = "The ID of the security group" 69 | value = module.db.security_group_id 70 | } 71 | 72 | output "db_cluster_cloudwatch_log_groups" { 73 | description = "The CloudWatch log groups for the DB cluster" 74 | value = module.db.db_cluster_cloudwatch_log_groups 75 | } 76 | 77 | output "cluster_master_user_secret" { 78 | description = "The AWS Secrets Manager secret created" 79 | value = module.db.cluster_master_user_secret 80 | } 81 | -------------------------------------------------------------------------------- /modules/rds-aurora/rds.tf: -------------------------------------------------------------------------------- 1 | module "db" { 2 | source = "terraform-aws-modules/rds-aurora/aws" 3 | version = "8.3.1" 4 | 5 | name = "${var.name}-rds-aurora" 6 | 7 | # All available versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts 8 | engine = var.db_engine 9 | engine_mode = var.db_engine_mode 10 | engine_version = var.db_engine_version 11 | instance_class = var.db_instance_class 12 | instances = var.db_instances 13 | 14 | serverlessv2_scaling_configuration = { 15 | min_capacity = 2 16 | max_capacity = 10 17 | } 18 | 19 | storage_type = var.db_storage_type 20 | storage_encrypted = var.storage_encrypted 21 | allocated_storage = var.allocated_storage 22 | 23 | # NOTE: Do NOT use 'user' as the value for 'username' as it throws: 24 | # "Error creating DB Instance: InvalidParameterValue: MasterUsername 25 | # user cannot be used as it is a reserved word used by the engine" 26 | database_name = var.db_name 27 | master_username = var.db_master_username 28 | master_password = var.db_master_password 29 | port = var.db_port 30 | 31 | db_subnet_group_name = var.db_subnet_group 32 | vpc_security_group_ids = var.vpc_security_group_ids 33 | vpc_id = var.vpc_id 34 | 35 | preferred_maintenance_window = var.db_maintenance_window 36 | preferred_backup_window = var.db_backup_window 37 | enabled_cloudwatch_logs_exports = ["postgresql"] 38 | create_cloudwatch_log_group = true 39 | 40 | backup_retention_period = var.db_backup_retention_period 41 | skip_final_snapshot = var.enable_skip_final_snapshot 42 | deletion_protection = false 43 | 44 | publicly_accessible = var.enable_public_access 45 | 46 | performance_insights_enabled = true 47 | performance_insights_retention_period = 7 48 | create_monitoring_role = true 49 | monitoring_interval = 60 50 | 51 | manage_master_user_password = var.manage_master_user_password 52 | 53 | tags = var.tags 54 | } 55 | -------------------------------------------------------------------------------- /modules/rds-aurora/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to be used on all the resources as identifier" 3 | type = string 4 | default = "" 5 | } 6 | 7 | variable "tags" { 8 | description = "Any extra tags to assign to objects" 9 | type = map(any) 10 | default = {} 11 | } 12 | 13 | variable "vpc_id" { 14 | description = "VPC id in which the RDS instance is to be created." 15 | type = string 16 | } 17 | 18 | variable "db_subnet_group" { 19 | description = "Database subnet group to use. Leave blank to create a new one." 20 | type = string 21 | default = "" 22 | } 23 | 24 | variable "db_name" { 25 | description = "Database name" 26 | type = string 27 | default = "name" 28 | } 29 | 30 | variable "db_master_username" { 31 | description = "Database username" 32 | type = string 33 | default = "name" 34 | } 35 | 36 | variable "db_master_password" { 37 | description = "Database password" 38 | type = string 39 | default = "" 40 | } 41 | 42 | variable "db_port" { 43 | description = "Database port" 44 | type = number 45 | default = 5432 46 | } 47 | 48 | variable "db_instance_class" { 49 | description = "The instance class to use for RDS." 50 | type = string 51 | default = "db.serverless" 52 | } 53 | 54 | variable "db_instances" { 55 | description = "Map of cluster instances and any specific/overriding attributes to be created" 56 | type = any 57 | default = {} 58 | } 59 | 60 | variable "db_engine" { 61 | description = "The name of the database engine to be used for RDS." 62 | type = string 63 | default = "aurora-postgresql" 64 | } 65 | 66 | variable "db_engine_mode" { 67 | description = "The database engine mode." 68 | type = string 69 | default = "provisioned" 70 | } 71 | 72 | variable "db_engine_version" { 73 | description = "The database engine version." 74 | type = string 75 | default = "16.3" 76 | } 77 | 78 | variable "db_storage_type" { 79 | description = "Storage Type for RDS." 80 | type = string 81 | default = null 82 | } 83 | 84 | variable "storage_encrypted" { 85 | description = "Enable storage encryption." 86 | type = bool 87 | default = true 88 | } 89 | 90 | variable "allocated_storage" { 91 | description = "Storage size in GB." 92 | type = number 93 | default = null 94 | } 95 | 96 | variable "db_maintenance_window" { 97 | description = "Preferred maintenance window." 98 | type = string 99 | default = "Mon:00:00-Mon:03:00" 100 | } 101 | 102 | variable "db_backup_window" { 103 | description = "Preferred backup window." 104 | type = string 105 | default = "03:00-06:00" 106 | } 107 | 108 | variable "db_backup_retention_period" { 109 | description = "Backup retention period in days." 110 | type = string 111 | default = "1" 112 | } 113 | 114 | variable "enable_skip_final_snapshot" { 115 | description = "When DB is deleted and If this variable is false, no final snapshot will be made." 116 | type = bool 117 | default = true 118 | } 119 | 120 | variable "enable_public_access" { 121 | description = "Enable public access for RDS." 122 | type = bool 123 | default = true 124 | } 125 | 126 | variable "manage_master_user_password" { 127 | description = "Set to true to allow RDS to manage the master user password in Secrets Manager. Cannot be set if `master_password` is provided" 128 | type = bool 129 | default = true 130 | } 131 | 132 | variable "vpc_security_group_ids" { 133 | description = "List of VPC security groups to associate with the RDS cluster" 134 | type = list(string) 135 | default = [] 136 | } 137 | -------------------------------------------------------------------------------- /modules/rds-aurora/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/rds/README.md: -------------------------------------------------------------------------------- 1 | # RDS DB Instance Module 2 | 3 | Terraform module to bootstrap a RDS DB instance. 4 | 5 | ## Usage 6 | 7 | ```hcl 8 | module "db" { 9 | source = "../../modules/rds" 10 | 11 | name = "examples-rds-instance" 12 | 13 | vpc_id = "vpc-1234567890" 14 | db_subnet_group = "db-subnet-group-1234567890" 15 | 16 | db_name = "db_name" 17 | db_master_username = "db_master_username" 18 | db_port = 5432 19 | 20 | db_instance_class = "db.t2.micro" 21 | } 22 | ``` 23 | 24 | ## Module Documentation 25 | 26 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running `terraform-docs md . > ./docs/MODULE.md` from the module directory. 27 | 28 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 29 | -------------------------------------------------------------------------------- /modules/rds/outputs.tf: -------------------------------------------------------------------------------- 1 | output "db_instance_address" { 2 | description = "The address of the RDS instance" 3 | value = module.db.db_instance_address 4 | } 5 | 6 | output "db_instance_arn" { 7 | description = "The ARN of the RDS instance" 8 | value = module.db.db_instance_arn 9 | } 10 | 11 | output "db_instance_availability_zone" { 12 | description = "The availability zone of the RDS instance" 13 | value = module.db.db_instance_availability_zone 14 | } 15 | 16 | output "db_instance_endpoint" { 17 | description = "The connection endpoint" 18 | value = module.db.db_instance_endpoint 19 | } 20 | 21 | output "db_instance_engine" { 22 | description = "The database engine" 23 | value = module.db.db_instance_engine 24 | } 25 | 26 | output "db_instance_engine_version_actual" { 27 | description = "The running version of the database" 28 | value = module.db.db_instance_engine_version_actual 29 | } 30 | 31 | output "db_instance_hosted_zone_id" { 32 | description = "The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record)" 33 | value = module.db.db_instance_hosted_zone_id 34 | } 35 | 36 | output "db_instance_identifier" { 37 | description = "The RDS instance ID" 38 | value = module.db.db_instance_identifier 39 | } 40 | 41 | output "db_instance_resource_id" { 42 | description = "The RDS Resource ID of this instance" 43 | value = module.db.db_instance_resource_id 44 | } 45 | 46 | output "db_instance_status" { 47 | description = "The RDS instance status" 48 | value = module.db.db_instance_status 49 | } 50 | 51 | output "db_instance_name" { 52 | description = "The database name" 53 | value = module.db.db_instance_name 54 | } 55 | 56 | output "db_instance_username" { 57 | description = "The master username for the database" 58 | value = module.db.db_instance_username 59 | sensitive = true 60 | } 61 | 62 | output "db_instance_port" { 63 | description = "The database port" 64 | value = module.db.db_instance_port 65 | } 66 | 67 | output "db_subnet_group_id" { 68 | description = "The db subnet group name" 69 | value = module.db.db_subnet_group_id 70 | } 71 | 72 | output "db_subnet_group_arn" { 73 | description = "The ARN of the db subnet group" 74 | value = module.db.db_subnet_group_arn 75 | } 76 | 77 | output "db_parameter_group_id" { 78 | description = "The db parameter group id" 79 | value = module.db.db_parameter_group_id 80 | } 81 | 82 | output "db_parameter_group_arn" { 83 | description = "The ARN of the db parameter group" 84 | value = module.db.db_parameter_group_arn 85 | } 86 | 87 | output "enhanced_monitoring_iam_role_arn" { 88 | description = "The Amazon Resource Name (ARN) specifying the monitoring role" 89 | value = module.db.enhanced_monitoring_iam_role_arn 90 | } 91 | 92 | output "db_instance_cloudwatch_log_groups" { 93 | description = "Map of CloudWatch log groups created and their attributes" 94 | value = module.db.db_instance_cloudwatch_log_groups 95 | } 96 | 97 | output "db_instance_master_user_secret_arn" { 98 | description = "The ARN of the AWS Secrets Manager secret created" 99 | value = module.db.db_instance_master_user_secret_arn 100 | } 101 | -------------------------------------------------------------------------------- /modules/rds/rds.tf: -------------------------------------------------------------------------------- 1 | module "db" { 2 | source = "terraform-aws-modules/rds/aws" 3 | version = "6.1.1" 4 | 5 | identifier = "${var.name}-rds" 6 | 7 | # All available versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts 8 | engine = var.db_engine 9 | engine_version = var.db_engine_version 10 | family = var.db_family 11 | major_engine_version = var.db_major_engine_version 12 | instance_class = var.db_instance_class 13 | 14 | storage_type = var.db_storage_type 15 | storage_encrypted = var.storage_encrypted 16 | allocated_storage = var.allocated_storage 17 | max_allocated_storage = var.max_allocated_storage 18 | 19 | # NOTE: Do NOT use 'user' as the value for 'username' as it throws: 20 | # "Error creating DB Instance: InvalidParameterValue: MasterUsername 21 | # user cannot be used as it is a reserved word used by the engine" 22 | db_name = var.db_name 23 | username = var.db_master_username 24 | password = var.db_master_password 25 | manage_master_user_password = var.manage_master_user_password 26 | master_user_secret_kms_key_id = var.master_user_secret_kms_key_id 27 | port = var.db_port 28 | 29 | multi_az = var.enable_multi_az 30 | db_subnet_group_name = var.db_subnet_group 31 | vpc_security_group_ids = var.vpc_security_group_ids 32 | 33 | maintenance_window = var.db_maintenance_window 34 | backup_window = var.db_backup_window 35 | enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"] 36 | create_cloudwatch_log_group = true 37 | 38 | backup_retention_period = var.db_backup_retention_period 39 | skip_final_snapshot = var.enable_skip_final_snapshot 40 | deletion_protection = false 41 | 42 | publicly_accessible = var.enable_public_access 43 | 44 | performance_insights_enabled = true 45 | performance_insights_retention_period = 7 46 | create_monitoring_role = true 47 | monitoring_interval = 60 48 | monitoring_role_name = "monitoring" 49 | monitoring_role_use_name_prefix = true 50 | monitoring_role_description = "Monitoring role for ${var.name}" 51 | 52 | parameters = [ 53 | { 54 | name = "autovacuum" 55 | value = 1 56 | }, 57 | { 58 | name = "client_encoding" 59 | value = "utf8" 60 | } 61 | ] 62 | 63 | tags = var.tags 64 | } 65 | -------------------------------------------------------------------------------- /modules/rds/ssm.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ssm_parameter" "address" { 2 | name = "/${var.name}/address" 3 | type = "String" 4 | value = module.db.db_instance_address 5 | } 6 | 7 | resource "aws_ssm_parameter" "name" { 8 | name = "/${var.name}/name" 9 | type = "String" 10 | value = module.db.db_instance_name 11 | } 12 | 13 | resource "aws_ssm_parameter" "port" { 14 | name = "/${var.name}/port" 15 | type = "String" 16 | value = module.db.db_instance_port 17 | } 18 | 19 | resource "aws_ssm_parameter" "master_user_secret_arn" { 20 | name = "/${var.name}/master_user_secret_arn" 21 | type = "String" 22 | value = module.db.db_instance_master_user_secret_arn 23 | } 24 | -------------------------------------------------------------------------------- /modules/rds/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/vpc-endpoints/README.md: -------------------------------------------------------------------------------- 1 | # VPC Endpoints 2 | 3 | Terraform module to create VPC endpoints in an existing VPC. 4 | 5 | ## Usage 6 | 7 | ```hcl 8 | module "vpc_endpoints" { 9 | source = "../../modules/vpc-endpoints" 10 | 11 | vpc_id = module.vpc.vpc_id 12 | 13 | vpc_endpoints = { 14 | s3 = { 15 | service = "s3" 16 | service_type = "Gateway" 17 | route_table_ids = module.vpc.public_route_table_ids 18 | policy = null 19 | tags = { Name = "s3-vpc-endpoint" } 20 | }, 21 | ec2 = { 22 | service = "ec2" 23 | service_type = "Interface" 24 | security_group_ids = [module.vpc.default_security_group_id] 25 | private_dns_enabled = true 26 | subnet_ids = module.vpc.private_subnets 27 | policy = null 28 | tags = { Name = "ec2-vpc-endpoint" } 29 | } 30 | } 31 | 32 | security_group_ids = [module.vpc.default_security_group_id] 33 | tags = { Environment = "dev" } 34 | } 35 | 36 | ## Module Documentation 37 | 38 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running `terraform-docs md . > ./docs/MODULE.md` from the module directory. 39 | 40 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 41 | -------------------------------------------------------------------------------- /modules/vpc-endpoints/docs/MODULE.md: -------------------------------------------------------------------------------- 1 | 2 | ## Requirements 3 | 4 | | Name | Version | 5 | |------|---------| 6 | | [terraform](#requirement\_terraform) | >= 1.0 | 7 | | [aws](#requirement\_aws) | >= 5.0.0 | 8 | 9 | ## Providers 10 | 11 | No providers. 12 | 13 | ## Modules 14 | 15 | | Name | Source | Version | 16 | |------|--------|---------| 17 | | [vpc\_endpoints](#module\_vpc\_endpoints) | terraform-aws-modules/vpc/aws//modules/vpc-endpoints | ~> 3.0 | 18 | 19 | ## Resources 20 | 21 | No resources. 22 | 23 | ## Inputs 24 | 25 | | Name | Description | Type | Default | Required | 26 | |------|-------------|------|---------|:--------:| 27 | | [endpoints](#input\_endpoints) | A map of VPC endpoint configurations to create. | `any` | `{}` | no | 28 | | [security\_group\_ids](#input\_security\_group\_ids) | List of security group IDs to associate with the endpoints | `list(string)` | `[]` | no | 29 | | [subnet\_ids](#input\_subnet\_ids) | List of subnet IDs to associate with the endpoints | `list(string)` | `[]` | no | 30 | | [tags](#input\_tags) | A map of tags to assign to the resources | `map(string)` | `{}` | no | 31 | | [vpc\_id](#input\_vpc\_id) | The ID of the VPC | `string` | n/a | yes | 32 | 33 | ## Outputs 34 | 35 | | Name | Description | 36 | |------|-------------| 37 | | [endpoints](#output\_endpoints) | IDs of the VPC endpoints | 38 | -------------------------------------------------------------------------------- /modules/vpc-endpoints/main.tf: -------------------------------------------------------------------------------- 1 | module "vpc_endpoints" { 2 | source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints" 3 | version = "~> 3.0" 4 | 5 | vpc_id = var.vpc_id 6 | security_group_ids = var.security_group_ids 7 | endpoints = var.endpoints 8 | tags = var.tags 9 | } 10 | -------------------------------------------------------------------------------- /modules/vpc-endpoints/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoints" { 2 | description = "IDs of the VPC endpoints" 3 | value = module.vpc_endpoints.endpoints 4 | } 5 | -------------------------------------------------------------------------------- /modules/vpc-endpoints/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_id" { 2 | description = "The ID of the VPC" 3 | type = string 4 | } 5 | 6 | variable "endpoints" { 7 | description = "A map of VPC endpoint configurations to create." 8 | type = any 9 | default = {} 10 | } 11 | 12 | variable "security_group_ids" { 13 | description = "List of security group IDs to associate with the endpoints" 14 | type = list(string) 15 | default = [] 16 | } 17 | 18 | variable "subnet_ids" { 19 | description = "List of subnet IDs to associate with the endpoints" 20 | type = list(string) 21 | default = [] 22 | } 23 | 24 | variable "tags" { 25 | description = "A map of tags to assign to the resources" 26 | type = map(string) 27 | default = {} 28 | } 29 | -------------------------------------------------------------------------------- /modules/vpc-endpoints/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/vpc/README.md: -------------------------------------------------------------------------------- 1 | # VPC 2 | 3 | Terraform module to bootstrap a VPC for use with our shared infrastructure. 4 | 5 | ## Usage 6 | 7 | ```hcl 8 | module "vpc" { 9 | source = "../../modules/vpc" 10 | 11 | name = "shared-vpc" 12 | 13 | vpc_cidr_block = "10.0.0.0/16" 14 | } 15 | ``` 16 | 17 | ## Module Documentation 18 | 19 | The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running `terraform-docs md . > ./docs/MODULE.md` from the module directory. 20 | 21 | You can also view the latest version of the module documentation [here](./docs/MODULE.md). 22 | -------------------------------------------------------------------------------- /modules/vpc/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "value of the vpc_id output from the vpc module" 3 | value = module.vpc.vpc_id 4 | } 5 | 6 | output "vpc_cidr_block" { 7 | description = "value of the vpc_cidr_block output from the vpc module" 8 | value = module.vpc.vpc_cidr_block 9 | } 10 | 11 | output "public_subnets" { 12 | description = "value of the public_subnets output from the vpc module" 13 | value = module.vpc.public_subnets 14 | } 15 | 16 | output "private_subnets" { 17 | description = "value of the private_subnets output from the vpc module" 18 | value = module.vpc.private_subnets 19 | } 20 | 21 | output "database_subnets" { 22 | description = "value of the database_subnets output from the vpc module" 23 | value = module.vpc.database_subnets 24 | } 25 | 26 | output "database_subnet_group" { 27 | description = "value of the database_subnet_group output from the vpc module" 28 | value = module.vpc.database_subnet_group 29 | } 30 | 31 | output "app_subnets" { 32 | description = "value of the app_subnets output from the vpc module. It is an alias for the private_subnets output" 33 | value = module.vpc.private_subnets 34 | } 35 | 36 | output "app_security_group" { 37 | description = "value of the app_security_group output from the vpc module" 38 | value = module.app_security_group.security_group_id 39 | } 40 | 41 | output "default_security_group_id" { 42 | description = "value of the default_security_group_id output from the vpc module" 43 | value = module.vpc.default_security_group_id 44 | } 45 | 46 | output "public_route_table_ids" { 47 | description = "List of IDs of public route tables" 48 | value = module.vpc.public_route_table_ids 49 | } 50 | 51 | output "private_route_table_ids" { 52 | description = "List of IDs of private route tables" 53 | value = module.vpc.private_route_table_ids 54 | } 55 | 56 | output "ssm_parameter_vpc_id" { 57 | description = "name of the ssm parameter for the vpc id" 58 | value = aws_ssm_parameter.vpc_id.name 59 | } 60 | 61 | output "ssm_parameter_public_subnets" { 62 | description = "name of the ssm parameter for the public subnets" 63 | value = aws_ssm_parameter.public_subnets.name 64 | } 65 | 66 | output "ssm_parameter_private_subnets" { 67 | description = "name of the ssm parameter for the private subnets" 68 | value = aws_ssm_parameter.private_subnets.name 69 | } 70 | 71 | output "ssm_parameter_database_subnets" { 72 | description = "name of the ssm parameter for the database subnets" 73 | value = aws_ssm_parameter.database_subnets.name 74 | } 75 | 76 | output "ssm_parameter_app_subnets" { 77 | description = "name of the ssm parameter for the app subnets" 78 | value = aws_ssm_parameter.app_subnets.name 79 | } 80 | 81 | output "ssm_parameter_app_security_group" { 82 | description = "name of the ssm parameter for the app security group" 83 | value = aws_ssm_parameter.app_security_group.name 84 | } 85 | -------------------------------------------------------------------------------- /modules/vpc/sg.tf: -------------------------------------------------------------------------------- 1 | module "app_security_group" { 2 | source = "terraform-aws-modules/security-group/aws" 3 | version = "~> 4.0" 4 | 5 | name = "${var.name}-app-security-group" 6 | description = "Security group to be used for application servers" 7 | vpc_id = module.vpc.vpc_id 8 | 9 | egress_rules = ["all-all"] 10 | 11 | tags = var.tags 12 | } 13 | -------------------------------------------------------------------------------- /modules/vpc/ssm.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ssm_parameter" "vpc_id" { 2 | name = "/${var.name}/vpc_id" 3 | type = "String" 4 | value = module.vpc.vpc_id 5 | } 6 | 7 | resource "aws_ssm_parameter" "database_subnets" { 8 | name = "/${var.name}/database_subnets" 9 | type = "StringList" 10 | value = join(",", module.vpc.database_subnets) 11 | } 12 | 13 | resource "aws_ssm_parameter" "database_subnet_group" { 14 | name = "/${var.name}/database_subnet_group" 15 | type = "String" 16 | value = module.vpc.database_subnet_group 17 | } 18 | 19 | resource "aws_ssm_parameter" "public_subnets" { 20 | name = "/${var.name}/public_subnets" 21 | type = "StringList" 22 | value = join(",", module.vpc.public_subnets) 23 | } 24 | 25 | resource "aws_ssm_parameter" "private_subnets" { 26 | name = "/${var.name}/private_subnets" 27 | type = "StringList" 28 | value = join(",", module.vpc.private_subnets) 29 | } 30 | 31 | resource "aws_ssm_parameter" "app_subnets" { 32 | name = "/${var.name}/app_subnets" 33 | type = "StringList" 34 | value = join(",", module.vpc.private_subnets) 35 | } 36 | 37 | resource "aws_ssm_parameter" "app_security_group" { 38 | name = "/${var.name}/app_security_group" 39 | type = "String" 40 | value = module.app_security_group.security_group_id 41 | } 42 | -------------------------------------------------------------------------------- /modules/vpc/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to be used on all the resources as identifier" 3 | type = string 4 | default = "" 5 | } 6 | 7 | variable "vpc_id" { 8 | description = "VPC to use. Leave blank to create a new VPC." 9 | type = string 10 | default = "" 11 | } 12 | 13 | variable "vpc_cidr_block" { 14 | description = "VPC CIDR Block to use if creating a new VPC" 15 | type = string 16 | default = "10.0.0.0/16" 17 | } 18 | 19 | variable "enable_nat_gateway" { 20 | description = "Enable NAT Gateways for each private subnet" 21 | type = bool 22 | default = true 23 | } 24 | 25 | variable "single_nat_gateway" { 26 | description = "Use a single NAT Gateway for all private subnets" 27 | type = bool 28 | default = true 29 | } 30 | 31 | variable "tags" { 32 | description = "Any extra tags to assign to objects" 33 | type = map(any) 34 | default = {} 35 | } 36 | 37 | variable "azs_count" { 38 | description = "Number of Availability Zones to use. This value is used to determine the number of public and private subnets to create." 39 | type = number 40 | default = 3 41 | } 42 | 43 | variable "public_subnet_tags" { 44 | description = "Any extra tags to assign to public subnets" 45 | type = map(any) 46 | default = {} 47 | } 48 | 49 | variable "private_subnet_tags" { 50 | description = "Any extra tags to assign to private subnets" 51 | type = map(any) 52 | default = {} 53 | } 54 | -------------------------------------------------------------------------------- /modules/vpc/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/vpc/vpc.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | use_existing_vpc = length(var.vpc_id) > 0 ? true : false 3 | } 4 | 5 | data "aws_availability_zones" "available" {} 6 | 7 | locals { 8 | vpc_id = local.use_existing_vpc ? var.vpc_id : module.vpc.vpc_id 9 | azs = slice(data.aws_availability_zones.available.names, 0, var.azs_count) 10 | } 11 | 12 | module "vpc" { 13 | source = "terraform-aws-modules/vpc/aws" 14 | version = "5.0.0" 15 | 16 | name = var.name 17 | 18 | create_vpc = local.use_existing_vpc ? false : true 19 | 20 | cidr = var.vpc_cidr_block 21 | create_igw = true 22 | enable_nat_gateway = var.enable_nat_gateway 23 | single_nat_gateway = var.single_nat_gateway 24 | 25 | azs = local.azs 26 | public_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr_block, 8, k)] 27 | private_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr_block, 8, k + length(local.azs))] 28 | database_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr_block, 8, k + 2 * length(local.azs))] 29 | 30 | enable_dns_hostnames = true 31 | enable_dns_support = true 32 | 33 | create_database_subnet_group = true 34 | public_subnet_tags = var.public_subnet_tags 35 | private_subnet_tags = var.private_subnet_tags 36 | 37 | tags = var.tags 38 | } 39 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Infra Tools Scripts 🛠️ 2 | 3 | Welcome to the **scripts/** directory, designed to house scripts that facilitate various infrastructure tasks, including connecting to AWS Bastion hosts, tunneling into EKS Clusters, and generating `kubeconfig` files for those clusters. These tools are crafted to streamline your interactions with cloud resources, ensuring both efficiency and security in your operations. 4 | 5 | ## Overview 6 | 7 | This directory currently includes the following key scripts: 8 | 9 | 1. **`connect-to-core-networking-bastion-host.sh`**: 10 | 11 | - This script connects to an AWS Bastion host within a specified environment. You can use it to either establish a direct connection or create a tunnel through the bastion host to another target. 12 | 13 | 2. **`connect-to-services-platform-eks-cluster.sh`**: 14 | 15 | - This script is specifically designed to create a tunnel through the bastion host directly to an EKS Cluster. This tunnel is crucial for securely interacting with the EKS Cluster using `kubectl` via a local port. 16 | 17 | 3. **`generate-services-platform-eks-kubeconfig.sh`**: 18 | - This script generates a `kubeconfig` file tailored for the EKS Cluster in a specified environment, configured to work with a local tunnel. It also sets up an `.envrc` file in the root of the repository to automatically set the `KUBECONFIG` environment variable using `direnv`. 19 | 20 | ## Usage Instructions 21 | 22 | For detailed information on how to use each script, including all available options, please run the script with the `--help` flag. This will provide you with comprehensive usage instructions. 23 | 24 | ### Example Usages 25 | 26 | #### 1. `connect-to-core-networking-bastion-host.sh` 27 | 28 | **Simple Example (Direct Connection):** 29 | 30 | ```bash 31 | ./connect-to-core-networking-bastion-host.sh --environment=sandbox 32 | ``` 33 | 34 | This command connects directly to the bastion host in the `sandbox` environment. 35 | 36 | **Advanced Example (With Tunnel):** 37 | 38 | ```bash 39 | ./connect-to-core-networking-bastion-host.sh --environment=staging --tunnel=8080:remote.host:80 40 | ``` 41 | 42 | This command creates a tunnel from port `8080` on your local machine to port `80` on `remote.host` through the bastion host in the `staging` environment. 43 | 44 | #### 2. `connect-to-services-platform-eks-cluster.sh` 45 | 46 | **Example (Create Tunnel to EKS Cluster):** 47 | 48 | ```bash 49 | ./connect-to-services-platform-eks-cluster.sh --environment=sandbox 50 | ``` 51 | 52 | This command sets up a tunnel through the bastion host to the EKS Cluster in the `sandbox` environment, allowing you to use `kubectl` via `http://localhost:${tunnel_local_port}`. 53 | 54 | #### 3. `generate-services-platform-eks-kubeconfig.sh` 55 | 56 | **Example (Generate Kubeconfig for EKS Cluster):** 57 | 58 | ```bash 59 | ./generate-services-platform-eks-kubeconfig.sh --environment=sandbox 60 | ``` 61 | 62 | This command generates a `kubeconfig` file for the EKS Cluster in the `sandbox` environment, configured to work with a local tunnel. It also sets up an `.envrc` file in the root of the repository to automatically set the `KUBECONFIG` environment variable. 63 | 64 | ## Adding New Scripts ✨ 65 | 66 | This repository is built to be scalable and can easily accommodate new scripts. To add a new script: 67 | 68 | 1. **Create your script** in a similar format to the existing ones. 69 | 2. **Document it** within this README, including a brief description and example usage. 70 | 3. **Ensure consistency** in option flags and usage instructions for ease of use across different scripts. 71 | 72 | ## Contribution Guide 73 | 74 | If you have improvements, fixes, or new scripts to add, feel free to submit a pull request. Ensure that your changes are well-documented and maintain the existing style and structure of this repository. 75 | -------------------------------------------------------------------------------- /scripts/connect-to-core-networking-bastion-host.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Script to connect to an AWS Bastion host or create a tunnel through the bastion host. Usage: 4 | ## 5 | ## @script.name [option] ARGUMENTS... 6 | ## 7 | ## Options: 8 | ## -h, --help Display this help message 9 | ## 10 | ## --environment=ENVIRONMENT Environment name (required. e.g. sandbox, development, qa, staging, production) 11 | ## Will be used to retrieve the Bastion host tag. 12 | ## 13 | ## --key-name=KEY_NAME Name of the SSH key file (default: bastion_key) 14 | ## --key-dir=KEY_DIR Directory to store the SSH key (default: ~/.ssh) 15 | ## 16 | ## --bastion-user=USER Username for the bastion host (default: ubuntu) 17 | ## --tunnel=LOCAL_PORT:REMOTE_HOST:REMOTE_PORT 18 | ## Create a tunnel from LOCAL_PORT to REMOTE_PORT on REMOTE_HOST through the bastion host. 19 | ## 20 | ## --tunnel-target-user=USER Username for the tunnel target (optional) 21 | ## --tunnel-target-key=KEY Path to the SSH key file for the tunnel target (optional) 22 | 23 | # -e: exit on error 24 | set -e 25 | 26 | ROOT="$(realpath "$(dirname "$0")"/..)" 27 | SCRIPTS_DIR="${ROOT}/scripts" 28 | 29 | # shellcheck disable=SC1091 30 | . "${SCRIPTS_DIR}/easy-options/easyoptions.sh" || exit 31 | 32 | # Check if the environment is provided 33 | if [[ -z "$environment" ]]; then 34 | show_error "Environment name must be provided" 35 | exit 1 36 | fi 37 | 38 | # Retrieve the Bastion host tag 39 | bastion_tag="nan-${environment}-core-networking-bastion" 40 | 41 | echo "Connecting to the bastion host for the environment: $environment" 42 | 43 | echo "Using the following parameters:" 44 | echo " - Bastion tag: $bastion_tag" 45 | echo " - Key name: ${key_name:-bastion_key}" 46 | echo " - Key directory: ${key_dir:-$HOME/.ssh}" 47 | echo " - Bastion user: ${bastion_user:-ubuntu}" 48 | 49 | # Display the tunnel configuration if provided 50 | if [[ -n "$tunnel" ]]; then 51 | echo " - Tunnel: $tunnel" 52 | if [[ -n "$tunnel_target_user" ]]; then 53 | echo " - Tunnel target user: $tunnel_target_user" 54 | fi 55 | if [[ -n "$tunnel_target_key" ]]; then 56 | echo " - Tunnel target key: $tunnel_target_key" 57 | fi 58 | fi 59 | 60 | # Collect arguments, excluding --environment 61 | connect_args=() 62 | for arg in "$@"; do 63 | [[ "$arg" == --environment* ]] && continue 64 | connect_args+=("$arg") 65 | done 66 | 67 | # Run the connection command 68 | "${ROOT}/modules/bastion/scripts/connect.sh" --tag="$bastion_tag" "${connect_args[@]}" 69 | -------------------------------------------------------------------------------- /scripts/connect-to-services-platform-eks-cluster.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Script to create a tunnel through the bastion host to the EKS Cluster. Usage: 4 | ## 5 | ## @script.name [option] ARGUMENTS... 6 | ## 7 | ## Options: 8 | ## -h, --help Display this help message 9 | ## 10 | ## --environment=ENVIRONMENT Environment name (required. e.g. sandbox, development, qa, staging, production) 11 | ## Will be used to retrieve the Bastion host tag. 12 | ## 13 | ## --key-name=KEY_NAME Name of the SSH key file (default: bastion_key) 14 | ## --key-dir=KEY_DIR Directory to store the SSH key (default: ~/.ssh) 15 | ## 16 | ## --bastion-user=USER Username for the bastion host (default: ubuntu) 17 | ## 18 | ## --tunnel-local-port=PORT Local port for the tunnel (default: 9444) 19 | 20 | # -e: exit on error 21 | set -e 22 | 23 | ROOT="$(realpath "$(dirname "$0")"/..)" 24 | SCRIPTS_DIR="${ROOT}/scripts" 25 | 26 | # shellcheck disable=SC1091 27 | . "${SCRIPTS_DIR}/easy-options/easyoptions.sh" || exit 28 | 29 | # Check if the environment is provided 30 | if [[ -z "$environment" ]]; then 31 | show_error "Environment name must be provided" 32 | exit 1 33 | fi 34 | 35 | tunnel_local_port="${tunnel_local_port:-9444}" 36 | 37 | cluster_name="nan-${environment}-services-platform-cluster" 38 | tunnel_remote_host="$(aws eks describe-cluster --name "$cluster_name" --query 'cluster.endpoint' --output text | sed 's/https:\/\///')" 39 | tunnel_remote_port=443 40 | tunnel="${tunnel_local_port}:${tunnel_remote_host}:${tunnel_remote_port}" 41 | 42 | # Collect arguments, excluding --tunnel-local-port and --tunnel-remote-port 43 | connect_args=() 44 | for arg in "$@"; do 45 | [[ "$arg" == --tunnel-local-port* ]] && continue 46 | connect_args+=("$arg") 47 | done 48 | 49 | # Run the connection command 50 | "${SCRIPTS_DIR}/connect-to-core-networking-bastion-host.sh" --tunnel="$tunnel" "${connect_args[@]}" 51 | -------------------------------------------------------------------------------- /scripts/easy-options/README.md: -------------------------------------------------------------------------------- 1 | # EasyOptions 2 | 3 | EasyOptions allows you to write the help text for your program _only once_, and have the described options _automatically parsed_ from command line into easily readable variables, _without complicated API_. EasyOptions was developed after discontentment with the existing solutions for option parsing in Bash. It was conceived with the following guidelines in mind: 4 | 5 | - Avoid duplication of source code documentation, help text and options specification. 6 | - Have the option values parsed into easily readable variables. 7 | - Have the non-option arguments available on a simple, separate array. 8 | - Usage as simple as one single line of code. 9 | 10 | EasyOptions is going to parse all of your options and arguments automatically once sourced. You specify what options are supported by your program by simply writing a help text, using special double-hash comments. This help text also works at the same time as source code documentation and options specification. All client scripts have an automatic `--help` option, which is going to display such documentation. You can see more details, specially about the options specification, in the help text of EasyOptions itself. 11 | 12 | ## Usage 13 | 14 | For using EasyOptions in your script, simply document it using double-hash comments like this: 15 | 16 | ```bash 17 | ## Program Name v1.0 18 | ## Copyright (C) Someone 19 | ## Licensed under XYZ 20 | ## 21 | ## This program does something with the arguments. Usage: 22 | ## @script.name [option] ARGUMENTS... 23 | ## 24 | ## Options: 25 | ## -h, --help All client scripts have this, it can be omitted. 26 | ## -o, --some-option This is a boolean option. Long version is 27 | ## mandatory, and can be specified before or 28 | ## after short version. 29 | ## --some-boolean This is a boolean option without a short version. 30 | ## --some-value=VALUE This is a parameter option. When calling your script 31 | ## the equal sign is optional and blank space can be 32 | ## used instead. Short version is not available in this 33 | ## format. 34 | ``` 35 | 36 | The above comments work both as source code documentation and as help text, as well as define the options supported by your script. There is no duplication of the options specification. The string `@script.name` will be replaced with the actual script name. Now you only need to call EasyOptions in your script and _that's it_! 37 | 38 | ### Using Arguments 39 | 40 | After writing your documentation, you simply source this script. Then all command line options will get parsed into the corresponding variables. You can then check their values for reacting to them. Regular arguments will be available in the `$arguments` array. You can source `easyoptions.sh` for a pure Bash implementation. Here is an example for parsing the comments above: 41 | 42 | ```bash 43 | ROOT=$(dirname "$0") 44 | source "${ROOT}/easyoptions.sh" || exit # Bash implementation, slower 45 | 46 | # Boolean and parameter options 47 | [[ -n "$some_option" ]] && echo "Option specified: --some-option" 48 | [[ -n "$some_boolean" ]] && echo "Option specified: --some-boolean" 49 | [[ -n "$some_value" ]] && echo "Option specified: --some-value is $some_value" 50 | 51 | # Arguments 52 | for argument in "${arguments[@]}"; do 53 | echo "Argument specified: $argument" 54 | done 55 | ``` 56 | 57 | If using the pure Bash implementation, then for better speed you may want to define the options in source code yourself, so they do not need to be parsed from the documentation. The side effect is that when changing them, you will need to update both the documentation and the source code. You define the options statically like this: 58 | 59 | ```bash 60 | options=(o=option some-boolean some-value=?) 61 | ``` 62 | -------------------------------------------------------------------------------- /scripts/easy-options/example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## EasyOptions Example 4 | ## Copyright (C) Someone 5 | ## Licensed under XYZ 6 | ## 7 | ## This program is an example of EasyOptions. It just prints the options and 8 | ## arguments provided in command line. Usage: 9 | ## 10 | ## @script.name [option] ARGUMENTS... 11 | ## 12 | ## Options: 13 | ## -h, --help All client scripts have this, it can be omitted. 14 | ## -o, --some-option This is a boolean option. Long version is 15 | ## mandatory, and can be specified before or 16 | ## after short version. 17 | ## --some-boolean This is a boolean option without a short version. 18 | ## --some-value=VALUE This is a parameter option. When calling your script 19 | ## the equal sign is optional and blank space can be 20 | ## used instead. Short version is not available in this 21 | ## format. 22 | 23 | ROOT=$(dirname "$0") 24 | source "${ROOT}/easyoptions.sh" || exit # Bash implementation, slower 25 | 26 | # Boolean and parameter options 27 | [[ -n "$some_option" ]] && echo "Option specified: --some-option" 28 | [[ -n "$some_boolean" ]] && echo "Option specified: --some-boolean" 29 | [[ -n "$some_value" ]] && echo "Option specified: --some-value is $some_value" 30 | 31 | # Arguments 32 | for argument in "${arguments[@]}"; do 33 | echo "Argument specified: $argument" 34 | done 35 | -------------------------------------------------------------------------------- /scripts/generate-services-platform-eks-kubeconfig.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Script to generate the kubeconfig file for the EKS Cluster. Usage: 4 | ## 5 | ## @script.name [option] ARGUMENTS... 6 | ## 7 | ## Options: 8 | ## -h, --help Display this help message 9 | ## 10 | ## --environment=ENVIRONMENT Environment name (required. e.g. sandbox, development, qa, staging, production) 11 | ## Will be used to retrieve the Bastion host tag. 12 | ## 13 | ## --local-port=PORT Local port to use when interacting with the EKS Cluster (default: 9444) 14 | 15 | # -e: exit on error 16 | set -e 17 | 18 | # Load easy-options 19 | ROOT="$(realpath "$(dirname "$0")"/..)" 20 | SCRIPTS_DIR="${ROOT}/scripts" 21 | 22 | # shellcheck disable=SC1091 23 | . "${SCRIPTS_DIR}/easy-options/easyoptions.sh" || exit 24 | 25 | # Validate required arguments 26 | if [[ -z "$environment" ]]; then 27 | show_error "Environment name must be provided" 28 | exit 1 29 | fi 30 | 31 | # Check if direnv is installed 32 | if ! command -v direnv &> /dev/null; then 33 | show_error "direnv is required to configure the .envrc file. Check the docs for installation instructions at https://direnv.net" 34 | exit 1 35 | fi 36 | 37 | KUBECONFIG_DIR="${ROOT}/.kubeconfig" 38 | LOCAL_PORT="${local_port:-9444}" 39 | REGION="us-west-2" 40 | CLUSTER_NAME="nan-${environment}-services-platform-cluster" 41 | 42 | KUBECONFIG_FILE="${KUBECONFIG_DIR}/$CLUSTER_NAME" 43 | 44 | # Create kubeconfig directory if it doesn't exist 45 | mkdir -p "${KUBECONFIG_DIR}" 46 | 47 | # Generate kubeconfig 48 | cat < "${KUBECONFIG_FILE}" 49 | apiVersion: v1 50 | clusters: 51 | - cluster: 52 | insecure-skip-tls-verify: true 53 | server: https://localhost:${LOCAL_PORT} 54 | name: ${CLUSTER_NAME}-using-tunnel 55 | contexts: 56 | - context: 57 | cluster: ${CLUSTER_NAME}-using-tunnel 58 | user: ${CLUSTER_NAME}-using-tunnel 59 | name: ${CLUSTER_NAME}-using-tunnel 60 | current-context: ${CLUSTER_NAME}-using-tunnel 61 | kind: Config 62 | preferences: {} 63 | users: 64 | - name: ${CLUSTER_NAME}-using-tunnel 65 | user: 66 | exec: 67 | apiVersion: client.authentication.k8s.io/v1beta1 68 | args: 69 | - --region 70 | - ${REGION} 71 | - eks 72 | - get-token 73 | - --cluster-name 74 | - ${CLUSTER_NAME} 75 | - --output 76 | - json 77 | command: aws 78 | interactiveMode: IfAvailable 79 | provideClusterInfo: false 80 | EOF 81 | 82 | # Inform the user 83 | echo "Kubeconfig generated at ${KUBECONFIG_FILE}" 84 | 85 | # Set up .envrc in the root of the repository 86 | ENVRC_PATH="${ROOT}/.envrc" 87 | 88 | if [ ! -f "${ENVRC_PATH}" ]; then 89 | echo "#!/usr/bin/env bash" > "${ENVRC_PATH}" 90 | echo "" >> "${ENVRC_PATH}" 91 | KUBECONFIG_FILE_RELATIVE_PATH=$(realpath --relative-to="${ROOT}" "${KUBECONFIG_FILE}") 92 | # shellcheck disable=SC2016 93 | echo 'export KUBECONFIG="$(realpath' "${KUBECONFIG_FILE_RELATIVE_PATH})\"" >> "${ENVRC_PATH}" 94 | direnv allow "${ROOT}" 95 | echo ".envrc configured in the repository root to automatically set KUBECONFIG." 96 | else 97 | echo ".envrc already exists in the repository root. Make sure it sets KUBECONFIG to $(realpath "${KUBECONFIG_FILE}")." 98 | fi 99 | 100 | echo "To start using kubectl, ensure you have the tunnel active and run your kubectl commands." 101 | -------------------------------------------------------------------------------- /tools/danger/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # See https://help.github.com/ignore-files/ for more about ignoring files. 64 | 65 | # dependencies 66 | /node_modules 67 | 68 | # testing 69 | /coverage 70 | 71 | # production 72 | /build 73 | 74 | # misc 75 | .DS_Store 76 | .env.local 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | /.vscode/**/* 81 | 82 | npm-debug.log* 83 | yarn-debug.log* 84 | yarn-error.log* 85 | -------------------------------------------------------------------------------- /tools/danger/.node-version: -------------------------------------------------------------------------------- 1 | v22.3.0 2 | -------------------------------------------------------------------------------- /tools/danger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "terraform-aws-starter-danger", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "tsc": "tsc", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/nanlabs/terraform-aws-starter.git" 16 | }, 17 | "author": "ulises-jeremias", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/nanlabs/terraform-aws-starter/issues" 21 | }, 22 | "homepage": "https://github.com/nanlabs/terraform-aws-starter#readme", 23 | "dependencies": { 24 | "danger": "^11.2.6" 25 | }, 26 | "devDependencies": { 27 | "typescript": "^5.5.4" 28 | } 29 | } 30 | --------------------------------------------------------------------------------