diff --git a/.github/actions/lint-terraform/action.yaml b/.github/actions/lint-terraform/action.yaml index 28d990c..6957a03 100644 --- a/.github/actions/lint-terraform/action.yaml +++ b/.github/actions/lint-terraform/action.yaml @@ -7,6 +7,11 @@ inputs: runs: using: "composite" steps: + - name: "Install Terraform binary" + shell: bash + run: | + asdf plugin add terraform || true + asdf install terraform || true - name: "Check Terraform format" shell: bash run: | @@ -14,8 +19,4 @@ runs: - name: "Validate Terraform" shell: bash run: | - stacks=${{ inputs.root-modules }} - for dir in $(find infrastructure/environments -maxdepth 1 -mindepth 1 -type d; echo ${stacks//,/$'\n'}); do - dir=$dir opts='-backend=false' make terraform-init - dir=$dir make terraform-validate - done + make terraform-validate-all diff --git a/.github/workflows/stage-1-commit.yaml b/.github/workflows/stage-1-commit.yaml index 811a15e..8b51fc5 100644 --- a/.github/workflows/stage-1-commit.yaml +++ b/.github/workflows/stage-1-commit.yaml @@ -144,6 +144,8 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v4 + - name: "Setup ASDF" + uses: asdf-vm/actions/setup@v4 - name: "Lint Terraform" uses: ./.github/actions/lint-terraform trivy: diff --git a/infrastructure/.gitignore b/infrastructure/.gitignore index 22ebdac..c0c5103 100644 --- a/infrastructure/.gitignore +++ b/infrastructure/.gitignore @@ -2,6 +2,7 @@ # Local .terraform directories **/.terraform/* +**/.terraform.lock.hcl # .tfstate files *.tfstate diff --git a/infrastructure/modules/kms/kms_replica_key_replica.tf b/infrastructure/modules/kms/kms_replica_key_replica.tf index c1ecc52..72cc985 100644 --- a/infrastructure/modules/kms/kms_replica_key_replica.tf +++ b/infrastructure/modules/kms/kms_replica_key_replica.tf @@ -1,6 +1,6 @@ resource "aws_kms_replica_key" "replica" { - provider = aws.us-east-1 - count = var.is_multi_region ? 1 : 0 + provider = aws.us-east-1 + count = var.is_multi_region ? 1 : 0 description = "Multi-Region replica key" deletion_window_in_days = var.deletion_window diff --git a/infrastructure/modules/kms/provider_aws.tf b/infrastructure/modules/kms/provider_aws.tf new file mode 100644 index 0000000..d694811 --- /dev/null +++ b/infrastructure/modules/kms/provider_aws.tf @@ -0,0 +1,24 @@ +provider "aws" { + region = var.region + + allowed_account_ids = [ + var.aws_account_id, + ] + + default_tags { + tags = local.default_tags + } +} + +provider "aws" { + alias = "us-east-1" + region = "us-east-1" + + default_tags { + tags = local.default_tags + } + + allowed_account_ids = [ + var.aws_account_id, + ] +} diff --git a/infrastructure/terraform/.gitignore b/infrastructure/terraform/.gitignore deleted file mode 100644 index 579b641..0000000 --- a/infrastructure/terraform/.gitignore +++ /dev/null @@ -1,67 +0,0 @@ -### Terraform ### - -# Transient backends -components/**/backend_tfscaffold.tf - -# Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject -# to change depending on the environment. -*.tfvars -*.tfvars.json - -# Compiled files -**/*.tfstate -**/*.tfplan -**/*.tfstate.backup -**/.terraform -**/.terraform.lock.hcl -**/.terraform/* -**/build/* -**/work/* -**/*tfstate.lock.info - -# Scaffold Plugin Cache -plugin-cache/* - -# PyCache -**/__pycache__ - -### OSX ### -**/.DS_Store -**/.AppleDouble -**/.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -*.swp -.nyc_output - -# VS Code -.vscode - -# IntelliJ Idea -.idea -**/*.iml - -# js -node_modules diff --git a/scripts/docker/examples/python/assets/hello_world/requirements.txt b/scripts/docker/examples/python/assets/hello_world/requirements.txt index 60f362a..9f9a999 100644 --- a/scripts/docker/examples/python/assets/hello_world/requirements.txt +++ b/scripts/docker/examples/python/assets/hello_world/requirements.txt @@ -8,5 +8,5 @@ MarkupSafe==2.1.3 pip==23.3 setuptools==65.5.1 Werkzeug==3.0.6 -wheel==0.41.1 +wheel==0.46.2 WTForms==3.0.1 diff --git a/scripts/githooks/check-terraform-format.sh b/scripts/githooks/check-terraform-format.sh index 7255e51..569e54c 100755 --- a/scripts/githooks/check-terraform-format.sh +++ b/scripts/githooks/check-terraform-format.sh @@ -29,11 +29,11 @@ function main() { # check_only=[do not format, run check only] function terraform-fmt() { - local opts= if is-arg-true "$check_only"; then - opts="-check" + make terraform-fmt-check + else + make terraform-fmt fi - opts=$opts make terraform-fmt } # ============================================================================== diff --git a/scripts/init.mk b/scripts/init.mk index e12255c..885d2d3 100644 --- a/scripts/init.mk +++ b/scripts/init.mk @@ -47,7 +47,7 @@ _install-dependency: # Install asdf dependency - mandatory: name=[listed in the _install-dependencies: # Install all the dependencies listed in .tool-versions for plugin in $$(grep ^[a-z] .tool-versions | sed 's/[[:space:]].*//'); do - make _install-dependency name="$${plugin}" + $(MAKE) _install-dependency name=$${plugin}; \ done clean:: # Remove all generated and temporary files (common) @Operations diff --git a/scripts/terraform/terraform.mk b/scripts/terraform/terraform.mk index 24afc4d..8f46009 100644 --- a/scripts/terraform/terraform.mk +++ b/scripts/terraform/terraform.mk @@ -1,62 +1,89 @@ -# This file is for you! Edit it to implement your own Terraform make targets. +# Terraform Make Targets for Shared Modules +# This repository contains only Terraform modules (no components or tfscaffold) +# Modules are located in infrastructure/modules/ # ============================================================================== -# Custom implementation - implementation of a make target should not exceed 5 lines of effective code. -# In most cases there should be no need to modify the existing make targets. +# Formatting and Validation -terraform-fmt: # Format Terraform files - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform fmt command, default is '-recursive'] @Quality - make _terraform cmd="fmt" \ - dir=$(or ${terraform_dir}, ${dir}) \ - opts=$(or ${terraform_opts}, ${opts}) +terraform-fmt: # Format Terraform module files @Quality + # Example: make terraform-fmt + @cd infrastructure && terraform fmt -recursive modules -_terraform: # Terraform command wrapper - mandatory: cmd=[command to execute]; optional: dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], opts=[options to pass to the Terraform command, default is none/empty] - # 'TERRAFORM_STACK' is passed to the functions as environment variable - TERRAFORM_STACK=$(or ${TERRAFORM_STACK}, $(or ${terraform_stack}, $(or ${STACK}, ${stack}))) - dir=$(or ${dir}, ${TERRAFORM_STACK}) - . "scripts/terraform/terraform.lib.sh"; \ - terraform-${cmd} # 'dir' and 'opts' are accessible by the function as environment variables, if set +terraform-fmt-check: # Check Terraform module formatting @Quality + # Example: make terraform-fmt-check + @cd infrastructure && terraform fmt -check -recursive modules -# ============================================================================== -# Quality checks - please DO NOT edit this section! +terraform-validate: # Validate a specific Terraform module - mandatory: module=[module_name] @Quality + # Example: make terraform-validate module=mymodule + # Note: Validation does not require environment/group as it checks syntax only + cd infrastructure/modules/$(module) && \ + terraform init -backend=false && \ + terraform validate -terraform-shellscript-lint: # Lint all Terraform module shell scripts @Quality - for file in $$(find scripts/terraform -type f -name "*.sh"); do - file=$${file} scripts/shellscript-linter.sh +terraform-validate-all: # Validate all Terraform modules @Quality + # Example: make terraform-validate-all + @for dir in infrastructure/modules/*; do \ + if [ -d "$$dir" ]; then \ + echo "Validating $$(basename $$dir)..."; \ + temp_provider=false; \ + if grep -q "configuration_aliases.*us-east-1" "$$dir/versions.tf" 2>/dev/null; then \ + echo "provider \"aws\" { alias = \"us-east-1\"; region = \"us-east-1\" }" > "$$dir/.tmp_providers.tf"; \ + temp_provider=true; \ + fi; \ + cd $$dir && \ + terraform init -backend=false && \ + terraform validate; \ + validation_result=$$?; \ + cd - > /dev/null; \ + if [ "$$temp_provider" = "true" ]; then \ + rm -f "$$dir/.tmp_providers.tf"; \ + fi; \ + if [ $$validation_result -ne 0 ]; then \ + exit $$validation_result; \ + fi; \ + fi; \ done -terraform-sec: # TFSEC check against Terraform files - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform fmt command, default is '-recursive'] @Quality - tfsec infrastructure/modules \ - --force-all-dirs \ - --exclude-downloaded-modules \ - --config-file scripts/config/tfsec.yaml +terraform-sec: # Run Trivy IaC security scanning on Terraform modules @Quality + # Example: make terraform-sec + ./scripts/terraform/trivy-scan.sh --mode iac infrastructure/modules -terraform-docs: # Terraform-docs check against Terraform files - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform fmt command, default is '-recursive'] @Quality - for dir in ./infrastructure/modules/*; do \ - if [ -d "$$dir" ]; then \ - ./scripts/terraform/terraform-docs.sh $$dir; \ - fi \ - done +terraform-docs: # Generate Terraform module documentation - optional: module=[specific module, or all if omitted] @Quality + # Example: make terraform-docs module=mymodule + # Example: make terraform-docs (generates for all modules) + @if [ -n "$(module)" ]; then \ + ./scripts/terraform/terraform-docs.sh infrastructure/modules/$(module); \ + else \ + for dir in infrastructure/modules/*; do \ + if [ -d "$$dir" ]; then \ + ./scripts/terraform/terraform-docs.sh $$dir; \ + fi; \ + done; \ + fi # ============================================================================== -# Configuration - please DO NOT edit this section! +# Cleanup + +clean:: # Remove Terraform build artifacts and cache @Operations + # Example: make clean + rm -rf infrastructure/modules/*/.terraform + rm -rf infrastructure/modules/*/.terraform.lock.hcl -terraform-install: # Install Terraform @Installation +# ============================================================================== +# Installation + +terraform-install: # Install Terraform using asdf @Installation + # Example: make terraform-install make _install-dependency name="terraform" # ============================================================================== ${VERBOSE}.SILENT: \ - _terraform \ clean \ - terraform-apply \ - terraform-destroy \ - terraform-example-clean \ - terraform-example-destroy-aws-infrastructure \ - terraform-example-provision-aws-infrastructure \ - terraform-fmt \ terraform-docs \ - terraform-init \ + terraform-fmt \ + terraform-fmt-check \ terraform-install \ - terraform-plan \ - terraform-shellscript-lint \ + terraform-sec \ terraform-validate \ + terraform-validate-all diff --git a/scripts/tests/test.mk b/scripts/tests/test.mk index aab47c6..a1f1690 100644 --- a/scripts/tests/test.mk +++ b/scripts/tests/test.mk @@ -65,12 +65,14 @@ test: # Run all the test tasks @Testing test-load _test: - set -e - script="./scripts/tests/${name}.sh" - if [ -e "$${script}" ]; then - exec $${script} - else - echo "make test-${name} not implemented: $${script} not found" >&2 + set -e; \ + script="./scripts/tests/${name}.sh"; \ + if [ -e "$${script}" ]; then \ + exec $${script}; \ + else \ + echo "test-${name}: Not currently implemented"; \ + echo "Create $${script} to implement this test target"; \ + exit 0; \ fi ${VERBOSE}.SILENT: \