diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ee1a7ce --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# EditorConfig is awesome: http://EditorConfig.org +# Uses editorconfig to maintain consistent coding styles + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 120 +trim_trailing_whitespace = true + +[*.md] +max_line_length = 0 +trim_trailing_whitespace = false + +[COMMIT_EDITMSG] +max_line_length = 0 diff --git a/.github/workflows/deploy-terraform.yaml b/.github/workflows/deploy-terraform.yaml new file mode 100644 index 0000000..d923d7e --- /dev/null +++ b/.github/workflows/deploy-terraform.yaml @@ -0,0 +1,37 @@ +name: deploy terraform + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - 'terraform/**' + - 'atlantis/**' + +jobs: + deployment: + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - uses: actions/checkout@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to Docker Hub + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin + - name: Terraform-CI + uses: docker/build-push-action@v3 + with: + context: ./terraform + push: true + tags: ghcr.io/${{ github.repository_owner }}/terraform-ci:latest + - name: Atlantis + uses: docker/build-push-action@v3 + with: + context: ./atlantis + push: true + tags: ghcr.io/${{ github.repository_owner }}/terraform-ci:atlantis diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bff2d76 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.iml diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..bbeeb03 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,6 @@ +ignored: + - DL3003 + - DL3007 + - DL3013 + - DL3018 + - SC1091 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..05ccbd8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,25 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: check-merge-conflict + - id: end-of-file-fixer + - id: check-added-large-files + - id: check-yaml + - id: detect-aws-credentials + args: [--allow-missing-credentials] + - repo: https://github.com/hadolint/hadolint + rev: v2.12.1-beta + hooks: + - id: hadolint-docker + - repo: https://github.com/gruntwork-io/pre-commit + rev: v0.1.23 + hooks: + - id: terragrunt-hclfmt + files: (\.hcl)$ + - id: shellcheck + types: [shell] + +ci: + autoupdate_schedule: monthly diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8788ba9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2023 Alexander Dobrodey + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/atlantis/Dockerfile b/atlantis/Dockerfile new file mode 100644 index 0000000..773a587 --- /dev/null +++ b/atlantis/Dockerfile @@ -0,0 +1,48 @@ +ARG ATLANTIS_VERSION=v0.26.0 +FROM ghcr.io/runatlantis/atlantis:${ATLANTIS_VERSION} AS tools + +ARG GOSU_VERSION=1.16 +RUN \ + wget -qO /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64" && \ + chmod +x /usr/local/bin/gosu + +# Install infracost +ARG INFRACOST_VERSION=v0.10.30 +RUN \ + wget -qO infracost-linux-amd64.tar.gz "https://github.com/infracost/infracost/releases/download/${INFRACOST_VERSION}/infracost-linux-amd64.tar.gz" && \ + tar xzf infracost-linux-amd64.tar.gz && \ + mv infracost-linux-amd64 /usr/local/bin/infracost && \ + rm -f infracost-linux-amd64.tar.gz + + +# Install Atlantis +FROM cerebellumnetwork/terraform-ci:latest + +RUN \ + addgroup atlantis && \ + adduser -S atlantis -G atlantis && \ + cp -R /root/.terraform.d /home/atlantis/ && \ + cp /root/.terraformrc /home/atlantis/ + +ENV TF_PLUGIN_CACHE_DIR="/home/atlantis/.terraform.d/plugins" +ENV ATLANTIS_TERRAFORM_VERSION=${TERRAFORM_VERSION} +ENV DEFAULT_TERRAFORM_VERSION=$TERRAFORM_VERSION +ENV TERRAGRUNT_TFPATH="terraform${TERRAFORM_VERSION}" +ENV TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE=true +ENV DEFAULT_CONFTEST_VERSION=0.35.0 + +COPY /root/.terraformrc /home/atlantis/ +COPY terragrunt-core-atlantis.hcl /home/atlantis/.terraform.d/terragrunt-core.hcl +COPY atlantis-config.yaml /etc/atlantis/repos.yaml + +COPY --from=tools /usr/local/bin/gosu /usr/local/bin/gosu +COPY --from=tools /usr/local/bin/atlantis /usr/local/bin/atlantis +COPY --from=tools /usr/bin/dumb-init /usr/local/bin/dumb-init +COPY --from=tools /usr/local/bin/conftest /usr/local/bin/conftest +COPY --from=tools /usr/local/bin/infracost /usr/local/bin/infracost + +COPY --from=tools /usr/bin/dumb-init /usr/bin/dumb-init +COPY --from=tools /usr/local/bin/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh + +ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"] +CMD ["server"] diff --git a/atlantis/atlantis-config.yaml b/atlantis/atlantis-config.yaml new file mode 100644 index 0000000..e2f52b0 --- /dev/null +++ b/atlantis/atlantis-config.yaml @@ -0,0 +1,117 @@ +--- +repos: + - id: "/.*/" + apply_requirements: + - approved + - mergeable + - undiverged + workflow: terragrunt + allowed_workflows: + - terragrunt +# - terragrunt-run-all + delete_source_branch_on_merge: true + repo_locking: false + post_workflow_hooks: + - run: | + if [ ! -d "/tmp/$BASE_REPO_OWNER-$BASE_REPO_NAME-$PULL_NUM" ]; then + exit 0 + fi + infracost comment github \ + --repo "${BASE_REPO_OWNER}/${BASE_REPO_NAME}" \ + --pull-request "${PULL_NUM}" \ + --path /tmp/${BASE_REPO_OWNER}-${BASE_REPO_NAME}-${PULL_NUM}/'*'-infracost.json \ + --github-token "${INFRACOST_GITHUB_TOKEN}" \ + --behavior delete-and-new + rm -rf "/tmp/${BASE_REPO_OWNER}-${BASE_REPO_NAME}-${PULL_NUM}" + mkdir -p "/tmp/${BASE_REPO_OWNER}-${BASE_REPO_NAME}-${PULL_NUM}" + +workflows: + terragrunt: + plan: + steps: + - env: + name: TERRAGRUNT_TFPATH + command: 'echo "terraform${ATLANTIS_TERRAFORM_VERSION}"' + - env: + name: TF_IN_AUTOMATION + value: 'true' + - env: + name: TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE + value: 'true' + - env: + name: INFRACOST_OUTPUT + command: 'echo "/tmp/$BASE_REPO_OWNER-$BASE_REPO_NAME-$PULL_NUM/$WORKSPACE-${REPO_REL_DIR//\//-}-infracost.json"' + - run: | + mkdir -p "/tmp/${BASE_REPO_OWNER}-${BASE_REPO_NAME}-${PULL_NUM}" + + if ! [[ "${BASE_BRANCH_NAME}" == "production" || "${BASE_BRANCH_NAME}" == "develop" ]]; then + echo "Environment skipped. Please open a Pull Request to production or develop branch to plan and apply infrastructure changes." + exit 0 + elif [[ "${REPO_REL_DIR}" == *"prod"* && "${BASE_BRANCH_NAME}" != "production" ]]; then + echo "Environment skipped. Please open a Pull Request to production branch to plan and apply production changes." + exit 0 + else + cp "${HOME}/.terraform.d/terragrunt-core.hcl" "${DIR/\/${REPO_REL_DIR}/}" + terragrunt plan -input=false -out=$PLANFILE + terragrunt show -json $PLANFILE > $SHOWFILE + infracost breakdown \ + --path=$SHOWFILE \ + --format=json \ + --log-level=info \ + --out-file=${INFRACOST_OUTPUT} \ + --project-name=$REPO_REL_DIR + fi + apply: + steps: + - env: + name: TERRAGRUNT_TFPATH + command: 'echo "terraform${ATLANTIS_TERRAFORM_VERSION}"' + - env: + name: TF_IN_AUTOMATION + value: 'true' + - env: + name: TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE + value: 'true' + - run: | + if ! [[ "${BASE_BRANCH_NAME}" == "production" || "${BASE_BRANCH_NAME}" == "develop" ]]; then + echo "Environment skipped. Please open a Pull Request to production or develop branch to plan and apply infrastructure changes." + exit 0 + elif [[ "${REPO_REL_DIR}" == *"prod"* && "${BASE_BRANCH_NAME}" != "production" ]]; then + echo "Environment skipped. Please open a Pull Request to production branch to plan and apply production changes." + exit 0 + else + cp "${HOME}/.terraform.d/terragrunt-core.hcl" "${DIR/\/${REPO_REL_DIR}/}" + terragrunt terragrunt apply -input=false $PLANFILE + fi + +# terragrunt-run-all: +# plan: +# steps: +# - run: | +# mkdir -p "/tmp/${BASE_REPO_OWNER}-${BASE_REPO_NAME}-${PULL_NUM}" +# if ! [[ "${BASE_BRANCH_NAME}" == "production" || "${BASE_BRANCH_NAME}" == "develop" ]]; then +# echo "Environment skipped. Please open a Pull Request to production or develop branch to plan and apply infrastructure changes." +# exit 0 +# elif [[ "${REPO_REL_DIR}" == *"prod"* ]]; then +# if ! [[ "${BASE_BRANCH_NAME}" == "production" ]]; then +# echo "Environment skipped. Please open a Pull Request to production branch to plan and apply production changes." +# exit 0 +# fi +# else +# cp "${HOME}/.terraform.d/terragrunt-core.hcl" "${DIR/\/${REPO_REL_DIR}/}" +# terragrunt run-all plan -out plan.out" +# fi +# apply: +# steps: +# - run: | +# if ! [[ "${BASE_BRANCH_NAME}" == "production" || "${BASE_BRANCH_NAME}" == "develop" ]]; then +# echo "Environment skipped. Please open a Pull Request to production or develop branch to plan and apply infrastructure changes." +# exit 0 +# elif [[ "${REPO_REL_DIR}" == *"prod"* ]]; then +# if ! [[ "${BASE_BRANCH_NAME}" == "production" ]]; then +# echo "Environment skipped. Please open a Pull Request to production branch to plan and apply production changes." +# exit 0 +# fi +# else +# terragrunt run-all apply plan.out --terragrunt-non-interactive +# fi diff --git a/terraform/Dockerfile b/terraform/Dockerfile new file mode 100644 index 0000000..da7f5ca --- /dev/null +++ b/terraform/Dockerfile @@ -0,0 +1,119 @@ +################################################################################# +# Inspired by +# https://github.com/cloudbees/java-build-tools-dockerfile/blob/master/Dockerfile +################################################################################# +ARG ALPINE_VERSION=3.16 +FROM python:3.10.5-alpine${ALPINE_VERSION} as awscli + +ARG AWS_CLI_VERSION=2.13.37 +RUN \ + apk add --no-cache \ + build-base \ + cmake \ + git \ + groff \ + libffi-dev \ + unzip \ + && git clone --single-branch --depth 1 -b ${AWS_CLI_VERSION} https://github.com/aws/aws-cli.git \ + && cd aws-cli \ + && python -m venv venv \ + && . venv/bin/activate \ + && scripts/installers/make-exe \ + && unzip -q dist/awscli-exe.zip \ + && aws/install --bin-dir /aws-cli-bin \ + && /aws-cli-bin/aws --version \ + && rm -rf \ + /usr/local/aws-cli/v2/current/dist/aws_completer \ + /usr/local/aws-cli/v2/current/dist/awscli/data/ac.index \ + /usr/local/aws-cli/v2/current/dist/awscli/examples \ + && find /usr/local/aws-cli/v2/current/dist/awscli/data -name 'completions-1*.json' -delete \ + && find /usr/local/aws-cli/v2/current/dist/awscli/botocore/data -name examples-1.json -delete + + +FROM alpine:${ALPINE_VERSION} as main + +ENV LANG=C.UTF-8 \ + PROFILE="~/.bashrc" + +RUN \ + apk add --no-cache \ + bash \ + curl \ + git \ + git-crypt \ + gnupg \ + jq \ + python3 \ + py3-pip \ + shadow \ + openssh \ + openssh-client \ + unzip \ + wget \ + && pip3 install --no-cache-dir -U \ + ansible \ + hvac \ + pip \ + yq \ + wheel \ + && rm -rf /var/cache/apk/* + +#======================================= +# AWS Cli +#======================================= +COPY --from=awscli /usr/local/aws-cli/ /usr/local/aws-cli/ +COPY --from=awscli /aws-cli-bin/ /usr/local/bin/ + +#======================================= +# Terraform install +#======================================= +ARG TERRAFORM_VERSION=1.6.4 +ENV TERRAFORM_VERSION=$TERRAFORM_VERSION +RUN wget --quiet -O /tmp/terraform.zip \ + "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip" \ + && unzip -p /tmp/terraform.zip terraform > /tmp/terraform \ + && chmod +x /tmp/terraform \ + && mv /tmp/terraform /usr/bin/terraform \ + && ln -s /usr/bin/terraform "/usr/bin/terraform${TERRAFORM_VERSION}" \ + && rm -rf /tmp/terraform.zip + +#======================================= +# Terragrunt install +#======================================= +ENV TERRAGRUNT_VERSION=0.53.5 +RUN wget --quiet -O /tmp/terragrunt \ + "https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64" \ + && chmod +x /tmp/terragrunt \ + && mv /tmp/terragrunt /usr/bin/terragrunt + +#======================================= +# Terraform providers cache +#======================================= +ENV TF_PLUGIN_CACHE_DIR="/root/.terraform.d/plugins" +COPY install-providers.sh /tmp/install-providers.sh +COPY providers.txt ${TF_PLUGIN_CACHE_DIR}/../ +COPY terraform.rc /root/.terraformrc +RUN /tmp/install-providers.sh \ + "${TF_PLUGIN_CACHE_DIR}/../providers.txt" +COPY terragrunt-core.hcl ${TF_PLUGIN_CACHE_DIR}/../ + +# GOLANG support +COPY --from=golang:1.19-alpine /usr/local/go/ /usr/local/go/ +ENV PATH="/usr/local/go/bin:${PATH}" + +#======================================= +# Known Hosts authentication +#======================================= +RUN mkdir -p ~/.ssh > /dev/null \ + && ssh-keyscan "github.com" >> ~/.ssh/known_hosts 2> /dev/null + +#======================================= +# Validate installation +#======================================= +RUN \ + terraform --version \ + && terragrunt --version \ + && yq --version \ + && aws --version \ + && go version +CMD [ "bash" ] diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 0000000..f5b9d75 --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,111 @@ +# Terragrunt execution Agent + +- [Overview](#overview) +- [Prerequisites](#prerequisites) +- [AWS SSO configuration](#aws-sso-configuration) +- [Usage](#usage) + - [Examples](#examples) + - [Arguments](#arguments) + - [Exit codes](#exit-codes) +- [Docker components](#docker-components) + +## Overview +The script builds stack using terraform modules repositories. + +## Prerequisites +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) +- [Docker](https://docs.docker.com/get-docker/) +- [git-crypt](https://github.com/AGWA/git-crypt) +- [jq](https://stedolan.github.io/jq/) +- [aws-sso-credential-process](https://pypi.org/project/aws-sso-credential-process/) + +## AWS SSO configuration +Prepare AWS profiles for SSO authentication +``` +[profile org] +sso_start_url = https://cere.awsapps.com/start +sso_region = us-west-2 +sso_account_id = 524725240689 +sso_role_name = AdministratorAccess +region = us-west-2 +output = json +``` +## Usage +#### Examples +Build plan of ecs/example-service stack in idt_dev AWS account. +Region and environment will be detected automatically. +```shell +terragrunt-test -a cere-sandbox -s ecs/example-service +``` + +List resources in terraform state for _ecs/example-service_ stack in _cere-sandbox_ AWS account, +_us-west-2_ region within _dev_ environment. Debug information will be shown. +```shell +terragrunt-test --debug \ + --account cere-sandbox \ + --region us-west-2 \ + --environment dev \ + --stack ecs/example-service \ + --command 'state list' +``` + +#### Arguments +* **-a**, **--account** (string): Required parameter. Account to plan. + Defined by location in _aws/_ folder. +* **-r**, **--region** (string): Optional parameter. Region to plan. + Defined by location in _aws/**account name**/_ folder. +* **-e**, **--environment** (string): Optional parameter. Environment to plan. + Defined by location in _aws/**account name**/**region**/_ folder. +* **-s**, **--stack** (string): Optional parameter. Opt-out to plan single stack instead of whole environment. + Defined by location of `terragrunt.hcl` pointing to module. + +* **-c**, **--command** (string): Optional parameter. Execute custom terragrunt command. + If stack is not provided adds `run-all` command. Defaults to `plan`. +* **-d**, **--destroy** (boolean): Optional parameter. + If `terraform plan` is executing to destroy resources. Defaults false. + +* **-v**, **--debug** (boolean): Output debug error log. Defaults false. +* **-t**, **--terraform-version** (string): Optional parameter. Select version of terraform image to use. Defaults to `latest`. + +* **--path** (string): Optional parameter. Location of the _*-terra-live_ repository. + Defaults to current working directory. +* **--modules-path** (string): Optional parameter. Location of the _*-terraform-modules_ repository. + Defaults to current working directory. + +#### Exit codes + +* **0**: If successful. +* **1**: Generic error. +* **2**: Missing arguments. +* **3**: Path not found. +* **4**: Missing prerequisite software. +* **5**: Authentication failure. + +## Docker components +- [Terraform](https://github.com/hashicorp/terraform/releases) +- [Terragrunt](https://github.com/gruntwork-io/terragrunt/releases) +- Terraform Providers: + - [archive](https://github.com/hashicorp/terraform-provider-archive/tags) + - [aws](https://github.com/hashicorp/terraform-provider-aws/releases) + - [cloudinit](https://github.com/hashicorp/terraform-provider-cloudinit/tags) + - [external](https://github.com/hashicorp/terraform-provider-external/releases) + - [grafana](https://github.com/grafana/terraform-provider-grafana/releases) + - [helm](https://github.com/hashicorp/terraform-provider-helm/releases) + - [http](https://github.com/hashicorp/terraform-provider-http/releases) + - [htpasswd](https://github.com/loafoe/terraform-provider-htpasswd/releases) + - [kubernetes](https://github.com/hashicorp/terraform-provider-kubernetes/releases) + - [local](https://github.com/hashicorp/terraform-provider-local/releases) + - [null](https://github.com/hashicorp/terraform-provider-null/releases) + - [random](https://github.com/hashicorp/terraform-provider-random/releases) + - [time](https://github.com/hashicorp/terraform-provider-time/releases) + - [tls](https://github.com/hashicorp/terraform-provider-tls/releases) + - [vault](https://github.com/hashicorp/terraform-provider-vault/releases) + +# Update provider versions +You can check new versions of the providers and make decision to upgrade them: + +`./install-providers.sh ` + +For Example: + +`./install-providers.sh providers.txt false` diff --git a/terraform/install-providers.sh b/terraform/install-providers.sh new file mode 100755 index 0000000..70f8c71 --- /dev/null +++ b/terraform/install-providers.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +set -e +###################################################################### +# License Andrei Shahun, Alexander Dobrodey +# Get terraform providers file as name:version separated with new line. +# Installs providers to the TF_PLUGIN_CACHE_DIR location +###################################################################### + +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { + echo -e "${GREEN}INFO: ${1}${NC}" +} + +warning() { + echo -e "${YELLOW}WARNING: ${1}${NC}" +} + +PROVIDERS_URL='https://releases.hashicorp.com' +if [[ "$(uname -p)" == "arm" ]]; then + PLATFORM="darwin_arm64" +else + PLATFORM="linux_amd64" +fi + +DOWNLOAD_FILES=${2:-true} +if $DOWNLOAD_FILES; then + mkdir -p "${TF_PLUGIN_CACHE_DIR}" +fi + +while IFS= read -r line; do + if ! [[ "${line}" =~ ^#.*$ || "${line}" =~ ^\s?$ ]]; then + # info "Processing \"${line}\"" + IFS=':' read -r -a config <<< "${line}" + OWNER="${config[0]}" + NAME="${config[1]}" + VERSION="${config[2]}" + ARCHIVE="/tmp/provider-${NAME}.zip" + if [[ "$OWNER" == "hashicorp" ]]; then + LATEST_VERSION=$( + curl -s "https://releases.hashicorp.com/terraform-provider-${NAME}/" \ + | sed -rn "s|.* /dev/null + rm -f "${ARCHIVE}" + fi + fi +done < "$1" diff --git a/terraform/providers.txt b/terraform/providers.txt new file mode 100644 index 0000000..be7e546 --- /dev/null +++ b/terraform/providers.txt @@ -0,0 +1,16 @@ +hashicorp:archive:2.4.0 +hashicorp:aws:5.26.0 +hashicorp:cloudinit:2.3.2 +hashicorp:external:2.3.1 +integrations:github:5.42.0 +grafana:grafana:2.6.1 +hashicorp:helm:2.11.0 +loafoe:htpasswd:1.0.4 +hashicorp:http:3.4.0 +hashicorp:kubernetes:2.23.0 +hashicorp:local:2.4.0 +hashicorp:null:3.2.2 +hashicorp:random:3.5.1 +hashicorp:time:0.9.1 +hashicorp:tls:4.0.4 +hashicorp:vault:3.23.0 diff --git a/terraform/terraform.rc b/terraform/terraform.rc new file mode 100644 index 0000000..eb59196 --- /dev/null +++ b/terraform/terraform.rc @@ -0,0 +1,17 @@ +plugin_cache_dir = "$HOME/.terraform.d/plugin-cache" + +provider_installation { + filesystem_mirror { + path = "$HOME/.terraform.d/plugins" + include = [ + "*/*", + "*/*/*" + ] + } + direct { + exclude = [ + "*/*", + "*/*/*" + ] + } +} diff --git a/terraform/terragrunt-core.hcl b/terraform/terragrunt-core.hcl new file mode 100644 index 0000000..065b17a --- /dev/null +++ b/terraform/terragrunt-core.hcl @@ -0,0 +1,223 @@ +locals { + master_account_id = "524725240689" + backend_bucket = "cere-io-terraform-states" + backend_region = "us-west-2" + backend_locks_table = "cere-terraform-locks" +} + +locals { + # Automatically load global-level variables + global_vars = read_terragrunt_config( + find_in_parent_folders( + ".global.hcl", + "fallback.hcl", + ) + ) + + # Automatically load account-level variables + account_vars = read_terragrunt_config( + find_in_parent_folders( + ".account.hcl", + "fallback.hcl", + ) + ) + + # Automatically load region-level variables + region_vars = read_terragrunt_config( + find_in_parent_folders( + ".region.hcl", + "fallback.hcl", + ), + { + locals = { + region = local.backend_region + } + } + ) + + # Automatically load environment-level variables + environment_vars = read_terragrunt_config( + find_in_parent_folders( + ".env.hcl", + "fallback.hcl", + ), + { + locals = { + env = "dev" + } + } + ) + + # Extract the variables we need for easy access + infrastructure_repository = local.global_vars.locals.infrastructure_repository + provider = split("/", path_relative_to_include())[0] + account_configuration = { + name = local.account_vars.locals.account_name + id = local.account_vars.locals.account_id + region = local.region_vars.locals.region + } + + env_name = local.environment_vars.locals.env + environment_path = join( + "/", + [ + local.provider, + local.account_configuration.name + ] + ) + stack_path = replace(path_relative_to_include(), local.environment_path, "") + + entry_point_role = "arn:aws:iam::${local.master_account_id}:role/terraform-management" + + terragrunt_config_path = "${get_terragrunt_dir()}/terragrunt.hcl" + terragrunt_stack_name = element( + regex( + ".*stack_name\\s+=\\s+\"([^\"]+)\"", + run_cmd( + "--terragrunt-quiet", + "grep", "stack_name ", local.terragrunt_config_path + ) + ), 0 + ) + + terragrunt_stack_version = element( + regex( + ".*stack_version\\s+=\\s+\"([^\"]+)\"", + run_cmd( + "--terragrunt-quiet", + "grep", "stack_version ", local.terragrunt_config_path + ) + ), 0 + ) + terragrunt_module_name = join( + " ", + [ + local.terragrunt_stack_name, + local.terragrunt_stack_version + ] + ) + full_configuration = merge( + local.global_vars.locals, + local.account_vars.locals, + local.region_vars.locals, + local.environment_vars.locals, + { + terragrunt_config = { + stack_name = local.terragrunt_stack_name + stack_version = local.terragrunt_stack_version + } + } + ) +} + +# Entry-point for terraform executions +iam_role = local.entry_point_role + +terraform { + extra_arguments "retry_lock" { + commands = get_terraform_commands_that_need_locking() + arguments = [ + "-lock-timeout=1m" + ] + } + + extra_arguments "init_upgrade" { + commands = [ + "init" + ] + arguments = [ + "-upgrade" + ] + } + + extra_arguments "unlock_plan" { + commands = [ + "plan" + ] + arguments = [ + "-lock=false" + ] + } + + extra_arguments "conditional_vars" { + commands = get_terraform_commands_that_need_vars() + + optional_var_files = [ + "${get_terragrunt_dir()}/terraform.tfvars", + "${get_terragrunt_dir()}/secrets.auto.tfvars", + ] + } +} + +# Generate an AWS provider block +generate "provider" { + path = "aws-provider.tf" + if_exists = "overwrite_terragrunt" + + contents = <