├── main.tf ├── .ruby-version ├── .tool-versions ├── TODO.md ├── .rspec ├── .gitattributes ├── examples └── full │ ├── providers.tf │ ├── terraform.tf │ ├── variables.tf │ ├── prerequisites.tf │ ├── base_networking.tf │ ├── .terraform.lock.hcl │ └── outputs.tf ├── spec ├── unit │ ├── infra │ │ ├── root │ │ │ ├── provider.tf │ │ │ ├── terraform.tf │ │ │ ├── variables.tf │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── .terraform.lock.hcl │ │ └── prerequisites │ │ │ ├── provider.tf │ │ │ ├── variables.tf │ │ │ ├── terraform.tf │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── .terraform.lock.hcl │ ├── support │ │ └── matchers.rb │ ├── spec_helper.rb │ ├── igw_spec.rb │ ├── vpc_spec.rb │ ├── public_subnets_spec.rb │ ├── nat_gateways_spec.rb │ └── private_subnets_spec.rb └── integration │ ├── spec_helper.rb │ ├── availability_zone_addition_spec.rb │ └── full_spec.rb ├── docs ├── architecture.png └── architecture.graffle ├── .circleci ├── gpg.private.enc └── config.yml ├── config ├── secrets │ ├── ci │ │ ├── gpg.public │ │ ├── ssh.public │ │ ├── gpg.private │ │ ├── ssh.private │ │ ├── aws-credentials.sh │ │ └── encryption.passphrase │ ├── github │ │ └── config.yaml │ └── circle_ci │ │ └── config.yaml ├── hiera.yaml ├── roles │ ├── prerequisites.yaml │ ├── root.yaml │ └── full.yaml ├── defaults.yaml └── gpg │ ├── liam.gpg.public │ ├── jonas.gpg.public │ └── toby.gpg.public ├── account.tf ├── scripts └── ci │ ├── common │ ├── install-orb-deps.sh │ ├── install-git-crypt.sh │ ├── configure-git.sh │ └── install-gpg-key.sh │ └── steps │ ├── build.sh │ ├── merge-pull-request.sh │ ├── prerelease.sh │ ├── release.sh │ └── test.sh ├── .git-crypt ├── .gitattributes └── keys │ └── default │ └── 0 │ ├── 41D2606F66C3FF28874362B61A16916844CE9D82.gpg │ ├── 933E3994686DC15C99D1369844037399AEDB1D8D.gpg │ ├── B925B9FB62A8C7C7F3400B2EB663FA80F1D77081.gpg │ └── D164A61C69E23C0F74475FBE5FFE76AD095FCA07.gpg ├── terraform.tf ├── .envrc ├── .github └── dependabot.yml ├── igw.tf ├── Gemfile ├── .gitignore ├── lib ├── paths.rb └── version.rb ├── vpc.tf ├── .rubocop.yml ├── nat.tf ├── LICENSE.txt ├── public_subnets.tf ├── private_subnets.tf ├── go ├── defaults.tf ├── outputs.tf ├── variables.tf ├── CODE_OF_CONDUCT.md ├── CHANGELOG.md ├── Rakefile ├── README.md └── Gemfile.lock /main.tf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.1.1 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 3.1.1 2 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * Add network ACLS. 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | config/secrets/** filter=git-crypt diff=git-crypt 2 | -------------------------------------------------------------------------------- /examples/full/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /spec/unit/infra/root/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" {} 2 | 3 | variable "domain_name" {} 4 | -------------------------------------------------------------------------------- /docs/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/docs/architecture.png -------------------------------------------------------------------------------- /.circleci/gpg.private.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/.circleci/gpg.private.enc -------------------------------------------------------------------------------- /docs/architecture.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/docs/architecture.graffle -------------------------------------------------------------------------------- /config/secrets/ci/gpg.public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/config/secrets/ci/gpg.public -------------------------------------------------------------------------------- /config/secrets/ci/ssh.public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/config/secrets/ci/ssh.public -------------------------------------------------------------------------------- /config/secrets/ci/gpg.private: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/config/secrets/ci/gpg.private -------------------------------------------------------------------------------- /config/secrets/ci/ssh.private: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/config/secrets/ci/ssh.private -------------------------------------------------------------------------------- /config/secrets/github/config.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/config/secrets/github/config.yaml -------------------------------------------------------------------------------- /account.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" {} 2 | 3 | locals { 4 | current_account_id = data.aws_caller_identity.current.account_id 5 | } 6 | -------------------------------------------------------------------------------- /config/secrets/ci/aws-credentials.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/config/secrets/ci/aws-credentials.sh -------------------------------------------------------------------------------- /config/secrets/circle_ci/config.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/config/secrets/circle_ci/config.yaml -------------------------------------------------------------------------------- /config/secrets/ci/encryption.passphrase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/config/secrets/ci/encryption.passphrase -------------------------------------------------------------------------------- /scripts/ci/common/install-orb-deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | apt-get update 8 | apt-get install -y --no-install-recommends jq 9 | -------------------------------------------------------------------------------- /.git-crypt/.gitattributes: -------------------------------------------------------------------------------- 1 | # Do not edit this file. To specify the files to encrypt, create your own 2 | # .gitattributes file in the directory where your files are. 3 | * !filter !diff 4 | *.gpg binary 5 | -------------------------------------------------------------------------------- /terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 3.29" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/41D2606F66C3FF28874362B61A16916844CE9D82.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/.git-crypt/keys/default/0/41D2606F66C3FF28874362B61A16916844CE9D82.gpg -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/933E3994686DC15C99D1369844037399AEDB1D8D.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/.git-crypt/keys/default/0/933E3994686DC15C99D1369844037399AEDB1D8D.gpg -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/B925B9FB62A8C7C7F3400B2EB663FA80F1D77081.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/.git-crypt/keys/default/0/B925B9FB62A8C7C7F3400B2EB663FA80F1D77081.gpg -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/D164A61C69E23C0F74475FBE5FFE76AD095FCA07.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/HEAD/.git-crypt/keys/default/0/D164A61C69E23C0F74475FBE5FFE76AD095FCA07.gpg -------------------------------------------------------------------------------- /config/hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :backends: 3 | - "env" 4 | - "overrides" 5 | - "yaml" 6 | :logger: "noop" 7 | :yaml: 8 | :datadir: "config" 9 | :hierarchy: 10 | - "roles/%{role}" 11 | - "defaults" 12 | -------------------------------------------------------------------------------- /scripts/ci/common/install-git-crypt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | apt-get update 8 | apt-get install -y --no-install-recommends git ssh git-crypt 9 | -------------------------------------------------------------------------------- /examples/full/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "4.33" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /spec/unit/infra/root/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "4.33" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/ci/common/configure-git.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | git config --global user.email "circleci@infrablocks.io" 8 | git config --global user.name "Circle CI" 9 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "4.33" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PROJECT_DIR="$(pwd)" 4 | 5 | PATH_add "${PROJECT_DIR}" 6 | PATH_add "${PROJECT_DIR}"/vendor/**/bin 7 | 8 | if has asdf; then 9 | asdf install 10 | fi 11 | 12 | layout ruby 13 | layout node 14 | -------------------------------------------------------------------------------- /config/roles/prerequisites.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | configuration_directory: "%{cwd}/spec/unit/infra/prerequisites" 3 | state_file: "%{cwd}/state/prerequisites.tfstate" 4 | vars: 5 | region: "%{hiera('region')}" 6 | 7 | domain_name: "%{hiera('domain_name')}" 8 | -------------------------------------------------------------------------------- /examples/full/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" {} 2 | 3 | variable "component" {} 4 | variable "deployment_identifier" {} 5 | 6 | variable "availability_zones" { 7 | type = list(string) 8 | } 9 | variable "vpc_cidr" {} 10 | 11 | variable "domain_name" {} 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "terraform" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | 9 | - package-ecosystem: "bundler" 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | 14 | -------------------------------------------------------------------------------- /config/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | region: 'eu-west-2' 3 | availability_zones: 4 | - 'eu-west-2a' 5 | - 'eu-west-2b' 6 | 7 | vpc_cidr: '10.1.0.0/16' 8 | 9 | component: 'test' 10 | deployment_identifier: "%{hiera('seed')}" 11 | 12 | domain_name: "infrablocks-base-networking.example" 13 | -------------------------------------------------------------------------------- /scripts/ci/steps/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | PROJECT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" 9 | 10 | cd "$PROJECT_DIR" 11 | 12 | ./go test:code:check 13 | -------------------------------------------------------------------------------- /igw.tf: -------------------------------------------------------------------------------- 1 | resource "aws_internet_gateway" "base_igw" { 2 | vpc_id = aws_vpc.base.id 3 | 4 | tags = { 5 | Name = "igw-${var.component}-${var.deployment_identifier}" 6 | Component = var.component 7 | DeploymentIdentifier = var.deployment_identifier 8 | Tier = "public" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/full/prerequisites.tf: -------------------------------------------------------------------------------- 1 | resource "aws_default_vpc" "default" {} 2 | 3 | module "dns_zones" { 4 | source = "infrablocks/dns-zones/aws" 5 | version = "2.0.0" 6 | 7 | domain_name = var.domain_name 8 | private_domain_name = var.domain_name 9 | private_zone_vpc_id = aws_default_vpc.default.id 10 | private_zone_vpc_region = var.region 11 | } 12 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_default_vpc" "default" {} 2 | 3 | module "dns_zones" { 4 | source = "infrablocks/dns-zones/aws" 5 | version = "2.0.0" 6 | 7 | domain_name = var.domain_name 8 | private_domain_name = var.domain_name 9 | private_zone_vpc_id = aws_default_vpc.default.id 10 | private_zone_vpc_region = var.region 11 | } 12 | -------------------------------------------------------------------------------- /config/roles/root.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | configuration_directory: "%{cwd}/spec/unit/infra/root" 3 | state_file: "%{cwd}/state/root.tfstate" 4 | vars: 5 | region: "%{hiera('region')}" 6 | availability_zones: "%{hiera('availability_zones')}" 7 | 8 | vpc_cidr: "%{hiera('vpc_cidr')}" 9 | 10 | component: "%{hiera('component')}" 11 | deployment_identifier: "%{hiera('deployment_identifier')}" 12 | -------------------------------------------------------------------------------- /config/roles/full.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | configuration_directory: "%{cwd}/examples/full" 3 | state_file: "%{cwd}/state/full.tfstate" 4 | vars: 5 | region: "%{hiera('region')}" 6 | availability_zones: "%{hiera('availability_zones')}" 7 | vpc_cidr: "%{hiera('vpc_cidr')}" 8 | 9 | component: "%{hiera('component')}" 10 | deployment_identifier: "%{hiera('deployment_identifier')}" 11 | 12 | domain_name: "%{hiera('domain_name')}" 13 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_zone_id" { 2 | value = module.dns_zones.public_zone_id 3 | } 4 | 5 | output "public_zone_name_servers" { 6 | value = module.dns_zones.public_zone_name_servers 7 | } 8 | 9 | output "private_zone_id" { 10 | value = module.dns_zones.private_zone_id 11 | } 12 | 13 | output "private_zone_name_servers" { 14 | value = module.dns_zones.private_zone_name_servers 15 | } 16 | -------------------------------------------------------------------------------- /scripts/ci/steps/merge-pull-request.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | PROJECT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" 9 | 10 | cd "$PROJECT_DIR" 11 | 12 | git-crypt unlock 13 | 14 | CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) 15 | 16 | ./go github:pull_requests:merge["$CURRENT_BRANCH","%s [skip ci]"] 17 | -------------------------------------------------------------------------------- /examples/full/base_networking.tf: -------------------------------------------------------------------------------- 1 | module "base_networking" { 2 | source = "../../" 3 | 4 | component = var.component 5 | deployment_identifier = var.deployment_identifier 6 | 7 | region = var.region 8 | availability_zones = var.availability_zones 9 | vpc_cidr = var.vpc_cidr 10 | 11 | dependencies = ["other_vpc_1", "other_vpc_2"] 12 | 13 | private_zone_id = module.dns_zones.private_zone_id 14 | } 15 | -------------------------------------------------------------------------------- /scripts/ci/common/install-gpg-key.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | PROJECT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" 9 | 10 | cd "$PROJECT_DIR" 11 | 12 | set +e 13 | openssl version 14 | openssl aes-256-cbc \ 15 | -d \ 16 | -md sha1 \ 17 | -in ./.circleci/gpg.private.enc \ 18 | -k "${ENCRYPTION_PASSPHRASE}" | gpg --import - 19 | set -e 20 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'awspec' 6 | gem 'confidante' 7 | gem 'git' 8 | gem 'netaddr' 9 | gem 'net-ssh' 10 | gem 'rake' 11 | gem 'rake_circle_ci' 12 | gem 'rake_git' 13 | gem 'rake_git_crypt' 14 | gem 'rake_github' 15 | gem 'rake_gpg' 16 | gem 'rake_ssh' 17 | gem 'rake_terraform' 18 | gem 'rspec' 19 | gem 'rspec-terraform' 20 | gem 'rubocop' 21 | gem 'rubocop-rake' 22 | gem 'rubocop-rspec' 23 | gem 'rubyzip' 24 | gem 'semantic' 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vim 2 | *.swp 3 | *.swo 4 | 5 | # IntelliJ 6 | .idea/ 7 | *.ipr 8 | *.iml 9 | *.iws 10 | 11 | # Build 12 | vendor/ 13 | build/ 14 | dist/ 15 | .bundle 16 | .rakeTasks 17 | .direnv 18 | 19 | # OS 20 | .DS_Store 21 | 22 | # Temporary 23 | run/pids/ 24 | run/logs/ 25 | *.log 26 | .tmp 27 | 28 | # RSpec 29 | .rspec_status 30 | 31 | # Terraform 32 | state/ 33 | .terraform 34 | *.tfplan 35 | *.tfstate 36 | *.tfstate.backup 37 | 38 | # Test run files 39 | spec/integration/test_runs/**/* 40 | -------------------------------------------------------------------------------- /scripts/ci/steps/prerelease.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | PROJECT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" 9 | 10 | cd "$PROJECT_DIR" 11 | 12 | set +e 13 | openssl version 14 | openssl aes-256-cbc \ 15 | -d \ 16 | -md sha1 \ 17 | -in ./.circleci/gpg.private.enc \ 18 | -k "${ENCRYPTION_PASSPHRASE}" | gpg --import - 19 | set -e 20 | 21 | git crypt unlock 22 | 23 | ./go version:bump[rc] 24 | -------------------------------------------------------------------------------- /scripts/ci/steps/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | PROJECT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" 9 | 10 | cd "$PROJECT_DIR" 11 | 12 | set +e 13 | openssl version 14 | openssl aes-256-cbc \ 15 | -d \ 16 | -md sha1 \ 17 | -in ./.circleci/gpg.private.enc \ 18 | -k "${ENCRYPTION_PASSPHRASE}" | gpg --import - 19 | set -e 20 | 21 | git crypt unlock 22 | 23 | ./go version:release 24 | -------------------------------------------------------------------------------- /lib/paths.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Paths 4 | class << self 5 | def project_root_directory 6 | join_and_expand(self_directory, '..') 7 | end 8 | 9 | def from_project_root_directory(*segments) 10 | join_and_expand(project_root_directory, *segments) 11 | end 12 | 13 | def join_and_expand(*segments) 14 | File.expand_path(join(*segments)) 15 | end 16 | 17 | def join(*segments) 18 | File.join(*segments.compact) 19 | end 20 | 21 | def self_directory 22 | File.dirname(__FILE__) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /vpc.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "base" { 2 | cidr_block = var.vpc_cidr 3 | enable_dns_hostnames = true 4 | 5 | tags = { 6 | Name = "vpc-${var.component}-${var.deployment_identifier}" 7 | Component = var.component 8 | DeploymentIdentifier = var.deployment_identifier 9 | Dependencies = join(",", local.dependencies) 10 | } 11 | } 12 | 13 | resource "aws_route53_zone_association" "base" { 14 | count = local.include_route53_zone_association == "yes" ? 1 : 0 15 | zone_id = var.private_zone_id 16 | vpc_id = aws_vpc.base.id 17 | } 18 | -------------------------------------------------------------------------------- /scripts/ci/steps/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | set -e 5 | set -o pipefail 6 | 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | PROJECT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" 9 | 10 | cd "$PROJECT_DIR" 11 | 12 | set +e 13 | openssl version 14 | openssl aes-256-cbc \ 15 | -d \ 16 | -md sha1 \ 17 | -in ./.circleci/gpg.private.enc \ 18 | -k "${ENCRYPTION_PASSPHRASE}" | gpg --import - 19 | set -e 20 | 21 | git crypt unlock 22 | 23 | source config/secrets/ci/aws-credentials.sh 24 | 25 | ./go test:unit 26 | ./go test:integration 27 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-rake 3 | - rubocop-rspec 4 | 5 | AllCops: 6 | NewCops: enable 7 | 8 | Layout/LineLength: 9 | Max: 80 10 | 11 | Metrics/BlockLength: 12 | AllowedMethods: 13 | - describe 14 | - context 15 | - shared_examples 16 | - it 17 | Exclude: 18 | - Rakefile 19 | 20 | Style/Documentation: 21 | Enabled: false 22 | 23 | RSpec/ExampleLength: 24 | Max: 40 25 | 26 | RSpec/DescribeClass: 27 | Enabled: false 28 | 29 | RSpec/InstanceVariable: 30 | Enabled: false 31 | 32 | RSpec/BeforeAfterAll: 33 | Enabled: false 34 | 35 | RSpec/MultipleMemoizedHelpers: 36 | Max: 10 37 | -------------------------------------------------------------------------------- /spec/unit/support/matchers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec::Matchers.define :a_cidr_block_within do |parent_cidr_block| 4 | def of_size(size) 5 | @size = size 6 | self 7 | end 8 | 9 | match do |child_cidr_block| 10 | parent_cidr = NetAddr::IPv4Net.parse(parent_cidr_block) 11 | child_cidr = NetAddr::IPv4Net.parse(child_cidr_block) 12 | 13 | parent_relationship = parent_cidr.rel(child_cidr) 14 | 15 | if @size 16 | netmask = NetAddr::Mask32.parse(@size) 17 | netmask_difference = child_cidr.netmask.cmp(netmask) 18 | 19 | return parent_relationship == 1 && netmask_difference == 0 20 | end 21 | 22 | return parent_relationship == 1 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/unit/infra/root/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" {} 2 | variable "availability_zones" { 3 | type = list(string) 4 | } 5 | variable "vpc_cidr" {} 6 | 7 | variable "component" {} 8 | variable "deployment_identifier" {} 9 | 10 | variable "public_subnets_offset" { 11 | type = number 12 | default = null 13 | } 14 | variable "private_subnets_offset" { 15 | type = number 16 | default = null 17 | } 18 | 19 | variable "dependencies" { 20 | type = list(string) 21 | default = null 22 | } 23 | 24 | variable "private_zone_id" { 25 | default = null 26 | } 27 | 28 | variable "include_route53_zone_association" { 29 | default = null 30 | } 31 | variable "include_nat_gateways" { 32 | default = null 33 | } 34 | -------------------------------------------------------------------------------- /nat.tf: -------------------------------------------------------------------------------- 1 | resource "aws_eip" "nat" { 2 | for_each = local.include_nat_gateways == "yes" ? local.az_map : {} 3 | 4 | vpc = true 5 | 6 | tags = { 7 | Name = "eip-nat-${var.component}-${var.deployment_identifier}-${each.value.zone}" 8 | Component = var.component 9 | DeploymentIdentifier = var.deployment_identifier 10 | } 11 | } 12 | 13 | resource "aws_nat_gateway" "base" { 14 | for_each = local.include_nat_gateways == "yes" ? local.az_map : {} 15 | 16 | allocation_id = aws_eip.nat[each.key].id 17 | subnet_id = aws_subnet.public[each.key].id 18 | 19 | depends_on = [ 20 | aws_internet_gateway.base_igw 21 | ] 22 | 23 | tags = { 24 | Name = "nat-${var.component}-${var.deployment_identifier}-${each.value.zone}" 25 | Component = var.component 26 | DeploymentIdentifier = var.deployment_identifier 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spec/unit/infra/root/main.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "prerequisites" { 2 | backend = "local" 3 | 4 | config = { 5 | path = "${path.module}/../../../../state/prerequisites.tfstate" 6 | } 7 | } 8 | 9 | module "base_network" { 10 | source = "../../../.." 11 | 12 | vpc_cidr = var.vpc_cidr 13 | region = var.region 14 | availability_zones = var.availability_zones 15 | 16 | public_subnets_offset = var.public_subnets_offset 17 | private_subnets_offset = var.private_subnets_offset 18 | 19 | component = var.component 20 | deployment_identifier = var.deployment_identifier 21 | dependencies = var.dependencies 22 | 23 | private_zone_id = data.terraform_remote_state.prerequisites.outputs.private_zone_id 24 | 25 | include_route53_zone_association = var.include_route53_zone_association 26 | include_nat_gateways = var.include_nat_gateways 27 | } 28 | -------------------------------------------------------------------------------- /spec/unit/infra/root/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | value = module.base_network.vpc_id 3 | } 4 | 5 | output "vpc_cidr" { 6 | value = module.base_network.vpc_cidr 7 | } 8 | 9 | output "availability_zones" { 10 | value = module.base_network.availability_zones 11 | } 12 | 13 | output "number_of_availability_zones" { 14 | value = module.base_network.number_of_availability_zones 15 | } 16 | 17 | output "public_subnet_ids" { 18 | value = module.base_network.public_subnet_ids 19 | } 20 | 21 | output "public_subnet_cidr_blocks" { 22 | value = module.base_network.public_subnet_cidr_blocks 23 | } 24 | 25 | output "public_route_table_ids" { 26 | value = module.base_network.public_route_table_ids 27 | } 28 | 29 | output "private_subnet_ids" { 30 | value = module.base_network.private_subnet_ids 31 | } 32 | 33 | output "private_subnet_cidr_blocks" { 34 | value = module.base_network.private_subnet_cidr_blocks 35 | } 36 | 37 | output "private_route_table_ids" { 38 | value = module.base_network.private_route_table_ids 39 | } 40 | 41 | output "nat_public_ips" { 42 | value = module.base_network.nat_public_ips 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 InfraBlocks Maintainers 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Semantic 4 | module Extensions 5 | def release! 6 | unless prerelease? 7 | raise 'Error: no pre segment, ' \ 8 | 'this version is not a pre-release version.' 9 | end 10 | 11 | new_version = clone 12 | new_version.build = new_version.pre = nil 13 | new_version 14 | end 15 | 16 | def rc! 17 | return start_rc if release? 18 | return increment_rc if rc? 19 | 20 | raise "Error: pre segment '#{pre}' does not look like 'rc.n'." 21 | end 22 | 23 | private 24 | 25 | def start_rc 26 | new_version = clone 27 | new_version = new_version.increment!(:minor) 28 | new_version.pre = 'rc.1' 29 | new_version 30 | end 31 | 32 | def increment_rc 33 | new_version = clone 34 | new_version.pre = "rc.#{Integer(new_version.pre.delete('rc.')) + 1}" 35 | new_version 36 | end 37 | 38 | def release? 39 | pre.nil? 40 | end 41 | 42 | def prerelease? 43 | !release? 44 | end 45 | 46 | def rc? 47 | pre =~ /^rc\.\d+$/ 48 | end 49 | end 50 | 51 | class Version 52 | prepend Extensions 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /examples/full/.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 = "4.33.0" 6 | constraints = ">= 3.29.0, 4.33.0" 7 | hashes = [ 8 | "h1:0S9ZXYg6K0CTOJUTQnoH94YrKuOYyJYEcc+hN5qGafA=", 9 | "h1:rLRYOeKvU17Tky5dleZwTPRoWtbdFTG/jOF/fTP2otY=", 10 | "zh:421b24e21d7fac4d65d97438d2c0a4effe71d3a1bd15820d6fde2879e49fe817", 11 | "zh:4378a84ca8e2a6990f47abc24367b801e884be928671b37ad7b8e7b656f73e48", 12 | "zh:54e0d7884edf3cefd096715794d32b6532138dca905f0b2fe84fb2117594293c", 13 | "zh:6269a7d0312057db5ded669e9f7f9bd80fb6dcb549b50d8d7f3f3b2a0361b8a5", 14 | "zh:67f57d16aa3db493a3174c3c5f30385c7af9767c4e3cdca14e5a4bf384ff59d9", 15 | "zh:7d4d4a1d963e431ffdc3348e3a578d3ba0fa782b1f4bf55fd5c0e527d24fed81", 16 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 17 | "zh:cd8e3d32485acb49c1b06f63916fec8e73a4caa6cf88ae9c4bf236d6f5d9b914", 18 | "zh:d586fd01195bd3775346495e61806e79b6012e745dc05e31a30b958acf968abe", 19 | "zh:d76122060f25ab87887a743096a42d47ba091c2c019ac13ce6b3973b2babe5a3", 20 | "zh:e917d36fe18eddc42ec743b3152b4dcb4853b75ea7a679abd19bdf271bc48221", 21 | "zh:eb780860d5c04f43a018aef564e76a2d84e9aa68984fa1f968ca8c09d23a611a", 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /spec/unit/infra/root/.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 = "4.33.0" 6 | constraints = ">= 3.29.0, 4.33.0" 7 | hashes = [ 8 | "h1:0S9ZXYg6K0CTOJUTQnoH94YrKuOYyJYEcc+hN5qGafA=", 9 | "h1:rLRYOeKvU17Tky5dleZwTPRoWtbdFTG/jOF/fTP2otY=", 10 | "zh:421b24e21d7fac4d65d97438d2c0a4effe71d3a1bd15820d6fde2879e49fe817", 11 | "zh:4378a84ca8e2a6990f47abc24367b801e884be928671b37ad7b8e7b656f73e48", 12 | "zh:54e0d7884edf3cefd096715794d32b6532138dca905f0b2fe84fb2117594293c", 13 | "zh:6269a7d0312057db5ded669e9f7f9bd80fb6dcb549b50d8d7f3f3b2a0361b8a5", 14 | "zh:67f57d16aa3db493a3174c3c5f30385c7af9767c4e3cdca14e5a4bf384ff59d9", 15 | "zh:7d4d4a1d963e431ffdc3348e3a578d3ba0fa782b1f4bf55fd5c0e527d24fed81", 16 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 17 | "zh:cd8e3d32485acb49c1b06f63916fec8e73a4caa6cf88ae9c4bf236d6f5d9b914", 18 | "zh:d586fd01195bd3775346495e61806e79b6012e745dc05e31a30b958acf968abe", 19 | "zh:d76122060f25ab87887a743096a42d47ba091c2c019ac13ce6b3973b2babe5a3", 20 | "zh:e917d36fe18eddc42ec743b3152b4dcb4853b75ea7a679abd19bdf271bc48221", 21 | "zh:eb780860d5c04f43a018aef564e76a2d84e9aa68984fa1f968ca8c09d23a611a", 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/.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 = "4.33.0" 6 | constraints = ">= 3.29.0, 4.33.0" 7 | hashes = [ 8 | "h1:0S9ZXYg6K0CTOJUTQnoH94YrKuOYyJYEcc+hN5qGafA=", 9 | "h1:rLRYOeKvU17Tky5dleZwTPRoWtbdFTG/jOF/fTP2otY=", 10 | "zh:421b24e21d7fac4d65d97438d2c0a4effe71d3a1bd15820d6fde2879e49fe817", 11 | "zh:4378a84ca8e2a6990f47abc24367b801e884be928671b37ad7b8e7b656f73e48", 12 | "zh:54e0d7884edf3cefd096715794d32b6532138dca905f0b2fe84fb2117594293c", 13 | "zh:6269a7d0312057db5ded669e9f7f9bd80fb6dcb549b50d8d7f3f3b2a0361b8a5", 14 | "zh:67f57d16aa3db493a3174c3c5f30385c7af9767c4e3cdca14e5a4bf384ff59d9", 15 | "zh:7d4d4a1d963e431ffdc3348e3a578d3ba0fa782b1f4bf55fd5c0e527d24fed81", 16 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 17 | "zh:cd8e3d32485acb49c1b06f63916fec8e73a4caa6cf88ae9c4bf236d6f5d9b914", 18 | "zh:d586fd01195bd3775346495e61806e79b6012e745dc05e31a30b958acf968abe", 19 | "zh:d76122060f25ab87887a743096a42d47ba091c2c019ac13ce6b3973b2babe5a3", 20 | "zh:e917d36fe18eddc42ec743b3152b4dcb4853b75ea7a679abd19bdf271bc48221", 21 | "zh:eb780860d5c04f43a018aef564e76a2d84e9aa68984fa1f968ca8c09d23a611a", 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /public_subnets.tf: -------------------------------------------------------------------------------- 1 | resource "aws_subnet" "public" { 2 | for_each = local.az_map 3 | 4 | vpc_id = aws_vpc.base.id 5 | cidr_block = each.value.public_subnet_cidr 6 | availability_zone = each.value.zone 7 | 8 | tags = { 9 | Name = "public-subnet-${var.component}-${var.deployment_identifier}-${each.value.zone}" 10 | Component = var.component 11 | DeploymentIdentifier = var.deployment_identifier 12 | Tier = "public" 13 | } 14 | } 15 | 16 | resource "aws_route_table" "public" { 17 | for_each = local.az_map 18 | 19 | vpc_id = aws_vpc.base.id 20 | 21 | tags = { 22 | Name = "public-routetable-${var.component}-${var.deployment_identifier}-${each.value.zone}" 23 | Component = var.component 24 | DeploymentIdentifier = var.deployment_identifier 25 | Tier = "public" 26 | } 27 | } 28 | 29 | resource "aws_route" "public_internet" { 30 | for_each = local.az_map 31 | 32 | route_table_id = aws_route_table.public[each.key].id 33 | gateway_id = aws_internet_gateway.base_igw.id 34 | destination_cidr_block = "0.0.0.0/0" 35 | } 36 | 37 | resource "aws_route_table_association" "public" { 38 | for_each = local.az_map 39 | 40 | subnet_id = aws_subnet.public[each.key].id 41 | route_table_id = aws_route_table.public[each.key].id 42 | } 43 | -------------------------------------------------------------------------------- /examples/full/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_zone_id" { 2 | value = module.dns_zones.public_zone_id 3 | } 4 | 5 | output "private_zone_id" { 6 | value = module.dns_zones.private_zone_id 7 | } 8 | 9 | output "vpc_id" { 10 | value = module.base_networking.vpc_id 11 | } 12 | 13 | output "vpc_cidr" { 14 | value = module.base_networking.vpc_cidr 15 | } 16 | 17 | output "availability_zones" { 18 | value = module.base_networking.availability_zones 19 | } 20 | 21 | output "number_of_availability_zones" { 22 | value = module.base_networking.number_of_availability_zones 23 | } 24 | 25 | output "public_subnet_ids" { 26 | value = module.base_networking.public_subnet_ids 27 | } 28 | 29 | output "public_subnet_cidr_blocks" { 30 | value = module.base_networking.public_subnet_cidr_blocks 31 | } 32 | 33 | output "public_route_table_ids" { 34 | value = module.base_networking.public_route_table_ids 35 | } 36 | 37 | output "private_subnet_ids" { 38 | value = module.base_networking.private_subnet_ids 39 | } 40 | 41 | output "private_subnet_cidr_blocks" { 42 | value = module.base_networking.private_subnet_cidr_blocks 43 | } 44 | 45 | output "private_route_table_ids" { 46 | value = module.base_networking.private_route_table_ids 47 | } 48 | 49 | output "nat_public_ips" { 50 | value = module.base_networking.nat_public_ips 51 | } 52 | 53 | output "internet_gateway_id" { 54 | value = module.base_networking.internet_gateway_id 55 | } 56 | -------------------------------------------------------------------------------- /spec/integration/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/setup' 4 | 5 | require 'awspec' 6 | require 'rspec' 7 | require 'ruby_terraform' 8 | require 'rspec/terraform' 9 | require 'logger' 10 | require 'stringio' 11 | 12 | Dir[File.join(__dir__, 'support', '**', '*.rb')] 13 | .each { |f| require f } 14 | 15 | RSpec.configure do |config| 16 | config.filter_run_when_matching :focus 17 | config.example_status_persistence_file_path = '.rspec_status' 18 | config.expect_with(:rspec) { |c| c.syntax = :expect } 19 | 20 | config.include(Awspec::Helper::Finder) 21 | 22 | config.terraform_binary = 'vendor/terraform/bin/terraform' 23 | config.terraform_log_file_path = 'build/logs/integration.log' 24 | config.terraform_log_streams = [:file] 25 | config.terraform_configuration_provider = 26 | RSpec::Terraform::Configuration.chain_provider( 27 | providers: [ 28 | RSpec::Terraform::Configuration.seed_provider( 29 | generator: -> { SecureRandom.hex[0, 8] } 30 | ), 31 | RSpec::Terraform::Configuration.in_memory_provider( 32 | no_color: true 33 | ), 34 | RSpec::Terraform::Configuration.confidante_provider( 35 | parameters: %i[ 36 | configuration_directory 37 | state_file 38 | vars 39 | ], 40 | scope_selector: ->(o) { o.slice(:role) } 41 | ) 42 | ] 43 | ) 44 | end 45 | -------------------------------------------------------------------------------- /private_subnets.tf: -------------------------------------------------------------------------------- 1 | resource "aws_subnet" "private" { 2 | for_each = local.az_map 3 | 4 | vpc_id = aws_vpc.base.id 5 | cidr_block = each.value.private_subnet_cidr 6 | availability_zone = each.value.zone 7 | 8 | tags = { 9 | Name = "private-subnet-${var.component}-${var.deployment_identifier}-${each.value.zone}" 10 | Component = var.component 11 | DeploymentIdentifier = var.deployment_identifier 12 | Tier = "private" 13 | } 14 | } 15 | 16 | resource "aws_route_table" "private" { 17 | for_each = local.az_map 18 | 19 | vpc_id = aws_vpc.base.id 20 | 21 | tags = { 22 | Name = "private-routetable-${var.component}-${var.deployment_identifier}-${each.value.zone}" 23 | Component = var.component 24 | DeploymentIdentifier = var.deployment_identifier 25 | Tier = "private" 26 | } 27 | } 28 | 29 | resource "aws_route" "private_internet" { 30 | for_each = local.include_nat_gateways == "yes" ? local.az_map : {} 31 | 32 | route_table_id = aws_route_table.private[each.key].id 33 | nat_gateway_id = aws_nat_gateway.base[each.key].id 34 | destination_cidr_block = "0.0.0.0/0" 35 | } 36 | 37 | resource "aws_route_table_association" "private" { 38 | for_each = local.az_map 39 | 40 | subnet_id = aws_subnet.private[each.key].id 41 | route_table_id = aws_route_table.private[each.key].id 42 | } 43 | -------------------------------------------------------------------------------- /spec/unit/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/setup' 4 | 5 | require 'awspec' 6 | require 'rspec' 7 | require 'ruby_terraform' 8 | require 'rspec/terraform' 9 | require 'logger' 10 | require 'stringio' 11 | 12 | Dir[File.join(__dir__, 'support', '**', '*.rb')] 13 | .each { |f| require f } 14 | 15 | RSpec.configure do |config| 16 | config.filter_run_when_matching :focus 17 | config.example_status_persistence_file_path = '.rspec_status' 18 | config.expect_with(:rspec) { |c| c.syntax = :expect } 19 | 20 | config.include(Awspec::Helper::Finder) 21 | 22 | config.terraform_binary = 'vendor/terraform/bin/terraform' 23 | config.terraform_log_file_path = 'build/logs/unit.log' 24 | config.terraform_log_streams = [:file] 25 | config.terraform_configuration_provider = 26 | RSpec::Terraform::Configuration.chain_provider( 27 | providers: [ 28 | RSpec::Terraform::Configuration.seed_provider( 29 | generator: -> { SecureRandom.hex[0, 8] } 30 | ), 31 | RSpec::Terraform::Configuration.in_memory_provider( 32 | no_color: true 33 | ), 34 | RSpec::Terraform::Configuration.confidante_provider( 35 | parameters: %i[ 36 | configuration_directory 37 | state_file 38 | vars 39 | ], 40 | scope_selector: ->(o) { o.slice(:role) } 41 | ) 42 | ] 43 | ) 44 | 45 | config.before(:suite) do 46 | apply( 47 | role: :prerequisites 48 | ) 49 | end 50 | config.after(:suite) do 51 | destroy( 52 | role: :prerequisites, 53 | only_if: -> { !ENV['FORCE_DESTROY'].nil? || ENV['SEED'].nil? } 54 | ) 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /go: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -n "$GO_DEBUG" ] && set -x 4 | set -e 5 | 6 | project_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | verbose="no" 9 | offline="no" 10 | skip_checks="no" 11 | 12 | missing_dependency="no" 13 | 14 | [ -n "$GO_DEBUG" ] && verbose="yes" 15 | [ -n "$GO_SKIP_CHECKS" ] && skip_checks="yes" 16 | [ -n "$GO_OFFLINE" ] && offline="yes" 17 | 18 | function loose_version() { 19 | local version="$1" 20 | 21 | IFS="." read -r -a version_parts <<<"$version" 22 | 23 | echo "${version_parts[0]}.${version_parts[1]}" 24 | } 25 | 26 | function read_version() { 27 | local tool="$1" 28 | local tool_versions 29 | 30 | tool_versions="$(cat "$project_dir"/.tool-versions)" 31 | 32 | echo "$tool_versions" | grep "$tool" | cut -d ' ' -f 2 33 | } 34 | 35 | ruby_full_version="$(read_version "ruby")" 36 | ruby_loose_version="$(loose_version "$ruby_full_version")" 37 | 38 | if [[ "$skip_checks" == "no" ]]; then 39 | if ! type ruby >/dev/null 2>&1 || ! ruby -v | grep -q "$ruby_loose_version"; then 40 | echo "This codebase requires Ruby $ruby_loose_version." 41 | missing_dependency="yes" 42 | fi 43 | 44 | if [[ "$missing_dependency" = "yes" ]]; then 45 | echo "Please install missing dependencies to continue." 46 | exit 1 47 | fi 48 | 49 | echo "All system dependencies present. Continuing." 50 | fi 51 | 52 | if [[ "$offline" = "no" ]]; then 53 | echo "Installing ruby dependencies." 54 | if [[ "$verbose" = "yes" ]]; then 55 | bundle install 56 | else 57 | bundle install >/dev/null 58 | fi 59 | fi 60 | 61 | echo "Starting rake." 62 | if [[ "$verbose" = "yes" ]]; then 63 | time bundle exec rake --verbose "$@" 64 | else 65 | time bundle exec rake "$@" 66 | fi 67 | -------------------------------------------------------------------------------- /spec/unit/igw_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'IGW' do 6 | let(:component) do 7 | var(role: :root, name: 'component') 8 | end 9 | let(:deployment_identifier) do 10 | var(role: :root, name: 'deployment_identifier') 11 | end 12 | 13 | describe 'by default' do 14 | before(:context) do 15 | @plan = plan(role: :root) do |vars| 16 | vars.private_zone_id = 17 | output(role: :prerequisites, name: 'private_zone_id') 18 | end 19 | end 20 | 21 | it 'creates an internet gateway' do 22 | expect(@plan) 23 | .to(include_resource_creation(type: 'aws_internet_gateway') 24 | .once) 25 | end 26 | 27 | it 'includes a Name tag' do 28 | expect(@plan) 29 | .to(include_resource_creation(type: 'aws_internet_gateway') 30 | .with_attribute_value( 31 | :tags, 32 | a_hash_including( 33 | Name: "igw-#{component}-#{deployment_identifier}" 34 | ) 35 | )) 36 | end 37 | 38 | it 'includes Component and DeploymentIdentifier tags' do 39 | expect(@plan) 40 | .to(include_resource_creation(type: 'aws_internet_gateway') 41 | .with_attribute_value( 42 | :tags, 43 | a_hash_including( 44 | Component: component, 45 | DeploymentIdentifier: deployment_identifier 46 | ) 47 | )) 48 | end 49 | 50 | it 'includes a Tier tag' do 51 | expect(@plan) 52 | .to(include_resource_creation(type: 'aws_internet_gateway') 53 | .with_attribute_value( 54 | :tags, 55 | a_hash_including( 56 | Tier: 'public' 57 | ) 58 | )) 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /defaults.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | # default for cases when `null` value provided, meaning "use default" 3 | dependencies = var.dependencies == null ? [] : var.dependencies 4 | public_subnets_offset = var.public_subnets_offset == null ? 0 : var.public_subnets_offset 5 | private_subnets_offset = var.private_subnets_offset == null ? 0 : var.private_subnets_offset 6 | include_route53_zone_association = var.include_route53_zone_association == null ? "yes" : var.include_route53_zone_association 7 | include_nat_gateways = var.include_nat_gateways == null ? "yes" : var.include_nat_gateways 8 | 9 | # Validation: ensure only one of availability_zones or availability_zone_configurations is provided 10 | _validate_az_input = ( 11 | (var.availability_zones == null && var.availability_zone_configurations == null) || 12 | (var.availability_zones != null && var.availability_zone_configurations != null) 13 | ) ? tobool("ERROR: Exactly one of availability_zones or availability_zone_configurations must be specified.") : true 14 | 15 | # Normalize AZ configuration to a consistent format 16 | # If availability_zone_configurations is provided, use it directly 17 | # Otherwise, generate configurations from availability_zones list 18 | az_configurations = var.availability_zone_configurations != null ? var.availability_zone_configurations : [ 19 | for idx, az in var.availability_zones : { 20 | zone = az 21 | public_subnet_cidr = cidrsubnet(var.vpc_cidr, 8, idx + local.public_subnets_offset) 22 | private_subnet_cidr = cidrsubnet(var.vpc_cidr, 8, idx + local.private_subnets_offset) 23 | } 24 | ] 25 | 26 | # Create a map keyed by AZ name for easy lookup in resources 27 | az_map = { for config in local.az_configurations : config.zone => config } 28 | 29 | # List of AZ names for outputs and other references 30 | availability_zones = [for config in local.az_configurations : config.zone] 31 | } 32 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "The ID of the created VPC." 3 | value = aws_vpc.base.id 4 | } 5 | 6 | output "vpc_cidr" { 7 | description = "The CIDR of the created VPC." 8 | value = aws_vpc.base.cidr_block 9 | } 10 | 11 | output "availability_zones" { 12 | description = "The availability zones in which subnets were created." 13 | value = local.availability_zones 14 | } 15 | 16 | output "number_of_availability_zones" { 17 | description = "The number of populated availability zones available." 18 | value = length(local.availability_zones) 19 | } 20 | 21 | output "public_subnet_ids" { 22 | description = "The IDs of the public subnets." 23 | value = [for az in local.availability_zones : aws_subnet.public[az].id] 24 | } 25 | 26 | output "public_subnet_cidr_blocks" { 27 | description = "The CIDRs of the public subnets." 28 | value = [for az in local.availability_zones : aws_subnet.public[az].cidr_block] 29 | } 30 | 31 | output "public_route_table_ids" { 32 | description = "The IDs of the public route tables." 33 | value = [for az in local.availability_zones : aws_route_table.public[az].id] 34 | } 35 | 36 | output "private_subnet_ids" { 37 | description = "The IDs of the private subnets." 38 | value = [for az in local.availability_zones : aws_subnet.private[az].id] 39 | } 40 | 41 | output "private_subnet_cidr_blocks" { 42 | description = "The CIDRs of the private subnets." 43 | value = [for az in local.availability_zones : aws_subnet.private[az].cidr_block] 44 | } 45 | 46 | output "private_route_table_ids" { 47 | description = "The IDs of the private route tables." 48 | value = [for az in local.availability_zones : aws_route_table.private[az].id] 49 | } 50 | 51 | output "nat_public_ips" { 52 | description = "The EIPs attached to the NAT gateways." 53 | value = local.include_nat_gateways == "yes" ? [for az in local.availability_zones : aws_eip.nat[az].public_ip] : [] 54 | } 55 | 56 | output "internet_gateway_id" { 57 | description = "The ID of IGW attached to the VPC." 58 | value = aws_internet_gateway.base_igw.id 59 | } 60 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_cidr" { 2 | type = string 3 | description = "The CIDR to use for the VPC." 4 | } 5 | variable "region" { 6 | type = string 7 | description = "The region into which to deploy the VPC." 8 | } 9 | variable "availability_zones" { 10 | type = list(string) 11 | description = "The availability zones for which to add subnets. Mutually exclusive with availability_zone_configurations." 12 | default = null 13 | } 14 | 15 | variable "availability_zone_configurations" { 16 | type = list(object({ 17 | zone = string 18 | public_subnet_cidr = string 19 | private_subnet_cidr = string 20 | })) 21 | description = <<-DESC 22 | Explicit availability zone configurations with CIDR blocks. Mutually exclusive with availability_zones. 23 | Use this to specify exact CIDR blocks for each subnet, allowing you to add new AZs without 24 | changing existing subnet CIDR allocations. Example: [{ 25 | zone = "us-east-1a" 26 | public_subnet_cidr = "10.0.0.0/24" 27 | private_subnet_cidr = "10.0.10.0/24" 28 | }] 29 | DESC 30 | default = null 31 | } 32 | 33 | variable "component" { 34 | type = string 35 | description = "The component this network will contain." 36 | } 37 | variable "deployment_identifier" { 38 | type = string 39 | description = "An identifier for this instantiation." 40 | } 41 | variable "dependencies" { 42 | description = "A comma separated list of components depended on my this component." 43 | type = list(string) 44 | default = [] 45 | } 46 | 47 | variable "public_subnets_offset" { 48 | description = "The number of /24s to offset the public subnets in the VPC CIDR." 49 | type = number 50 | default = 0 51 | } 52 | variable "private_subnets_offset" { 53 | description = "The number of /24s to offset the private subnets in the VPC CIDR." 54 | type = number 55 | default = 128 56 | } 57 | 58 | variable "include_route53_zone_association" { 59 | description = "Whether or not to associate the VPC with a zone id (\"yes\" or \"no\")." 60 | type = string 61 | default = "yes" 62 | } 63 | 64 | variable "private_zone_id" { 65 | description = "The ID of the private Route 53 zone." 66 | type = string 67 | default = null 68 | } 69 | 70 | variable "include_nat_gateways" { 71 | description = "Whether or not to deploy NAT gateways in each availability zone for outbound Internet connectivity (\"yes\" or \"no\")." 72 | type = string 73 | default = "yes" 74 | } 75 | -------------------------------------------------------------------------------- /config/gpg/liam.gpg.public: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | 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 40 | =0Zai 41 | -----END PGP PUBLIC KEY BLOCK----- 42 | -------------------------------------------------------------------------------- /spec/integration/availability_zone_addition_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'netaddr' 5 | 6 | ORIGINAL_AVAILABILITY_ZONES = %w[eu-west-2a eu-west-2b].freeze 7 | UPDATED_AVAILABILITY_ZONES = %w[eu-west-2a eu-west-2b eu-west-2c].freeze 8 | 9 | describe 'adding additional availability zones' do 10 | let(:component) do 11 | var(role: :full, name: 'component') 12 | end 13 | let(:dep_id) do 14 | var(role: :full, name: 'deployment_identifier') 15 | end 16 | let(:domain_name) do 17 | var(role: :full, name: 'domain_name') 18 | end 19 | let(:vpc_cidr) do 20 | var(role: :full, name: 'vpc_cidr') 21 | end 22 | let(:private_subnets_offset) do 23 | var(role: :full, name: 'private_subnets_offset') 24 | end 25 | 26 | before(:context) do 27 | apply(role: :full) do |vars| 28 | vars.availability_zones = ORIGINAL_AVAILABILITY_ZONES 29 | end 30 | end 31 | 32 | after(:context) do 33 | destroy( 34 | role: :full, 35 | only_if: -> { !ENV['FORCE_DESTROY'].nil? || ENV['SEED'].nil? } 36 | ) 37 | end 38 | 39 | it 'creates the initial availability zones' do 40 | expected_availability_zones = ORIGINAL_AVAILABILITY_ZONES 41 | actual_availability_zones = output(role: :full, name: 'availability_zones') 42 | expect(actual_availability_zones).to(eq(expected_availability_zones)) 43 | end 44 | 45 | describe 'adding a new availability zone' do 46 | before(:context) do 47 | @plan = plan(role: :full) do |vars| 48 | vars.availability_zones = UPDATED_AVAILABILITY_ZONES 49 | end 50 | end 51 | 52 | def expect_addition_of_one_new_resource(state, type, name) 53 | expect(state) 54 | .not_to(include_resource_deletion(type:, name:)) 55 | expect(state) 56 | .not_to(include_resource_update(type:, name:)) 57 | expect(state) 58 | .not_to(include_resource_replacement(type:, name:)) 59 | expect(state) 60 | .to(include_resource_creation(type:, name:) 61 | .exactly(1).times) 62 | end 63 | 64 | it 'adds a single new private subnet' do 65 | expect_addition_of_one_new_resource(@plan, 'aws_subnet', 'private') 66 | end 67 | 68 | it 'adds a single new public subnet' do 69 | expect_addition_of_one_new_resource(@plan, 'aws_subnet', 'public') 70 | end 71 | 72 | it 'adds a single new nat gateway' do 73 | expect_addition_of_one_new_resource(@plan, 'aws_nat_gateway', 'base') 74 | end 75 | 76 | it 'adds a single new eip' do 77 | expect_addition_of_one_new_resource(@plan, 'aws_eip', 'nat') 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at maintainers@infrablocks.io. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an 62 | incident. Further details of specific enforcement policies may be posted 63 | separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 72 | version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 73 | 74 | [homepage]: http://contributor-covenant.org 75 | 76 | [version]: http://contributor-covenant.org/version/1/4/ 77 | -------------------------------------------------------------------------------- /config/gpg/jonas.gpg.public: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | 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 59 | =e5EY 60 | -----END PGP PUBLIC KEY BLOCK----- -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | queue: eddiewebb/queue@1.6.4 5 | slack: circleci/slack@4.8.3 6 | 7 | defaults: &defaults 8 | docker: 9 | - image: ruby:3.1.1 10 | 11 | slack_context: &slack_context 12 | context: 13 | - slack 14 | 15 | only_main: &only_main 16 | filters: 17 | branches: 18 | only: 19 | - main 20 | 21 | only_dependabot: &only_dependabot 22 | filters: 23 | branches: 24 | only: 25 | - /^dependabot.*/ 26 | 27 | only_main_and_dependabot: &only_main_and_dependabot 28 | filters: 29 | branches: 30 | only: 31 | - main 32 | - /^dependabot.*/ 33 | 34 | commands: 35 | notify: 36 | steps: 37 | - when: 38 | condition: 39 | matches: 40 | pattern: "^dependabot.*" 41 | value: << pipeline.git.branch >> 42 | steps: 43 | - slack/notify: 44 | event: fail 45 | channel: builds-dependabot 46 | template: SLACK_FAILURE_NOTIFICATION 47 | - slack/notify: 48 | event: pass 49 | channel: builds-dependabot 50 | template: SLACK_SUCCESS_NOTIFICATION 51 | - when: 52 | condition: 53 | matches: 54 | pattern: "^(?!dependabot).*" 55 | value: << pipeline.git.branch >> 56 | steps: 57 | - slack/notify: 58 | event: fail 59 | channel: dev 60 | template: SLACK_FAILURE_NOTIFICATION 61 | - slack/notify: 62 | event: pass 63 | channel: builds 64 | template: SLACK_SUCCESS_NOTIFICATION 65 | configure_tools: 66 | steps: 67 | - run: ./scripts/ci/common/install-git-crypt.sh 68 | - run: ./scripts/ci/common/install-gpg-key.sh 69 | - run: ./scripts/ci/common/install-orb-deps.sh 70 | - run: ./scripts/ci/common/configure-git.sh 71 | 72 | jobs: 73 | build: 74 | <<: *defaults 75 | steps: 76 | - checkout 77 | - configure_tools 78 | - queue/until_front_of_line: 79 | consider-branch: false 80 | - run: ./scripts/ci/steps/build.sh 81 | - notify 82 | 83 | test: 84 | <<: *defaults 85 | steps: 86 | - checkout 87 | - configure_tools 88 | - run: 89 | no_output_timeout: 30m 90 | command: ./scripts/ci/steps/test.sh 91 | - store_artifacts: 92 | path: build/logs 93 | - notify 94 | 95 | prerelease: 96 | <<: *defaults 97 | steps: 98 | - checkout 99 | - configure_tools 100 | - run: ./scripts/ci/steps/prerelease.sh 101 | - notify 102 | 103 | release: 104 | <<: *defaults 105 | steps: 106 | - checkout 107 | - configure_tools 108 | - run: ./scripts/ci/steps/release.sh 109 | - notify 110 | 111 | merge_pull_request: 112 | <<: *defaults 113 | steps: 114 | - checkout 115 | - configure_tools 116 | - run: ./scripts/ci/steps/merge-pull-request.sh 117 | - notify 118 | 119 | workflows: 120 | version: 2 121 | pipeline: 122 | jobs: 123 | - build: 124 | <<: *only_main_and_dependabot 125 | <<: *slack_context 126 | - test: 127 | <<: *only_main_and_dependabot 128 | <<: *slack_context 129 | requires: 130 | - build 131 | - merge_pull_request: 132 | <<: *only_dependabot 133 | <<: *slack_context 134 | requires: 135 | - test 136 | - prerelease: 137 | <<: *only_main 138 | <<: *slack_context 139 | requires: 140 | - test 141 | - slack/on-hold: 142 | <<: *only_main 143 | <<: *slack_context 144 | requires: 145 | - prerelease 146 | channel: release 147 | template: SLACK_ON_HOLD_NOTIFICATION 148 | - hold: 149 | <<: *only_main 150 | type: approval 151 | requires: 152 | - prerelease 153 | - slack/on-hold 154 | - release: 155 | <<: *only_main 156 | <<: *slack_context 157 | requires: 158 | - hold 159 | -------------------------------------------------------------------------------- /spec/unit/vpc_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'VPC' do 6 | let(:component) do 7 | var(role: :root, name: 'component') 8 | end 9 | let(:deployment_identifier) do 10 | var(role: :root, name: 'deployment_identifier') 11 | end 12 | let(:vpc_cidr) do 13 | var(role: :root, name: 'vpc_cidr') 14 | end 15 | let(:availability_zones) do 16 | var(role: :root, name: 'availability_zones') 17 | end 18 | let(:private_zone_id) do 19 | output(role: :prerequisites, name: 'private_zone_id') 20 | end 21 | 22 | describe 'by default' do 23 | before(:context) do 24 | @plan = plan(role: :root) do |vars| 25 | vars.private_zone_id = 26 | output(role: :prerequisites, name: 'private_zone_id') 27 | end 28 | end 29 | 30 | it 'creates a VPC' do 31 | expect(@plan) 32 | .to(include_resource_creation(type: 'aws_vpc') 33 | .once) 34 | end 35 | 36 | it 'uses the provided VPC CIDR' do 37 | expect(@plan) 38 | .to(include_resource_creation(type: 'aws_vpc') 39 | .with_attribute_value(:cidr_block, vpc_cidr)) 40 | end 41 | 42 | it 'uses enables DNS hostnames' do 43 | expect(@plan) 44 | .to(include_resource_creation(type: 'aws_vpc') 45 | .with_attribute_value( 46 | :enable_dns_hostnames, true 47 | )) 48 | end 49 | 50 | it 'includes Component and DeploymentIdentifier tags' do 51 | expect(@plan) 52 | .to(include_resource_creation(type: 'aws_vpc') 53 | .with_attribute_value( 54 | :tags, 55 | a_hash_including( 56 | Component: component, 57 | DeploymentIdentifier: deployment_identifier 58 | ) 59 | )) 60 | end 61 | 62 | it 'includes a Dependencies tag as an empty string' do 63 | expect(@plan) 64 | .to(include_resource_creation(type: 'aws_vpc') 65 | .with_attribute_value( 66 | :tags, 67 | a_hash_including(Dependencies: '') 68 | )) 69 | end 70 | 71 | it 'outputs the VPC ID' do 72 | expect(@plan) 73 | .to(include_output_creation(name: 'vpc_id')) 74 | end 75 | 76 | it 'outputs the VPC CIDR' do 77 | expect(@plan) 78 | .to(include_output_creation(name: 'vpc_cidr') 79 | .with_value(vpc_cidr)) 80 | end 81 | 82 | it 'outputs the availability zones' do 83 | expect(@plan) 84 | .to(include_output_creation(name: 'availability_zones') 85 | .with_value(availability_zones)) 86 | end 87 | 88 | it 'outputs the number of availability zones' do 89 | expect(@plan) 90 | .to(include_output_creation(name: 'number_of_availability_zones') 91 | .with_value(availability_zones.length)) 92 | end 93 | 94 | it 'creates a Route 53 zone association for the provided ' \ 95 | 'hosted zone' do 96 | expect(@plan) 97 | .to(include_resource_creation(type: 'aws_route53_zone_association') 98 | .with_attribute_value(:zone_id, private_zone_id)) 99 | end 100 | end 101 | 102 | context 'when include_route53_zone_association is "no"' do 103 | before(:context) do 104 | @plan = plan(role: :root) do |vars| 105 | vars.include_route53_zone_association = 'no' 106 | end 107 | end 108 | 109 | it 'does not create a Route53 zone association' do 110 | expect(@plan) 111 | .not_to(include_resource_creation(type: 'aws_route53_zone_association')) 112 | end 113 | end 114 | 115 | context 'when include_route53_zone_association is "yes"' do 116 | before(:context) do 117 | @plan = plan(role: :root) do |vars| 118 | vars.include_route53_zone_association = 'yes' 119 | vars.private_zone_id = 120 | output(role: :prerequisites, name: 'private_zone_id') 121 | end 122 | end 123 | 124 | it 'creates a Route 53 zone association for the provided ' \ 125 | 'hosted zone' do 126 | expect(@plan) 127 | .to(include_resource_creation(type: 'aws_route53_zone_association') 128 | .with_attribute_value(:zone_id, private_zone_id)) 129 | end 130 | end 131 | 132 | context 'when dependencies are provided' do 133 | before(:context) do 134 | @plan = plan(role: :root) do |vars| 135 | vars.dependencies = %w[first second] 136 | vars.private_zone_id = 137 | output(role: :prerequisites, name: 'private_zone_id') 138 | end 139 | end 140 | 141 | it 'includes a Dependencies tag containing the dependencies' do 142 | expect(@plan) 143 | .to(include_resource_creation(type: 'aws_vpc') 144 | .with_attribute_value( 145 | :tags, 146 | a_hash_including(Dependencies: 'first,second') 147 | )) 148 | end 149 | end 150 | end 151 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | 3 | BACKWARDS INCOMPATIBILITIES / NOTES: 4 | 5 | * `for_each` is used instead of `count` for creating resources for each 6 | availability zone, so the availability zone will be used as the resource key, 7 | not an index. 8 | 9 | As a consequence, if resources have been created with a 10 | previous version of this module, they will need to be `moved` to avoid them 11 | being destroyed and recreated. 12 | 13 | e.g. 14 | 15 | ```terraform 16 | moved { 17 | from = module.base-network.aws_subnet.public[0] 18 | to = module.base-network.aws_subnet.public["eu-west-1a"] 19 | } 20 | 21 | moved { 22 | from = module.base-network.aws_subnet.private[0] 23 | to = module.base-network.aws_subnet.private["eu-west-1a"] 24 | } 25 | 26 | # etc.. 27 | ``` 28 | 29 | 30 | * The default value for the `private_subnets_offset` variable has been changed 31 | from 0 to 128. This means that if the offsets are not provided, there will 32 | be sufficient space between the private and public CIDR blocks such that new 33 | availability_zones can be added without needing to destroy existing private 34 | subnets. 35 | 36 | **NB**. If resources were created with a previous module version and the 37 | `private_subnets_offset` variable wasn't previously supplied, then the 38 | variable will need to be explicitly set to `0` to ensure that the private 39 | subnet CIDR blocks are calculated to the same values that they previously had. 40 | 41 | ENHANCEMENTS 42 | 43 | * As an alternative to the `availability_zones` variable, an 44 | `availability_zone_configuration` variable is also supported, which takes a 45 | list of objects with the keys `zone`, `public_subnet_cidr` and 46 | `private_subnet_cidr`. This can be useful in cases where a new availability 47 | zone is being added, but inference of the CIDR blocks could result in existing 48 | subnets being destroyed and recreated, in which case the existing public and 49 | private subnet CIDRs can be explicitly supplied to prevent this. 50 | 51 | ## 5.1.0 (13th Feb 2023) 52 | 53 | ENHANCEMENTS 54 | 55 | * This module now outputs the ID of the created IGW. 56 | 57 | ## 5.0.0 (28th Dec 2022) 58 | 59 | ENHANCEMENTS 60 | 61 | * This module can now be used with version 4 of the AWS provider. 62 | 63 | BACKWARDS INCOMPATIBILITIES / NOTES: 64 | 65 | * This module is now compatible with Terraform 1.0 and higher. 66 | 67 | ## 4.0.0 (27th May 2021) 68 | 69 | BACKWARDS INCOMPATIBILITIES / NOTES: 70 | 71 | * This module is now compatible with Terraform 0.14 and higher. 72 | 73 | ## 3.0.0 (5th September 2020) 74 | 75 | BACKWARDS INCOMPATIBILITIES / NOTES: 76 | 77 | * Prior to this version, the module created a single NAT gateway routed to by 78 | all private subnets. This created a single point of failure in the case of an 79 | availability zone outage of the zone in which the NAT gateway resided. The 80 | module now creates a NAT gateway per availability zone, which also 81 | necessitates a route table per availability zone. 82 | 83 | As a result, applying this version of the module will destroy both the public 84 | and private route tables in the network and then recreate a public and 85 | private route table for each availability zone. Any additional routes added 86 | to the existing route tables must be readded to each of the newly created 87 | route tables. 88 | 89 | Additionally, where previously single valued `public_route_table_id`, 90 | `private_route_table_id` and `nat_public_ip` outputs were produced, now array 91 | valued `public_route_table_ids`, `private_route_table_ids` and 92 | `nat_public_ips` outputs are produced. 93 | 94 | ## 2.0.0 (5th December 2019) 95 | 96 | BACKWARDS INCOMPATIBILITIES / NOTES: 97 | 98 | * The `infrastructure_events_bucket` variable, the `include_lifecycle_events` 99 | variable and the `aws_s3_bucket_object` representing the VPC lifecycle event 100 | have been removed from this module and are now encapsulated into the 101 | `terraform-aws-vpc-lifecycle-event` module. This is to allow the lifecycle 102 | events bucket to be in a different account to the VPC. 103 | 104 | ## 1.1.0 (28th November 2019) 105 | 106 | BUG FIXES: 107 | 108 | * Upgrade to avoid vulnerability in test library 109 | 110 | ## 1.0.0 (18th November 2019) 111 | 112 | ENHANCEMENTS: 113 | 114 | * Make use of more recent module improvements such as list inputs and outputs. 115 | * Fully convert to HCL 2. 116 | 117 | BACKWARDS INCOMPATIBILITIES / NOTES: 118 | 119 | * The `availability_zones` and `dependencies` inputs are now lists of strings. 120 | * The `availability_zones`, `public_subnet_ids`, `public_subnet_cidr_blocks`, 121 | `private_subnet_ids` and `private_subnet_cidr_blocks` outputs are now all 122 | lists of strings. 123 | 124 | ## 0.2.0 (25th April 2018) 125 | 126 | ENHANCEMENTS: 127 | 128 | * Make NAT gateway optional. 129 | 130 | ## 0.1.17 (December 28th, 2017) 131 | 132 | BACKWARDS INCOMPATIBILITIES / NOTES: 133 | 134 | * The configuration directory has changed from `/src` to `` to 135 | satisfy the terraform standard module structure. 136 | 137 | ENHANCEMENTS: 138 | 139 | * All variables and outputs now have descriptions to satisfy the terraform 140 | standard module structure. 141 | 142 | ## 0.1.11 (October 5th, 2017) 143 | 144 | BACKWARDS INCOMPATIBILITIES / NOTES: 145 | 146 | * This version extracts the bastion into a separate 147 | [bastion module](https://github.com/infrablocks/terraform-aws-bastion) which 148 | allows networks to be deployed with or without a bastion or for a bastion to 149 | be shared across networks. The bastion module also uses an autoscaling group 150 | to ensure the bastion remains available. As such, it will require a load 151 | balancer in order to be given a DNS name. 152 | -------------------------------------------------------------------------------- /spec/unit/public_subnets_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'netaddr' 5 | 6 | describe 'public' do 7 | let(:component) do 8 | var(role: :root, name: 'component') 9 | end 10 | let(:deployment_identifier) do 11 | var(role: :root, name: 'deployment_identifier') 12 | end 13 | let(:vpc_cidr) do 14 | var(role: :root, name: 'vpc_cidr') 15 | end 16 | let(:availability_zones) do 17 | var(role: :root, name: 'availability_zones') 18 | end 19 | 20 | describe 'subnets' do 21 | describe 'by default' do 22 | before(:context) do 23 | @plan = plan(role: :root) do |vars| 24 | vars.private_zone_id = 25 | output(role: :prerequisites, name: 'private_zone_id') 26 | end 27 | end 28 | 29 | it 'creates a subnet for each availability zone' do 30 | expect(@plan) 31 | .to(include_resource_creation(type: 'aws_subnet', name: 'public') 32 | .exactly(availability_zones.count).times) 33 | end 34 | 35 | it 'adds Component and DeploymentIdentifier tags on each subnet' do 36 | availability_zones.each do |availability_zone| 37 | expect(@plan) 38 | .to(include_resource_creation(type: 'aws_subnet', name: 'public') 39 | .with_attribute_value(:availability_zone, availability_zone) 40 | .with_attribute_value( 41 | :tags, 42 | a_hash_including( 43 | Component: component, 44 | DeploymentIdentifier: deployment_identifier 45 | ) 46 | )) 47 | end 48 | end 49 | 50 | it 'adds a Tier tag of public on each subnet' do 51 | availability_zones.each do |availability_zone| 52 | expect(@plan) 53 | .to(include_resource_creation(type: 'aws_subnet', name: 'public') 54 | .with_attribute_value(:availability_zone, availability_zone) 55 | .with_attribute_value( 56 | :tags, 57 | a_hash_including( 58 | Tier: 'public' 59 | ) 60 | )) 61 | end 62 | end 63 | 64 | it 'uses /24 networks relative to the VPC CIDR for each subnet' do 65 | availability_zones.each do |availability_zone| 66 | expect(@plan) 67 | .to(include_resource_creation(type: 'aws_subnet', name: 'public') 68 | .with_attribute_value(:availability_zone, availability_zone) 69 | .with_attribute_value( 70 | :cidr_block, 71 | a_cidr_block_within(vpc_cidr).of_size('/24') 72 | )) 73 | end 74 | end 75 | 76 | it 'uses unique CIDRs for each subnet' do 77 | public_subnet_creations = 78 | @plan.resource_changes_matching(type: 'aws_subnet', name: 'public') 79 | cidr_blocks = public_subnet_creations.map do |creation| 80 | creation.change.after_object[:cidr_block] 81 | end 82 | 83 | expect(cidr_blocks.uniq.length).to(eq(availability_zones.length)) 84 | end 85 | 86 | it 'outputs the public subnet IDs' do 87 | expect(@plan) 88 | .to(include_output_creation(name: 'public_subnet_ids')) 89 | end 90 | 91 | it 'outputs the public subnet CIDR blocks' do 92 | expect(@plan) 93 | .to(include_output_creation(name: 'public_subnet_cidr_blocks')) 94 | end 95 | end 96 | end 97 | 98 | describe 'route tables' do 99 | describe 'by default' do 100 | before(:context) do 101 | @plan = plan(role: :root) do |vars| 102 | vars.private_zone_id = 103 | output(role: :prerequisites, name: 'private_zone_id') 104 | end 105 | end 106 | 107 | it 'creates a route table for each availability zone' do 108 | expect(@plan) 109 | .to(include_resource_creation(type: 'aws_route_table', 110 | name: 'public') 111 | .exactly(availability_zones.count).times) 112 | end 113 | 114 | it 'adds Component and DeploymentIdentifier tags on each route table' do 115 | expect(@plan) 116 | .to(include_resource_creation(type: 'aws_route_table', name: 'public') 117 | .with_attribute_value( 118 | :tags, 119 | a_hash_including( 120 | Component: component, 121 | DeploymentIdentifier: deployment_identifier 122 | ) 123 | ) 124 | .exactly(availability_zones.length).times) 125 | end 126 | 127 | it 'adds a Tier tag of public on each route table' do 128 | expect(@plan) 129 | .to(include_resource_creation(type: 'aws_route_table', name: 'public') 130 | .with_attribute_value( 131 | :tags, 132 | a_hash_including( 133 | Tier: 'public' 134 | ) 135 | ).exactly(availability_zones.length).times) 136 | end 137 | 138 | it 'creates a catch all route for each route table' do 139 | expect(@plan) 140 | .to(include_resource_creation( 141 | type: 'aws_route', name: 'public_internet' 142 | ) 143 | .with_attribute_value(:destination_cidr_block, '0.0.0.0/0') 144 | .exactly(availability_zones.length).times) 145 | end 146 | 147 | it 'creates a route table association for each route table' do 148 | expect(@plan) 149 | .to(include_resource_creation( 150 | type: 'aws_route_table_association', 151 | name: 'public' 152 | ) 153 | .exactly(availability_zones.length).times) 154 | end 155 | 156 | it 'outputs the public route table IDs' do 157 | expect(@plan) 158 | .to(include_output_creation(name: 'public_route_table_ids')) 159 | end 160 | end 161 | end 162 | end 163 | -------------------------------------------------------------------------------- /spec/unit/nat_gateways_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'NAT gateways' do 6 | let(:component) do 7 | var(role: :root, name: 'component') 8 | end 9 | let(:deployment_identifier) do 10 | var(role: :root, name: 'deployment_identifier') 11 | end 12 | let(:availability_zones) do 13 | var(role: :root, name: 'availability_zones') 14 | end 15 | 16 | describe 'by default' do 17 | before(:context) do 18 | @plan = plan(role: :root) do |vars| 19 | vars.private_zone_id = 20 | output(role: :prerequisites, name: 'private_zone_id') 21 | end 22 | end 23 | 24 | it 'creates a NAT gateway in each public subnet' do 25 | expect(@plan) 26 | .to(include_resource_creation(type: 'aws_nat_gateway') 27 | .exactly(availability_zones.count).times) 28 | end 29 | 30 | it 'includes a Name tag on each NAT gateway' do 31 | availability_zones.each do |availability_zone| 32 | name = "nat-#{component}-#{deployment_identifier}-#{availability_zone}" 33 | expect(@plan) 34 | .to(include_resource_creation(type: 'aws_nat_gateway') 35 | .with_attribute_value(:tags, a_hash_including(Name: name))) 36 | end 37 | end 38 | 39 | it 'includes Component and DeploymentIdentifier tags on each NAT gateway' do 40 | expect(@plan) 41 | .to(include_resource_creation(type: 'aws_nat_gateway') 42 | .with_attribute_value( 43 | :tags, 44 | a_hash_including( 45 | Component: component, 46 | DeploymentIdentifier: deployment_identifier 47 | ) 48 | ) 49 | .exactly(availability_zones.count).times) 50 | end 51 | 52 | it 'creates an EIP for each public subnet' do 53 | expect(@plan) 54 | .to(include_resource_creation(type: 'aws_eip') 55 | .exactly(availability_zones.count).times) 56 | end 57 | 58 | it 'includes a Name tag on each EIP' do 59 | availability_zones.each do |availability_zone| 60 | name = 61 | "eip-nat-#{component}-#{deployment_identifier}-#{availability_zone}" 62 | expect(@plan) 63 | .to(include_resource_creation(type: 'aws_eip') 64 | .with_attribute_value(:tags, a_hash_including(Name: name))) 65 | end 66 | end 67 | 68 | it 'includes Component and DeploymentIdentifier tags on each EIP' do 69 | expect(@plan) 70 | .to(include_resource_creation(type: 'aws_eip') 71 | .with_attribute_value( 72 | :tags, 73 | a_hash_including( 74 | Component: component, 75 | DeploymentIdentifier: deployment_identifier 76 | ) 77 | ) 78 | .exactly(availability_zones.count).times) 79 | end 80 | 81 | it 'outputs the NAT public IPs' do 82 | expect(@plan) 83 | .to(include_output_creation(name: 'nat_public_ips')) 84 | end 85 | end 86 | 87 | context 'when include_nat_gateways is no' do 88 | before(:context) do 89 | @plan = plan(role: :root) do |vars| 90 | vars.include_nat_gateways = 'no' 91 | vars.private_zone_id = 92 | output(role: :prerequisites, name: 'private_zone_id') 93 | end 94 | end 95 | 96 | it 'does not create any NAT gateways' do 97 | expect(@plan) 98 | .not_to(include_resource_creation(type: 'aws_nat_gateway')) 99 | end 100 | 101 | it 'does not create any EIPs' do 102 | expect(@plan) 103 | .not_to(include_resource_creation(type: 'aws_eip')) 104 | end 105 | end 106 | 107 | context 'when include_nat_gateways is yes' do 108 | before(:context) do 109 | @plan = plan(role: :root) do |vars| 110 | vars.include_nat_gateways = 'yes' 111 | vars.private_zone_id = 112 | output(role: :prerequisites, name: 'private_zone_id') 113 | end 114 | end 115 | 116 | it 'creates a NAT gateway in each public subnet' do 117 | expect(@plan) 118 | .to(include_resource_creation(type: 'aws_nat_gateway') 119 | .exactly(availability_zones.count).times) 120 | end 121 | 122 | it 'includes a Name tag on each NAT gateway' do 123 | availability_zones.each do |availability_zone| 124 | name = "nat-#{component}-#{deployment_identifier}-#{availability_zone}" 125 | expect(@plan) 126 | .to(include_resource_creation(type: 'aws_nat_gateway') 127 | .with_attribute_value(:tags, a_hash_including(Name: name))) 128 | end 129 | end 130 | 131 | it 'includes Component and DeploymentIdentifier tags on each NAT gateway' do 132 | expect(@plan) 133 | .to(include_resource_creation(type: 'aws_nat_gateway') 134 | .with_attribute_value( 135 | :tags, 136 | a_hash_including( 137 | Component: component, 138 | DeploymentIdentifier: deployment_identifier 139 | ) 140 | ) 141 | .exactly(availability_zones.count).times) 142 | end 143 | 144 | it 'creates an EIP for each public subnet' do 145 | expect(@plan) 146 | .to(include_resource_creation(type: 'aws_eip') 147 | .exactly(availability_zones.count).times) 148 | end 149 | 150 | it 'includes a Name tag on each EIP' do 151 | availability_zones.each do |availability_zone| 152 | name = 153 | "eip-nat-#{component}-#{deployment_identifier}-#{availability_zone}" 154 | expect(@plan) 155 | .to(include_resource_creation(type: 'aws_eip') 156 | .with_attribute_value(:tags, a_hash_including(Name: name))) 157 | end 158 | end 159 | 160 | it 'includes Component and DeploymentIdentifier tags on each EIP' do 161 | expect(@plan) 162 | .to(include_resource_creation(type: 'aws_eip') 163 | .with_attribute_value( 164 | :tags, 165 | a_hash_including( 166 | Component: component, 167 | DeploymentIdentifier: deployment_identifier 168 | ) 169 | ) 170 | .exactly(availability_zones.count).times) 171 | end 172 | end 173 | end 174 | -------------------------------------------------------------------------------- /config/gpg/toby.gpg.public: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | 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 105 | VryshT5m6VKxVqJv 106 | =vHmq 107 | -----END PGP PUBLIC KEY BLOCK----- 108 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'confidante' 4 | require 'git' 5 | require 'rake_circle_ci' 6 | require 'rake_git' 7 | require 'rake_git_crypt' 8 | require 'rake_github' 9 | require 'rake_gpg' 10 | require 'rake_ssh' 11 | require 'rake_terraform' 12 | require 'rspec/core/rake_task' 13 | require 'rubocop/rake_task' 14 | require 'securerandom' 15 | require 'semantic' 16 | require 'yaml' 17 | 18 | require_relative 'lib/paths' 19 | require_relative 'lib/version' 20 | 21 | configuration = Confidante.configuration 22 | 23 | def repo 24 | Git.open(Pathname.new('.')) 25 | end 26 | 27 | def latest_tag 28 | repo.tags.map do |tag| 29 | Semantic::Version.new(tag.name) 30 | end.max 31 | end 32 | 33 | task default: %i[ 34 | test:code:fix 35 | test:unit 36 | test:integration 37 | ] 38 | 39 | RakeTerraform.define_installation_tasks( 40 | path: File.join(Dir.pwd, 'vendor', 'terraform'), 41 | version: '1.3.1' 42 | ) 43 | 44 | RakeGitCrypt.define_standard_tasks( 45 | namespace: :git_crypt, 46 | 47 | provision_secrets_task_name: :'secrets:provision', 48 | destroy_secrets_task_name: :'secrets:destroy', 49 | 50 | install_commit_task_name: :'git:commit', 51 | uninstall_commit_task_name: :'git:commit', 52 | 53 | gpg_user_key_paths: %w[ 54 | config/gpg 55 | config/secrets/ci/gpg.public 56 | ] 57 | ) 58 | 59 | namespace :git do 60 | RakeGit.define_commit_task( 61 | argument_names: [:message] 62 | ) do |t, args| 63 | t.message = args.message 64 | end 65 | end 66 | 67 | namespace :encryption do 68 | namespace :directory do 69 | desc 'Ensure CI secrets directory exists.' 70 | task :ensure do 71 | FileUtils.mkdir_p('config/secrets/ci') 72 | end 73 | end 74 | 75 | namespace :passphrase do 76 | desc 'Generate encryption passphrase for CI GPG key' 77 | task generate: ['directory:ensure'] do 78 | File.write( 79 | 'config/secrets/ci/encryption.passphrase', 80 | SecureRandom.base64(36) 81 | ) 82 | end 83 | end 84 | end 85 | 86 | namespace :keys do 87 | namespace :deploy do 88 | RakeSSH.define_key_tasks( 89 | path: 'config/secrets/ci/', 90 | comment: 'maintainers@infrablocks.io' 91 | ) 92 | end 93 | 94 | namespace :gpg do 95 | RakeGPG.define_generate_key_task( 96 | output_directory: 'config/secrets/ci', 97 | name_prefix: 'gpg', 98 | owner_name: 'InfraBlocks Maintainers', 99 | owner_email: 'maintainers@infrablocks.io', 100 | owner_comment: 'terraform-aws-base-networking CI Key' 101 | ) 102 | end 103 | end 104 | 105 | namespace :secrets do 106 | namespace :directory do 107 | desc 'Ensure secrets directory exists and is set up correctly' 108 | task :ensure do 109 | FileUtils.mkdir_p('config/secrets') 110 | unless File.exist?('config/secrets/.unlocked') 111 | File.write('config/secrets/.unlocked', 'true') 112 | end 113 | end 114 | end 115 | 116 | desc 'Generate all generatable secrets.' 117 | task generate: %w[ 118 | directory:ensure 119 | encryption:passphrase:generate 120 | keys:deploy:generate 121 | keys:gpg:generate 122 | ] 123 | 124 | desc 'Provision all secrets.' 125 | task provision: [:generate] 126 | 127 | desc 'Delete all secrets.' 128 | task :destroy do 129 | rm_rf 'config/secrets' 130 | end 131 | 132 | desc 'Rotate all secrets.' 133 | task rotate: [:'git_crypt:reinstall'] 134 | end 135 | 136 | RakeCircleCI.define_project_tasks( 137 | namespace: :circle_ci, 138 | project_slug: 'github/infrablocks/terraform-aws-base-networking' 139 | ) do |t| 140 | circle_ci_config = 141 | YAML.load_file('config/secrets/circle_ci/config.yaml') 142 | 143 | t.api_token = circle_ci_config['circle_ci_api_token'] 144 | t.environment_variables = { 145 | ENCRYPTION_PASSPHRASE: 146 | File.read('config/secrets/ci/encryption.passphrase') 147 | .chomp, 148 | CIRCLECI_API_KEY: 149 | YAML.load_file( 150 | 'config/secrets/circle_ci/config.yaml' 151 | )['circle_ci_api_token'] 152 | } 153 | t.checkout_keys = [] 154 | t.ssh_keys = [ 155 | { 156 | hostname: 'github.com', 157 | private_key: File.read('config/secrets/ci/ssh.private') 158 | } 159 | ] 160 | end 161 | 162 | RakeGithub.define_repository_tasks( 163 | namespace: :github, 164 | repository: 'infrablocks/terraform-aws-base-networking' 165 | ) do |t| 166 | github_config = 167 | YAML.load_file('config/secrets/github/config.yaml') 168 | 169 | t.access_token = github_config['github_personal_access_token'] 170 | t.deploy_keys = [ 171 | { 172 | title: 'CircleCI', 173 | public_key: File.read('config/secrets/ci/ssh.public') 174 | } 175 | ] 176 | end 177 | 178 | namespace :pipeline do 179 | desc 'Prepare CircleCI Pipeline' 180 | task prepare: %i[ 181 | circle_ci:env_vars:ensure 182 | circle_ci:checkout_keys:ensure 183 | circle_ci:ssh_keys:ensure 184 | github:deploy_keys:ensure 185 | ] 186 | end 187 | 188 | RuboCop::RakeTask.new 189 | 190 | namespace :test do 191 | namespace :code do 192 | desc 'Run all checks on the test code' 193 | task check: [:rubocop] 194 | 195 | desc 'Attempt to automatically fix issues with the test code' 196 | task fix: [:'rubocop:autocorrect_all'] 197 | end 198 | 199 | desc 'Run module unit tests' 200 | RSpec::Core::RakeTask.new(unit: ['terraform:ensure']) do |t| 201 | t.pattern = 'spec/unit/**{,/*/**}/*_spec.rb' 202 | t.rspec_opts = '-I spec/unit' 203 | 204 | ENV['AWS_REGION'] = configuration.region 205 | end 206 | 207 | desc 'Run module integration tests' 208 | RSpec::Core::RakeTask.new(integration: ['terraform:ensure']) do |t| 209 | t.pattern = 'spec/integration/**{,/*/**}/*_spec.rb' 210 | t.rspec_opts = '-I spec/integration' 211 | 212 | ENV['AWS_REGION'] = configuration.region 213 | end 214 | end 215 | 216 | namespace :deployment do 217 | namespace :prerequisites do 218 | RakeTerraform.define_command_tasks( 219 | configuration_name: 'prerequisites', 220 | argument_names: [:seed] 221 | ) do |t, args| 222 | deployment_configuration = 223 | configuration 224 | .for_scope(role: :prerequisites) 225 | .for_overrides(args.to_h) 226 | 227 | t.source_directory = 'spec/unit/infra/prerequisites' 228 | t.work_directory = 'build/infra' 229 | 230 | t.state_file = deployment_configuration.state_file 231 | t.vars = deployment_configuration.vars 232 | end 233 | end 234 | 235 | namespace :root do 236 | RakeTerraform.define_command_tasks( 237 | configuration_name: 'root', 238 | argument_names: [:seed] 239 | ) do |t, args| 240 | deployment_configuration = 241 | configuration 242 | .for_scope(role: :root) 243 | .for_overrides(args.to_h) 244 | 245 | t.source_directory = 'spec/unit/infra/root' 246 | t.work_directory = 'build/infra' 247 | 248 | t.state_file = deployment_configuration.state_file 249 | t.vars = deployment_configuration.vars 250 | end 251 | end 252 | end 253 | 254 | namespace :version do 255 | desc 'Bump version for specified type (pre, major, minor, patch)' 256 | task :bump, [:type] do |_, args| 257 | next_tag = latest_tag.send("#{args.type}!") 258 | repo.add_tag(next_tag.to_s) 259 | repo.push('origin', 'main', tags: true) 260 | puts "Bumped version to #{next_tag}." 261 | end 262 | 263 | desc 'Release module' 264 | task :release do 265 | next_tag = latest_tag.release! 266 | repo.add_tag(next_tag.to_s) 267 | repo.push('origin', 'main', tags: true) 268 | puts "Released version #{next_tag}." 269 | end 270 | end 271 | -------------------------------------------------------------------------------- /spec/unit/private_subnets_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'netaddr' 5 | 6 | describe 'private' do 7 | let(:component) do 8 | var(role: :root, name: 'component') 9 | end 10 | let(:deployment_identifier) do 11 | var(role: :root, name: 'deployment_identifier') 12 | end 13 | let(:vpc_cidr) do 14 | var(role: :root, name: 'vpc_cidr') 15 | end 16 | let(:availability_zones) do 17 | var(role: :root, name: 'availability_zones') 18 | end 19 | 20 | describe 'subnets' do 21 | describe 'by default' do 22 | before(:context) do 23 | @plan = plan(role: :root) do |vars| 24 | vars.private_zone_id = 25 | output(role: :prerequisites, name: 'private_zone_id') 26 | end 27 | end 28 | 29 | it 'creates a subnet for each availability zone' do 30 | expect(@plan) 31 | .to(include_resource_creation(type: 'aws_subnet', name: 'private') 32 | .exactly(availability_zones.count).times) 33 | end 34 | 35 | it 'adds Component and DeploymentIdentifier tags on each subnet' do 36 | availability_zones.each do |availability_zone| 37 | expect(@plan) 38 | .to(include_resource_creation(type: 'aws_subnet', name: 'private') 39 | .with_attribute_value(:availability_zone, availability_zone) 40 | .with_attribute_value( 41 | :tags, 42 | a_hash_including( 43 | Component: component, 44 | DeploymentIdentifier: deployment_identifier 45 | ) 46 | )) 47 | end 48 | end 49 | 50 | it 'adds a Tier tag of private on each subnet' do 51 | availability_zones.each do |availability_zone| 52 | expect(@plan) 53 | .to(include_resource_creation(type: 'aws_subnet', name: 'private') 54 | .with_attribute_value(:availability_zone, availability_zone) 55 | .with_attribute_value( 56 | :tags, 57 | a_hash_including( 58 | Tier: 'private' 59 | ) 60 | )) 61 | end 62 | end 63 | 64 | it 'uses /24 networks relative to the VPC CIDR for each subnet' do 65 | availability_zones.each do |availability_zone| 66 | expect(@plan) 67 | .to(include_resource_creation(type: 'aws_subnet', name: 'private') 68 | .with_attribute_value(:availability_zone, availability_zone) 69 | .with_attribute_value( 70 | :cidr_block, 71 | a_cidr_block_within(vpc_cidr).of_size('/24') 72 | )) 73 | end 74 | end 75 | 76 | it 'uses unique CIDRs for each subnet' do 77 | private_subnet_creations = 78 | @plan.resource_changes_matching(type: 'aws_subnet', name: 'private') 79 | cidr_blocks = private_subnet_creations.map do |creation| 80 | creation.change.after_object[:cidr_block] 81 | end 82 | 83 | expect(cidr_blocks.uniq.length).to(eq(availability_zones.length)) 84 | end 85 | 86 | it 'outputs the private subnet IDs' do 87 | expect(@plan) 88 | .to(include_output_creation(name: 'private_subnet_ids')) 89 | end 90 | 91 | it 'outputs the private subnet CIDR blocks' do 92 | expect(@plan) 93 | .to(include_output_creation(name: 'private_subnet_cidr_blocks')) 94 | end 95 | end 96 | end 97 | 98 | describe 'route tables' do 99 | describe 'by default' do 100 | before(:context) do 101 | @plan = plan(role: :root) do |vars| 102 | vars.private_zone_id = 103 | output(role: :prerequisites, name: 'private_zone_id') 104 | end 105 | end 106 | 107 | it 'creates a route table for each availability zone' do 108 | expect(@plan) 109 | .to(include_resource_creation(type: 'aws_route_table', 110 | name: 'private') 111 | .exactly(availability_zones.count).times) 112 | end 113 | 114 | it 'adds Component and DeploymentIdentifier tags on each route table' do 115 | expect(@plan) 116 | .to(include_resource_creation( 117 | type: 'aws_route_table', 118 | name: 'private' 119 | ) 120 | .with_attribute_value( 121 | :tags, 122 | a_hash_including( 123 | Component: component, 124 | DeploymentIdentifier: deployment_identifier 125 | ) 126 | ) 127 | .exactly(availability_zones.length).times) 128 | end 129 | 130 | it 'adds a Tier tag of private on each route table' do 131 | expect(@plan) 132 | .to(include_resource_creation( 133 | type: 'aws_route_table', 134 | name: 'private' 135 | ) 136 | .with_attribute_value( 137 | :tags, 138 | a_hash_including( 139 | Tier: 'private' 140 | ) 141 | ).exactly(availability_zones.length).times) 142 | end 143 | 144 | it 'creates a catch all route for each route table' do 145 | expect(@plan) 146 | .to(include_resource_creation( 147 | type: 'aws_route', name: 'private_internet' 148 | ) 149 | .with_attribute_value(:destination_cidr_block, '0.0.0.0/0') 150 | .exactly(availability_zones.length).times) 151 | end 152 | 153 | it 'creates a route table association for each route table' do 154 | expect(@plan) 155 | .to(include_resource_creation( 156 | type: 'aws_route_table_association', 157 | name: 'private' 158 | ) 159 | .exactly(availability_zones.length).times) 160 | end 161 | 162 | it 'outputs the private route table IDs' do 163 | expect(@plan) 164 | .to(include_output_creation(name: 'private_route_table_ids')) 165 | end 166 | end 167 | 168 | describe 'when include_nat_gateways is "no"' do 169 | before(:context) do 170 | @plan = plan(role: :root) do |vars| 171 | vars.include_nat_gateways = 'no' 172 | vars.private_zone_id = 173 | output(role: :prerequisites, name: 'private_zone_id') 174 | end 175 | end 176 | 177 | it 'creates a route table for each availability zone' do 178 | expect(@plan) 179 | .to(include_resource_creation(type: 'aws_route_table', 180 | name: 'private') 181 | .exactly(availability_zones.count).times) 182 | end 183 | 184 | it 'creates a route table association for each route table' do 185 | expect(@plan) 186 | .to(include_resource_creation( 187 | type: 'aws_route_table_association', 188 | name: 'private' 189 | ) 190 | .exactly(availability_zones.length).times) 191 | end 192 | 193 | it 'does not create any routes in each route table' do 194 | expect(@plan) 195 | .not_to(include_resource_creation( 196 | type: 'aws_route', name: 'private_internet' 197 | )) 198 | end 199 | end 200 | 201 | describe 'when include_nat_gateways is "yes"' do 202 | before(:context) do 203 | @plan = plan(role: :root) do |vars| 204 | vars.include_nat_gateways = 'yes' 205 | vars.private_zone_id = 206 | output(role: :prerequisites, name: 'private_zone_id') 207 | end 208 | end 209 | 210 | it 'creates a route table for each availability zone' do 211 | expect(@plan) 212 | .to(include_resource_creation(type: 'aws_route_table', 213 | name: 'private') 214 | .exactly(availability_zones.count).times) 215 | end 216 | 217 | it 'creates a route table association for each route table' do 218 | expect(@plan) 219 | .to(include_resource_creation( 220 | type: 'aws_route_table_association', 221 | name: 'private' 222 | ) 223 | .exactly(availability_zones.length).times) 224 | end 225 | 226 | it 'creates a catch all route for each route table' do 227 | expect(@plan) 228 | .to(include_resource_creation( 229 | type: 'aws_route', name: 'private_internet' 230 | ) 231 | .with_attribute_value(:destination_cidr_block, '0.0.0.0/0') 232 | .exactly(availability_zones.length).times) 233 | end 234 | end 235 | end 236 | end 237 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Terraform AWS Base Networking 2 | ============================= 3 | 4 | [![Version](https://img.shields.io/github/v/tag/infrablocks/terraform-aws-base-networking?label=version&sort=semver)](https://github.com/infrablocks/terraform-aws-base-networking/tags) 5 | [![Build Pipeline](https://img.shields.io/circleci/build/github/infrablocks/terraform-aws-base-networking/main?label=build-pipeline)](https://app.circleci.com/pipelines/github/infrablocks/terraform-aws-base-networking?filter=all) 6 | [![Maintainer](https://img.shields.io/badge/maintainer-go--atomic.io-red)](https://go-atomic.io) 7 | 8 | A Terraform module for building a base network in AWS. 9 | 10 | The network consists of: 11 | 12 | * Public and private subnets for each supplied availability zone 13 | * A NAT gateway for each supplied availability zone for outbound Internet 14 | connectivity 15 | * Routes from the public subnets to the Internet gateway 16 | * Routes from the private subnets to the NAT 17 | * Standard tags for all resources 18 | 19 | ![Diagram of infrastructure managed by this module](https://raw.githubusercontent.com/infrablocks/terraform-aws-base-networking/main/docs/architecture.png) 20 | 21 | Usage 22 | ----- 23 | 24 | To use the module, include something like the following in your Terraform 25 | configuration: 26 | 27 | ```terraform 28 | module "base-network" { 29 | source = "infrablocks/base-networking/aws" 30 | version = "4.0.0" 31 | 32 | vpc_cidr = "10.0.0.0/16" 33 | region = "eu-west-2" 34 | availability_zones = ["eu-west-2a", "eu-west-2b"] 35 | 36 | component = "important-component" 37 | deployment_identifier = "production" 38 | 39 | private_zone_id = "Z3CVA9QD5NHSW3" 40 | } 41 | ``` 42 | 43 | See the 44 | [Terraform registry entry](https://registry.terraform.io/modules/infrablocks/base-networking/aws/latest) 45 | for more details. 46 | 47 | ### Inputs 48 | 49 | | Name | Description | Default | Required | 50 | |------------------------------------|-----------------------------------------------------------------------------------------------|:-------:|:------------------------------------------------:| 51 | | `vpc_cidr` | The CIDR to use for the VPC. | - | Yes | 52 | | `region` | The region into which to deploy the VPC. | - | Yes | 53 | | `availability_zones` | The availability zones for which to add subnets. | - | Yes | 54 | | `public_subnets_offset` | The number of /24s to offset the public subnets in the VPC CIDR. | `0` | No | 55 | | `private_subnets_offset` | The number of /24s to offset the private subnets in the VPC CIDR. | `0` | No | 56 | | `component` | The component this network will contain. | - | Yes | 57 | | `deployment_identifier` | An identifier for this instantiation. | - | Yes | 58 | | `dependencies` | A comma separated list of components depended on my this component. | `[]` | No | 59 | | `include_route53_zone_association` | Whether or not to associate VPC with the private Route 53 zone (`"yes"` or `"no"`). | `"yes"` | No | 60 | | `private_zone_id` | The ID of the private Route 53 zone` | - | If `include_route53_zone_association` is `"yes"` | 61 | | `include_nat_gateways` | Whether or not to deploy NAT gateways for outbound Internet connectivity (`"yes"` or `"no"`). | `"yes"` | No | 62 | 63 | ### Outputs 64 | 65 | | Name | Description | 66 | |--------------------------------|-------------------------------------------------------| 67 | | `vpc_id` | The ID of the created VPC. | 68 | | `vpc_cidr` | The CIDR of the created VPC. | 69 | | `availability_zones` | The availability zones in which subnets were created. | 70 | | `number_of_availability_zones` | The number of populated availability zones available. | 71 | | `public_subnet_ids` | The IDs of the public subnets. | 72 | | `public_subnet_cidrs` | The CIDRs of the public subnets. | 73 | | `public_route_table_ids` | The IDs of the public route tables. | 74 | | `private_subnet_ids` | The IDs of the private subnets. | 75 | | `private_subnet_cidrs` | The CIDRs of the private subnets. | 76 | | `private_route_table_ids` | The IDs of the private route tables. | 77 | | `nat_public_ips` | The EIPs attached to the NAT gateways. | 78 | | `internet_gateway_id` | The ID of the created IGW. | 79 | 80 | ### Compatibility 81 | 82 | This module is compatible with Terraform versions greater than or equal to 83 | Terraform 1.0 and Terraform AWS provider versions greater than or equal to 3.27. 84 | 85 | ### Required Permissions 86 | 87 | * ec2:DescribeVpcs 88 | * ec2:DescribeAddresses 89 | * ec2:DescribeVpcAttribute 90 | * ec2:DescribeVpcClassicLink 91 | * ec2:DescribeVpcClassicLinkDnsSupport 92 | * ec2:DescribeRouteTables 93 | * ec2:DescribeSecurityGroups 94 | * ec2:DescribeNetworkAcls 95 | * ec2:DescribeSubnets 96 | * ec2:DescribeInternetGateways 97 | * ec2:DescribeNatGateways 98 | * ec2:ModifyVpcAttribute 99 | * ec2:AllocateAddress 100 | * ec2:ReleaseAddress 101 | * ec2:AssociateRouteTable 102 | * ec2:DisassociateRouteTable 103 | * ec2:AttachInternetGateway 104 | * ec2:DetachInternetGateway 105 | * ec2:DeleteInternetGateway 106 | * ec2:CreateRoute 107 | * ec2:CreateNatGateway 108 | * ec2:CreateVpc 109 | * ec2:CreateTags 110 | * ec2:CreateSubnet 111 | * ec2:CreateRouteTable 112 | * ec2:CreateInternetGateway 113 | * ec2:DeleteRoute 114 | * ec2:DeleteRouteTable 115 | * ec2:DeleteSubnet 116 | * ec2:DeleteNatGateway 117 | * ec2:DeleteVpc 118 | * s3:ListBucket 119 | * s3:GetObject 120 | * s3:GetObjectTagging 121 | * s3:DeleteObject 122 | * route53:AssociateVPCWithHostedZone 123 | * route53:DisassociateVPCFromHostedZone 124 | * route53:GetChange 125 | * route53:GetHostedZone 126 | 127 | Development 128 | ----------- 129 | 130 | ### Machine Requirements 131 | 132 | In order for the build to run correctly, a few tools will need to be installed 133 | on your development machine: 134 | 135 | * Ruby (3.1) 136 | * Bundler 137 | * git 138 | * git-crypt 139 | * gnupg 140 | * direnv 141 | * aws-vault 142 | 143 | #### Mac OS X Setup 144 | 145 | Installing the required tools is best managed by [homebrew](http://brew.sh). 146 | 147 | To install homebrew: 148 | 149 | ```shell 150 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 151 | ``` 152 | 153 | Then, to install the required tools: 154 | 155 | ```shell 156 | # ruby 157 | brew install rbenv 158 | brew install ruby-build 159 | echo 'eval "$(rbenv init - bash)"' >> ~/.bash_profile 160 | echo 'eval "$(rbenv init - zsh)"' >> ~/.zshrc 161 | eval "$(rbenv init -)" 162 | rbenv install 3.1.1 163 | rbenv rehash 164 | rbenv local 3.1.1 165 | gem install bundler 166 | 167 | # git, git-crypt, gnupg 168 | brew install git 169 | brew install git-crypt 170 | brew install gnupg 171 | 172 | # aws-vault 173 | brew cask install 174 | 175 | # direnv 176 | brew install direnv 177 | echo "$(direnv hook bash)" >> ~/.bash_profile 178 | echo "$(direnv hook zsh)" >> ~/.zshrc 179 | eval "$(direnv hook $SHELL)" 180 | 181 | direnv allow 182 | ``` 183 | 184 | ### Running the build 185 | 186 | Running the build requires an AWS account and AWS credentials. You are free to 187 | configure credentials however you like as long as an access key ID and secret 188 | access key are available. These instructions utilise 189 | [aws-vault](https://github.com/99designs/aws-vault) which makes credential 190 | management easy and secure. 191 | 192 | To run the full build, including unit and integration tests, execute: 193 | 194 | ```shell 195 | aws-vault exec -- ./go 196 | ``` 197 | 198 | To run the unit tests, execute: 199 | 200 | ```shell 201 | aws-vault exec -- ./go test:unit 202 | ``` 203 | 204 | To run the integration tests, execute: 205 | 206 | ```shell 207 | aws-vault exec -- ./go test:integration 208 | ``` 209 | 210 | To provision the module prerequisites: 211 | 212 | ```shell 213 | aws-vault exec -- ./go deployment:prerequisites:provision[] 214 | ``` 215 | 216 | To provision the module contents: 217 | 218 | ```shell 219 | aws-vault exec -- ./go deployment:root:provision[] 220 | ``` 221 | 222 | To destroy the module contents: 223 | 224 | ```shell 225 | aws-vault exec -- ./go deployment:root:destroy[] 226 | ``` 227 | 228 | To destroy the module prerequisites: 229 | 230 | ```shell 231 | aws-vault exec -- ./go deployment:prerequisites:destroy[] 232 | ``` 233 | 234 | Configuration parameters can be overridden via environment variables. For 235 | example, to run the unit tests with a seed of `"testing"`, execute: 236 | 237 | ```shell 238 | SEED=testing aws-vault exec -- ./go test:unit 239 | ``` 240 | 241 | When a seed is provided via an environment variable, infrastructure will not be 242 | destroyed at the end of test execution. This can be useful during development 243 | to avoid lengthy provision and destroy cycles. 244 | 245 | To subsequently destroy unit test infrastructure for a given seed: 246 | 247 | ```shell 248 | FORCE_DESTROY=yes SEED=testing aws-vault exec -- ./go test:unit 249 | ``` 250 | 251 | ### Common Tasks 252 | 253 | #### Generating an SSH key pair 254 | 255 | To generate an SSH key pair: 256 | 257 | ```shell 258 | ssh-keygen -m PEM -t rsa -b 4096 -C integration-test@example.com -N '' -f config/secrets/keys/bastion/ssh 259 | ``` 260 | 261 | #### Generating a self-signed certificate 262 | 263 | To generate a self signed certificate: 264 | 265 | ```shell 266 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 267 | ``` 268 | 269 | To decrypt the resulting key: 270 | 271 | ```shell 272 | openssl rsa -in key.pem -out ssl.key 273 | ``` 274 | 275 | #### Managing CircleCI keys 276 | 277 | To encrypt a GPG key for use by CircleCI: 278 | 279 | ```shell 280 | openssl aes-256-cbc \ 281 | -e \ 282 | -md sha1 \ 283 | -in ./config/secrets/ci/gpg.private \ 284 | -out ./.circleci/gpg.private.enc \ 285 | -k "" 286 | ``` 287 | 288 | To check decryption is working correctly: 289 | 290 | ```shell 291 | openssl aes-256-cbc \ 292 | -d \ 293 | -md sha1 \ 294 | -in ./.circleci/gpg.private.enc \ 295 | -k "" 296 | ``` 297 | 298 | Contributing 299 | ------------ 300 | 301 | Bug reports and pull requests are welcome on GitHub at 302 | https://github.com/infrablocks/terraform-aws-base-networking. This project is 303 | intended to be a safe, welcoming space for collaboration, and contributors are 304 | expected to adhere to the 305 | [Contributor Covenant](http://contributor-covenant.org) code of conduct. 306 | 307 | License 308 | ------- 309 | 310 | The library is available as open source under the terms of the 311 | [MIT License](http://opensource.org/licenses/MIT). 312 | -------------------------------------------------------------------------------- /spec/integration/full_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'netaddr' 5 | 6 | # rubocop:disable RSpec/MultipleMemoizedHelpers 7 | describe 'full example' do 8 | let(:component) do 9 | var(role: :full, name: 'component') 10 | end 11 | let(:dep_id) do 12 | var(role: :full, name: 'deployment_identifier') 13 | end 14 | let(:domain_name) do 15 | var(role: :full, name: 'domain_name') 16 | end 17 | let(:availability_zones) do 18 | var(role: :full, name: 'availability_zones') 19 | end 20 | let(:vpc_cidr) do 21 | var(role: :full, name: 'vpc_cidr') 22 | end 23 | let(:dependencies) do 24 | %w[other_vpc_1 other_vpc_2] 25 | end 26 | let(:dependencies_string) do 27 | 'other_vpc_1,other_vpc_2' 28 | end 29 | 30 | before(:context) do 31 | apply(role: :full) 32 | end 33 | 34 | after(:context) do 35 | destroy( 36 | role: :full, 37 | only_if: -> { !ENV['FORCE_DESTROY'].nil? || ENV['SEED'].nil? } 38 | ) 39 | end 40 | 41 | describe 'VPC' do 42 | subject(:created_vpc) { vpc("vpc-#{component}-#{dep_id}") } 43 | 44 | it { is_expected.to(exist) } 45 | 46 | it { is_expected.to(have_tag('Component').value(component)) } 47 | it { is_expected.to(have_tag('DeploymentIdentifier').value(dep_id)) } 48 | it { is_expected.to(have_tag('Dependencies').value(dependencies_string)) } 49 | 50 | its(:cidr_block) { is_expected.to(eq(vpc_cidr)) } 51 | 52 | it 'exposes the VPC ID as an output' do 53 | expected_vpc_id = created_vpc.vpc_id 54 | actual_vpc_id = output(role: :full, name: 'vpc_id') 55 | 56 | expect(actual_vpc_id).to(eq(expected_vpc_id)) 57 | end 58 | 59 | it 'exposes the VPC CIDR block as an output' do 60 | expected_vpc_cidr = created_vpc.cidr_block 61 | actual_vpc_cidr = output(role: :full, name: 'vpc_cidr') 62 | 63 | expect(actual_vpc_cidr).to(eq(expected_vpc_cidr)) 64 | end 65 | 66 | it 'enables DNS hostnames' do 67 | full_resource = Aws::EC2::Vpc.new(created_vpc.id) 68 | dns_hostnames = 69 | full_resource.describe_attribute({ attribute: 'enableDnsHostnames' }) 70 | 71 | expect(dns_hostnames.enable_dns_hostnames.value).to be(true) 72 | end 73 | 74 | it 'exposes the availability zones as an output' do 75 | expected_availability_zones = availability_zones 76 | actual_availability_zones = 77 | output(role: :full, name: 'availability_zones') 78 | 79 | expect(actual_availability_zones).to(eq(expected_availability_zones)) 80 | end 81 | 82 | it 'exposes the number of availability zones as an output' do 83 | expected_count = availability_zones.count 84 | actual_count = 85 | output(role: :full, name: 'number_of_availability_zones') 86 | 87 | expect(actual_count).to(eq(expected_count)) 88 | end 89 | 90 | it 'associates the supplied private hosted with the VPC' do 91 | private_zone_id = output(role: :full, name: 'private_zone_id') 92 | private_hosted_zone = 93 | route53_client.get_hosted_zone({ id: private_zone_id }) 94 | 95 | expect(private_hosted_zone.vp_cs.map(&:vpc_id)) 96 | .to(include(created_vpc.id)) 97 | end 98 | end 99 | 100 | describe 'IGW' do 101 | subject(:created_igw) do 102 | internet_gateway("igw-#{component}-#{dep_id}") 103 | end 104 | 105 | it { is_expected.to exist } 106 | it { is_expected.to have_tag('Component').value(component) } 107 | it { is_expected.to have_tag('DeploymentIdentifier').value(dep_id) } 108 | it { is_expected.to have_tag('Tier').value('public') } 109 | 110 | it 'is attached to the created VPC' do 111 | vpc_igw = vpc("vpc-#{component}-#{dep_id}") 112 | .internet_gateways.first 113 | 114 | expect(created_igw.internet_gateway_id) 115 | .to(eq(vpc_igw.internet_gateway_id)) 116 | end 117 | 118 | it 'exposes the IGW ID as an output' do 119 | expected_igw_id = created_igw.internet_gateway_id 120 | actual_igw_id = output(role: :full, name: 'internet_gateway_id') 121 | 122 | expect(actual_igw_id).to(eq(expected_igw_id)) 123 | end 124 | end 125 | 126 | describe 'NAT gateways' do 127 | let :created_vpc do 128 | vpc("vpc-#{component}-#{dep_id}") 129 | end 130 | let :public_subnets do 131 | availability_zones.map do |zone| 132 | subnet("public-subnet-#{component}-#{dep_id}-#{zone}") 133 | end 134 | end 135 | 136 | let :nat_gateways do 137 | public_subnets.map do |subnet| 138 | ec2_client 139 | .describe_nat_gateways( 140 | { 141 | filter: [ 142 | { name: 'vpc-id', 143 | values: [created_vpc.id] }, 144 | { name: 'subnet-id', 145 | values: [subnet.id] }, 146 | { name: 'state', values: ['available'] } 147 | ] 148 | } 149 | ) 150 | .nat_gateways 151 | .first 152 | end.compact 153 | end 154 | 155 | it 'creates a NAT gateway in each public subnet' do 156 | expect(nat_gateways.length).to(eq(availability_zones.length)) 157 | end 158 | 159 | it 'associates an EIP to each NAT gateway and exposes as an output' do 160 | nat_gateways.zip(output(role: :full, name: 'nat_public_ips')) 161 | .each do |nat_gateway, nat_public_ip_output| 162 | expect(nat_gateway.nat_gateway_addresses.map(&:public_ip)) 163 | .to(include(nat_public_ip_output)) 164 | end 165 | end 166 | end 167 | 168 | describe 'private' do 169 | let :created_vpc do 170 | vpc("vpc-#{component}-#{dep_id}") 171 | end 172 | let :public_subnets do 173 | availability_zones.map do |zone| 174 | subnet("public-subnet-#{component}-#{dep_id}-#{zone}") 175 | end 176 | end 177 | let :private_subnets do 178 | availability_zones.map do |zone| 179 | subnet("private-subnet-#{component}-#{dep_id}-#{zone}") 180 | end 181 | end 182 | let :private_route_tables do 183 | availability_zones.map do |zone| 184 | route_table("private-routetable-#{component}-#{dep_id}-#{zone}") 185 | end 186 | end 187 | let :nat_gateways do 188 | public_subnets.map do |subnet| 189 | ec2_client 190 | .describe_nat_gateways( 191 | { 192 | filter: [ 193 | { name: 'vpc-id', 194 | values: [created_vpc.id] }, 195 | { name: 'subnet-id', 196 | values: [subnet.id] }, 197 | { name: 'state', values: ['available'] } 198 | ] 199 | } 200 | ) 201 | .nat_gateways 202 | .single_resource(subnet.id) 203 | end 204 | end 205 | 206 | describe 'subnets' do 207 | it 'has a Component tag on each subnet' do 208 | expect(private_subnets) 209 | .to(all(have_tag('Component').value(component))) 210 | end 211 | 212 | it 'has a DeploymentIdentifier tag on each subnet' do 213 | expect(private_subnets) 214 | .to(all(have_tag('DeploymentIdentifier').value(dep_id))) 215 | end 216 | 217 | it 'has a Tier of private on each subnet' do 218 | expect(private_subnets) 219 | .to(all(have_tag('Tier').value('private'))) 220 | end 221 | 222 | it 'associates each subnet with the created VPC' do 223 | private_subnets.each do |subnet| 224 | expect(subnet.vpc_id) 225 | .to(eq(created_vpc.id)) 226 | end 227 | end 228 | 229 | it 'distributes subnets across availability zones' do 230 | availability_zones.map do |zone| 231 | expect(private_subnets.map(&:availability_zone)).to(include(zone)) 232 | end 233 | end 234 | 235 | # rubocop:disable RSpec/MultipleExpectations 236 | it 'uses unique /24 networks relative to the VPC CIDR for each subnet' do 237 | cidr = NetAddr::IPv4Net.parse(vpc_cidr) 238 | 239 | private_subnets.each do |subnet| 240 | subnet_cidr = NetAddr::IPv4Net.parse(subnet.cidr_block) 241 | 242 | expect(subnet_cidr.netmask.cmp(NetAddr::Mask32.parse('/24'))) 243 | .to(eq(0)) 244 | expect(cidr.rel(subnet_cidr)).to(eq(1)) 245 | end 246 | 247 | expect(private_subnets.map(&:cidr_block).uniq.length) 248 | .to(eq(private_subnets.length)) 249 | end 250 | # rubocop:enable RSpec/MultipleExpectations 251 | 252 | it 'exposes the private subnet IDs as an output' do 253 | expected_private_subnet_ids = private_subnets.map(&:id) 254 | actual_private_subnet_ids = 255 | output(role: :full, name: 'private_subnet_ids') 256 | 257 | expect(actual_private_subnet_ids).to(eq(expected_private_subnet_ids)) 258 | end 259 | 260 | it 'exposes the private subnet CIDR blocks as an output' do 261 | expected_private_subnet_ids = private_subnets.map(&:cidr_block) 262 | actual_private_subnet_ids = 263 | output(role: :full, name: 'private_subnet_cidr_blocks') 264 | 265 | expect(actual_private_subnet_ids).to(eq(expected_private_subnet_ids)) 266 | end 267 | end 268 | 269 | describe 'route tables' do 270 | it 'has a Component tag on each route table' do 271 | expect(private_route_tables) 272 | .to(all(have_tag('Component').value(component))) 273 | end 274 | 275 | it 'has a DeploymentIdentifier on each route table' do 276 | expect(private_route_tables) 277 | .to(all(have_tag('DeploymentIdentifier').value(dep_id))) 278 | end 279 | 280 | it 'has a Tier of private on each route table' do 281 | expect(private_route_tables) 282 | .to(all(have_tag('Tier').value('private'))) 283 | end 284 | 285 | it 'associates each route table to the created VPC' do 286 | private_route_tables.each do |route_table| 287 | expect(route_table.vpc_id).to(eq(created_vpc.id)) 288 | end 289 | end 290 | 291 | it 'includes a route to the NAT gateway for all internet traffic' do 292 | private_route_tables 293 | .zip(nat_gateways) 294 | .each do |route_table, nat_gateway| 295 | expect(route_table) 296 | .to(have_route('0.0.0.0/0') 297 | .target(nat: nat_gateway.nat_gateway_id)) 298 | end 299 | end 300 | 301 | it 'associates each route table to each subnet' do 302 | private_route_tables 303 | .zip(private_subnets) 304 | .each do |route_table, subnet| 305 | expect(route_table).to(have_subnet(subnet.id)) 306 | end 307 | end 308 | 309 | it 'exposes the private route table ids as an output' do 310 | expect(output(role: :full, name: 'private_route_table_ids')) 311 | .to(eq(private_route_tables.map(&:id))) 312 | end 313 | end 314 | end 315 | 316 | describe 'public' do 317 | let :created_vpc do 318 | vpc("vpc-#{component}-#{dep_id}") 319 | end 320 | let :public_subnets do 321 | availability_zones.map do |zone| 322 | subnet("public-subnet-#{component}-#{dep_id}-#{zone}") 323 | end 324 | end 325 | let :public_route_tables do 326 | availability_zones.map do |zone| 327 | route_table("public-routetable-#{component}-#{dep_id}-#{zone}") 328 | end 329 | end 330 | 331 | describe 'subnets' do 332 | it 'has a Component tag on each subnet' do 333 | expect(public_subnets) 334 | .to(all(have_tag('Component').value(component))) 335 | end 336 | 337 | it 'has a DeploymentIdentifier tag on each subnet' do 338 | expect(public_subnets) 339 | .to(all(have_tag('DeploymentIdentifier').value(dep_id))) 340 | end 341 | 342 | it 'has a Tier of public on each subnet' do 343 | expect(public_subnets) 344 | .to(all(have_tag('Tier').value('public'))) 345 | end 346 | 347 | it 'associates each subnet with the created VPC' do 348 | public_subnets.each do |subnet| 349 | expect(subnet.vpc_id) 350 | .to(eq(created_vpc.id)) 351 | end 352 | end 353 | 354 | it 'distributes subnets across availability zones' do 355 | availability_zones.map do |zone| 356 | expect(public_subnets.map(&:availability_zone)).to(include(zone)) 357 | end 358 | end 359 | 360 | # rubocop:disable RSpec/MultipleExpectations 361 | it 'uses unique /24 networks relative to the VPC CIDR for each subnet' do 362 | cidr = NetAddr::IPv4Net.parse(vpc_cidr) 363 | 364 | public_subnets.each do |subnet| 365 | subnet_cidr = NetAddr::IPv4Net.parse(subnet.cidr_block) 366 | 367 | expect(subnet_cidr.netmask.cmp(NetAddr::Mask32.parse('/24'))) 368 | .to(eq(0)) 369 | expect(cidr.rel(subnet_cidr)).to(eq(1)) 370 | end 371 | 372 | expect(public_subnets.map(&:cidr_block).uniq.length) 373 | .to(eq(public_subnets.length)) 374 | end 375 | # rubocop:enable RSpec/MultipleExpectations 376 | 377 | it 'exposes the public subnet IDs as an output' do 378 | expected_public_subnet_ids = public_subnets.map(&:id) 379 | actual_public_subnet_ids = 380 | output(role: :full, name: 'public_subnet_ids') 381 | 382 | expect(actual_public_subnet_ids).to(eq(expected_public_subnet_ids)) 383 | end 384 | 385 | it 'exposes the public subnet CIDR blocks as an output' do 386 | expected_public_subnet_ids = public_subnets.map(&:cidr_block) 387 | actual_public_subnet_ids = 388 | output(role: :full, name: 'public_subnet_cidr_blocks') 389 | 390 | expect(actual_public_subnet_ids).to(eq(expected_public_subnet_ids)) 391 | end 392 | end 393 | 394 | describe 'route tables' do 395 | it 'has a Component tag on each route table' do 396 | expect(public_route_tables) 397 | .to(all(have_tag('Component').value(component))) 398 | end 399 | 400 | it 'has a DeploymentIdentifier on each route table' do 401 | expect(public_route_tables) 402 | .to(all(have_tag('DeploymentIdentifier') 403 | .value(dep_id))) 404 | end 405 | 406 | it 'has a Tier of public on each route table' do 407 | expect(public_route_tables) 408 | .to(all(have_tag('Tier').value('public'))) 409 | end 410 | 411 | it 'associates each route table to the created VPC' do 412 | public_route_tables.each do |route_table| 413 | expect(route_table.vpc_id).to(eq(created_vpc.id)) 414 | end 415 | end 416 | 417 | it 'has a route to the internet gateway for all internet traffic' do 418 | created_igw = internet_gateway("igw-#{component}-#{dep_id}") 419 | expect(public_route_tables) 420 | .to(all(have_route('0.0.0.0/0') 421 | .target(gateway: created_igw.id))) 422 | end 423 | 424 | it 'associates each route table to each subnet' do 425 | public_route_tables 426 | .zip(public_subnets) 427 | .each do |route_table, subnet| 428 | expect(route_table).to(have_subnet(subnet.id)) 429 | end 430 | end 431 | 432 | it 'exposes the public route tables as an output' do 433 | expect(output(role: :full, name: 'public_route_table_ids')) 434 | .to(eq(public_route_tables.map(&:id))) 435 | end 436 | end 437 | end 438 | end 439 | # rubocop:enable RSpec/MultipleMemoizedHelpers 440 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (7.1.3.4) 5 | base64 6 | bigdecimal 7 | concurrent-ruby (~> 1.0, >= 1.0.2) 8 | connection_pool (>= 2.2.5) 9 | drb 10 | i18n (>= 1.6, < 2) 11 | minitest (>= 5.1) 12 | mutex_m 13 | tzinfo (~> 2.0) 14 | addressable (2.8.7) 15 | public_suffix (>= 2.0.2, < 7.0) 16 | ast (2.4.2) 17 | aws-eventstream (1.3.0) 18 | aws-partitions (1.958.0) 19 | aws-sdk (3.2.0) 20 | aws-sdk-resources (~> 3) 21 | aws-sdk-accessanalyzer (1.54.0) 22 | aws-sdk-core (~> 3, >= 3.201.0) 23 | aws-sigv4 (~> 1.1) 24 | aws-sdk-account (1.28.0) 25 | aws-sdk-core (~> 3, >= 3.201.0) 26 | aws-sigv4 (~> 1.1) 27 | aws-sdk-acm (1.74.0) 28 | aws-sdk-core (~> 3, >= 3.201.0) 29 | aws-sigv4 (~> 1.5) 30 | aws-sdk-acmpca (1.76.0) 31 | aws-sdk-core (~> 3, >= 3.201.0) 32 | aws-sigv4 (~> 1.5) 33 | aws-sdk-amplify (1.65.0) 34 | aws-sdk-core (~> 3, >= 3.201.0) 35 | aws-sigv4 (~> 1.5) 36 | aws-sdk-amplifybackend (1.36.0) 37 | aws-sdk-core (~> 3, >= 3.201.0) 38 | aws-sigv4 (~> 1.1) 39 | aws-sdk-amplifyuibuilder (1.30.0) 40 | aws-sdk-core (~> 3, >= 3.201.0) 41 | aws-sigv4 (~> 1.1) 42 | aws-sdk-apigateway (1.101.0) 43 | aws-sdk-core (~> 3, >= 3.201.0) 44 | aws-sigv4 (~> 1.5) 45 | aws-sdk-apigatewaymanagementapi (1.48.0) 46 | aws-sdk-core (~> 3, >= 3.201.0) 47 | aws-sigv4 (~> 1.1) 48 | aws-sdk-apigatewayv2 (1.60.0) 49 | aws-sdk-core (~> 3, >= 3.201.0) 50 | aws-sigv4 (~> 1.1) 51 | aws-sdk-appconfig (1.51.0) 52 | aws-sdk-core (~> 3, >= 3.201.0) 53 | aws-sigv4 (~> 1.5) 54 | aws-sdk-appconfigdata (1.25.0) 55 | aws-sdk-core (~> 3, >= 3.201.0) 56 | aws-sigv4 (~> 1.1) 57 | aws-sdk-appfabric (1.14.0) 58 | aws-sdk-core (~> 3, >= 3.201.0) 59 | aws-sigv4 (~> 1.1) 60 | aws-sdk-appflow (1.62.0) 61 | aws-sdk-core (~> 3, >= 3.201.0) 62 | aws-sigv4 (~> 1.5) 63 | aws-sdk-appintegrationsservice (1.36.0) 64 | aws-sdk-core (~> 3, >= 3.201.0) 65 | aws-sigv4 (~> 1.5) 66 | aws-sdk-applicationautoscaling (1.90.0) 67 | aws-sdk-core (~> 3, >= 3.201.0) 68 | aws-sigv4 (~> 1.5) 69 | aws-sdk-applicationcostprofiler (1.28.0) 70 | aws-sdk-core (~> 3, >= 3.201.0) 71 | aws-sigv4 (~> 1.5) 72 | aws-sdk-applicationdiscoveryservice (1.71.0) 73 | aws-sdk-core (~> 3, >= 3.201.0) 74 | aws-sigv4 (~> 1.5) 75 | aws-sdk-applicationinsights (1.51.0) 76 | aws-sdk-core (~> 3, >= 3.201.0) 77 | aws-sigv4 (~> 1.5) 78 | aws-sdk-applicationsignals (1.4.0) 79 | aws-sdk-core (~> 3, >= 3.201.0) 80 | aws-sigv4 (~> 1.5) 81 | aws-sdk-appmesh (1.66.0) 82 | aws-sdk-core (~> 3, >= 3.201.0) 83 | aws-sigv4 (~> 1.1) 84 | aws-sdk-appregistry (1.39.0) 85 | aws-sdk-core (~> 3, >= 3.201.0) 86 | aws-sigv4 (~> 1.5) 87 | aws-sdk-apprunner (1.44.0) 88 | aws-sdk-core (~> 3, >= 3.201.0) 89 | aws-sigv4 (~> 1.5) 90 | aws-sdk-appstream (1.92.0) 91 | aws-sdk-core (~> 3, >= 3.201.0) 92 | aws-sigv4 (~> 1.5) 93 | aws-sdk-appsync (1.82.0) 94 | aws-sdk-core (~> 3, >= 3.201.0) 95 | aws-sigv4 (~> 1.5) 96 | aws-sdk-apptest (1.3.0) 97 | aws-sdk-core (~> 3, >= 3.201.0) 98 | aws-sigv4 (~> 1.5) 99 | aws-sdk-arczonalshift (1.19.0) 100 | aws-sdk-core (~> 3, >= 3.201.0) 101 | aws-sigv4 (~> 1.5) 102 | aws-sdk-artifact (1.7.0) 103 | aws-sdk-core (~> 3, >= 3.201.0) 104 | aws-sigv4 (~> 1.5) 105 | aws-sdk-athena (1.89.0) 106 | aws-sdk-core (~> 3, >= 3.201.0) 107 | aws-sigv4 (~> 1.5) 108 | aws-sdk-auditmanager (1.51.0) 109 | aws-sdk-core (~> 3, >= 3.201.0) 110 | aws-sigv4 (~> 1.5) 111 | aws-sdk-augmentedairuntime (1.42.0) 112 | aws-sdk-core (~> 3, >= 3.201.0) 113 | aws-sigv4 (~> 1.5) 114 | aws-sdk-autoscaling (1.113.0) 115 | aws-sdk-core (~> 3, >= 3.201.0) 116 | aws-sigv4 (~> 1.5) 117 | aws-sdk-autoscalingplans (1.59.0) 118 | aws-sdk-core (~> 3, >= 3.201.0) 119 | aws-sigv4 (~> 1.5) 120 | aws-sdk-b2bi (1.14.0) 121 | aws-sdk-core (~> 3, >= 3.201.0) 122 | aws-sigv4 (~> 1.5) 123 | aws-sdk-backup (1.73.0) 124 | aws-sdk-core (~> 3, >= 3.201.0) 125 | aws-sigv4 (~> 1.5) 126 | aws-sdk-backupgateway (1.24.0) 127 | aws-sdk-core (~> 3, >= 3.201.0) 128 | aws-sigv4 (~> 1.1) 129 | aws-sdk-batch (1.94.0) 130 | aws-sdk-core (~> 3, >= 3.201.0) 131 | aws-sigv4 (~> 1.5) 132 | aws-sdk-bcmdataexports (1.8.0) 133 | aws-sdk-core (~> 3, >= 3.201.0) 134 | aws-sigv4 (~> 1.1) 135 | aws-sdk-bedrock (1.13.0) 136 | aws-sdk-core (~> 3, >= 3.201.0) 137 | aws-sigv4 (~> 1.1) 138 | aws-sdk-bedrockagent (1.19.0) 139 | aws-sdk-core (~> 3, >= 3.201.0) 140 | aws-sigv4 (~> 1.1) 141 | aws-sdk-bedrockagentruntime (1.17.0) 142 | aws-sdk-core (~> 3, >= 3.201.0) 143 | aws-sigv4 (~> 1.1) 144 | aws-sdk-bedrockruntime (1.17.0) 145 | aws-sdk-core (~> 3, >= 3.201.0) 146 | aws-sigv4 (~> 1.5) 147 | aws-sdk-billingconductor (1.27.0) 148 | aws-sdk-core (~> 3, >= 3.201.0) 149 | aws-sigv4 (~> 1.1) 150 | aws-sdk-braket (1.40.0) 151 | aws-sdk-core (~> 3, >= 3.201.0) 152 | aws-sigv4 (~> 1.1) 153 | aws-sdk-budgets (1.71.0) 154 | aws-sdk-core (~> 3, >= 3.201.0) 155 | aws-sigv4 (~> 1.5) 156 | aws-sdk-chatbot (1.9.0) 157 | aws-sdk-core (~> 3, >= 3.201.0) 158 | aws-sigv4 (~> 1.5) 159 | aws-sdk-chime (1.89.0) 160 | aws-sdk-core (~> 3, >= 3.201.0) 161 | aws-sigv4 (~> 1.5) 162 | aws-sdk-chimesdkidentity (1.30.0) 163 | aws-sdk-core (~> 3, >= 3.201.0) 164 | aws-sigv4 (~> 1.5) 165 | aws-sdk-chimesdkmediapipelines (1.26.0) 166 | aws-sdk-core (~> 3, >= 3.201.0) 167 | aws-sigv4 (~> 1.5) 168 | aws-sdk-chimesdkmeetings (1.36.0) 169 | aws-sdk-core (~> 3, >= 3.201.0) 170 | aws-sigv4 (~> 1.5) 171 | aws-sdk-chimesdkmessaging (1.36.0) 172 | aws-sdk-core (~> 3, >= 3.201.0) 173 | aws-sigv4 (~> 1.5) 174 | aws-sdk-chimesdkvoice (1.26.0) 175 | aws-sdk-core (~> 3, >= 3.201.0) 176 | aws-sigv4 (~> 1.5) 177 | aws-sdk-cleanrooms (1.27.0) 178 | aws-sdk-core (~> 3, >= 3.201.0) 179 | aws-sigv4 (~> 1.5) 180 | aws-sdk-cleanroomsml (1.10.0) 181 | aws-sdk-core (~> 3, >= 3.201.0) 182 | aws-sigv4 (~> 1.5) 183 | aws-sdk-cloud9 (1.74.0) 184 | aws-sdk-core (~> 3, >= 3.201.0) 185 | aws-sigv4 (~> 1.5) 186 | aws-sdk-cloudcontrolapi (1.26.0) 187 | aws-sdk-core (~> 3, >= 3.201.0) 188 | aws-sigv4 (~> 1.1) 189 | aws-sdk-clouddirectory (1.61.0) 190 | aws-sdk-core (~> 3, >= 3.201.0) 191 | aws-sigv4 (~> 1.5) 192 | aws-sdk-cloudformation (1.114.0) 193 | aws-sdk-core (~> 3, >= 3.201.0) 194 | aws-sigv4 (~> 1.5) 195 | aws-sdk-cloudfront (1.96.0) 196 | aws-sdk-core (~> 3, >= 3.201.0) 197 | aws-sigv4 (~> 1.5) 198 | aws-sdk-cloudfrontkeyvaluestore (1.10.0) 199 | aws-sdk-core (~> 3, >= 3.201.0) 200 | aws-sigv4 (~> 1.1) 201 | aws-sdk-cloudhsm (1.58.0) 202 | aws-sdk-core (~> 3, >= 3.201.0) 203 | aws-sigv4 (~> 1.5) 204 | aws-sdk-cloudhsmv2 (1.62.0) 205 | aws-sdk-core (~> 3, >= 3.201.0) 206 | aws-sigv4 (~> 1.5) 207 | aws-sdk-cloudsearch (1.60.0) 208 | aws-sdk-core (~> 3, >= 3.201.0) 209 | aws-sigv4 (~> 1.5) 210 | aws-sdk-cloudsearchdomain (1.47.0) 211 | aws-sdk-core (~> 3, >= 3.201.0) 212 | aws-sigv4 (~> 1.5) 213 | aws-sdk-cloudtrail (1.85.0) 214 | aws-sdk-core (~> 3, >= 3.201.0) 215 | aws-sigv4 (~> 1.5) 216 | aws-sdk-cloudtraildata (1.16.0) 217 | aws-sdk-core (~> 3, >= 3.201.0) 218 | aws-sigv4 (~> 1.1) 219 | aws-sdk-cloudwatch (1.96.0) 220 | aws-sdk-core (~> 3, >= 3.201.0) 221 | aws-sigv4 (~> 1.5) 222 | aws-sdk-cloudwatchevents (1.77.0) 223 | aws-sdk-core (~> 3, >= 3.201.0) 224 | aws-sigv4 (~> 1.5) 225 | aws-sdk-cloudwatchevidently (1.28.0) 226 | aws-sdk-core (~> 3, >= 3.201.0) 227 | aws-sigv4 (~> 1.1) 228 | aws-sdk-cloudwatchlogs (1.87.0) 229 | aws-sdk-core (~> 3, >= 3.201.0) 230 | aws-sigv4 (~> 1.5) 231 | aws-sdk-cloudwatchrum (1.26.0) 232 | aws-sdk-core (~> 3, >= 3.201.0) 233 | aws-sigv4 (~> 1.1) 234 | aws-sdk-codeartifact (1.47.0) 235 | aws-sdk-core (~> 3, >= 3.201.0) 236 | aws-sigv4 (~> 1.5) 237 | aws-sdk-codebuild (1.122.0) 238 | aws-sdk-core (~> 3, >= 3.201.0) 239 | aws-sigv4 (~> 1.5) 240 | aws-sdk-codecatalyst (1.23.0) 241 | aws-sdk-core (~> 3, >= 3.201.0) 242 | aws-sdk-codecommit (1.72.0) 243 | aws-sdk-core (~> 3, >= 3.201.0) 244 | aws-sigv4 (~> 1.5) 245 | aws-sdk-codeconnections (1.7.0) 246 | aws-sdk-core (~> 3, >= 3.201.0) 247 | aws-sigv4 (~> 1.5) 248 | aws-sdk-codedeploy (1.72.0) 249 | aws-sdk-core (~> 3, >= 3.201.0) 250 | aws-sigv4 (~> 1.5) 251 | aws-sdk-codeguruprofiler (1.42.0) 252 | aws-sdk-core (~> 3, >= 3.201.0) 253 | aws-sigv4 (~> 1.1) 254 | aws-sdk-codegurureviewer (1.52.0) 255 | aws-sdk-core (~> 3, >= 3.201.0) 256 | aws-sigv4 (~> 1.5) 257 | aws-sdk-codegurusecurity (1.17.0) 258 | aws-sdk-core (~> 3, >= 3.201.0) 259 | aws-sigv4 (~> 1.1) 260 | aws-sdk-codepipeline (1.77.0) 261 | aws-sdk-core (~> 3, >= 3.201.0) 262 | aws-sigv4 (~> 1.5) 263 | aws-sdk-codestar (1.58.0) 264 | aws-sdk-core (~> 3, >= 3.201.0) 265 | aws-sigv4 (~> 1.5) 266 | aws-sdk-codestarconnections (1.48.0) 267 | aws-sdk-core (~> 3, >= 3.201.0) 268 | aws-sigv4 (~> 1.5) 269 | aws-sdk-codestarnotifications (1.39.0) 270 | aws-sdk-core (~> 3, >= 3.201.0) 271 | aws-sigv4 (~> 1.5) 272 | aws-sdk-cognitoidentity (1.60.0) 273 | aws-sdk-core (~> 3, >= 3.201.0) 274 | aws-sigv4 (~> 1.5) 275 | aws-sdk-cognitoidentityprovider (1.97.0) 276 | aws-sdk-core (~> 3, >= 3.201.0) 277 | aws-sigv4 (~> 1.5) 278 | aws-sdk-cognitosync (1.55.0) 279 | aws-sdk-core (~> 3, >= 3.201.0) 280 | aws-sigv4 (~> 1.5) 281 | aws-sdk-comprehend (1.87.0) 282 | aws-sdk-core (~> 3, >= 3.201.0) 283 | aws-sigv4 (~> 1.5) 284 | aws-sdk-comprehendmedical (1.57.0) 285 | aws-sdk-core (~> 3, >= 3.201.0) 286 | aws-sigv4 (~> 1.5) 287 | aws-sdk-computeoptimizer (1.61.0) 288 | aws-sdk-core (~> 3, >= 3.201.0) 289 | aws-sigv4 (~> 1.5) 290 | aws-sdk-configservice (1.113.0) 291 | aws-sdk-core (~> 3, >= 3.201.0) 292 | aws-sigv4 (~> 1.5) 293 | aws-sdk-connect (1.169.0) 294 | aws-sdk-core (~> 3, >= 3.201.0) 295 | aws-sigv4 (~> 1.5) 296 | aws-sdk-connectcampaignservice (1.21.0) 297 | aws-sdk-core (~> 3, >= 3.201.0) 298 | aws-sigv4 (~> 1.1) 299 | aws-sdk-connectcases (1.28.0) 300 | aws-sdk-core (~> 3, >= 3.201.0) 301 | aws-sigv4 (~> 1.1) 302 | aws-sdk-connectcontactlens (1.31.0) 303 | aws-sdk-core (~> 3, >= 3.201.0) 304 | aws-sigv4 (~> 1.5) 305 | aws-sdk-connectparticipant (1.49.0) 306 | aws-sdk-core (~> 3, >= 3.201.0) 307 | aws-sigv4 (~> 1.5) 308 | aws-sdk-connectwisdomservice (1.34.0) 309 | aws-sdk-core (~> 3, >= 3.201.0) 310 | aws-sigv4 (~> 1.1) 311 | aws-sdk-controlcatalog (1.6.0) 312 | aws-sdk-core (~> 3, >= 3.201.0) 313 | aws-sigv4 (~> 1.1) 314 | aws-sdk-controltower (1.26.0) 315 | aws-sdk-core (~> 3, >= 3.201.0) 316 | aws-sigv4 (~> 1.5) 317 | aws-sdk-core (3.201.3) 318 | aws-eventstream (~> 1, >= 1.3.0) 319 | aws-partitions (~> 1, >= 1.651.0) 320 | aws-sigv4 (~> 1.8) 321 | jmespath (~> 1, >= 1.6.1) 322 | aws-sdk-costandusagereportservice (1.61.0) 323 | aws-sdk-core (~> 3, >= 3.201.0) 324 | aws-sigv4 (~> 1.5) 325 | aws-sdk-costexplorer (1.105.0) 326 | aws-sdk-core (~> 3, >= 3.201.0) 327 | aws-sigv4 (~> 1.5) 328 | aws-sdk-costoptimizationhub (1.10.0) 329 | aws-sdk-core (~> 3, >= 3.201.0) 330 | aws-sigv4 (~> 1.5) 331 | aws-sdk-customerprofiles (1.47.0) 332 | aws-sdk-core (~> 3, >= 3.201.0) 333 | aws-sigv4 (~> 1.5) 334 | aws-sdk-databasemigrationservice (1.100.0) 335 | aws-sdk-core (~> 3, >= 3.201.0) 336 | aws-sigv4 (~> 1.5) 337 | aws-sdk-dataexchange (1.52.0) 338 | aws-sdk-core (~> 3, >= 3.201.0) 339 | aws-sigv4 (~> 1.1) 340 | aws-sdk-datapipeline (1.55.0) 341 | aws-sdk-core (~> 3, >= 3.201.0) 342 | aws-sigv4 (~> 1.5) 343 | aws-sdk-datasync (1.83.0) 344 | aws-sdk-core (~> 3, >= 3.201.0) 345 | aws-sigv4 (~> 1.5) 346 | aws-sdk-datazone (1.17.0) 347 | aws-sdk-core (~> 3, >= 3.201.0) 348 | aws-sigv4 (~> 1.1) 349 | aws-sdk-dax (1.58.0) 350 | aws-sdk-core (~> 3, >= 3.201.0) 351 | aws-sigv4 (~> 1.5) 352 | aws-sdk-deadline (1.7.0) 353 | aws-sdk-core (~> 3, >= 3.201.0) 354 | aws-sigv4 (~> 1.1) 355 | aws-sdk-detective (1.53.0) 356 | aws-sdk-core (~> 3, >= 3.201.0) 357 | aws-sigv4 (~> 1.5) 358 | aws-sdk-devicefarm (1.72.0) 359 | aws-sdk-core (~> 3, >= 3.201.0) 360 | aws-sigv4 (~> 1.5) 361 | aws-sdk-devopsguru (1.47.0) 362 | aws-sdk-core (~> 3, >= 3.201.0) 363 | aws-sigv4 (~> 1.5) 364 | aws-sdk-directconnect (1.77.0) 365 | aws-sdk-core (~> 3, >= 3.201.0) 366 | aws-sigv4 (~> 1.5) 367 | aws-sdk-directoryservice (1.70.0) 368 | aws-sdk-core (~> 3, >= 3.201.0) 369 | aws-sigv4 (~> 1.5) 370 | aws-sdk-dlm (1.75.0) 371 | aws-sdk-core (~> 3, >= 3.201.0) 372 | aws-sigv4 (~> 1.5) 373 | aws-sdk-docdb (1.70.0) 374 | aws-sdk-core (~> 3, >= 3.201.0) 375 | aws-sigv4 (~> 1.5) 376 | aws-sdk-docdbelastic (1.18.0) 377 | aws-sdk-core (~> 3, >= 3.201.0) 378 | aws-sigv4 (~> 1.1) 379 | aws-sdk-drs (1.36.0) 380 | aws-sdk-core (~> 3, >= 3.201.0) 381 | aws-sigv4 (~> 1.1) 382 | aws-sdk-dynamodb (1.118.0) 383 | aws-sdk-core (~> 3, >= 3.201.0) 384 | aws-sigv4 (~> 1.5) 385 | aws-sdk-dynamodbstreams (1.62.0) 386 | aws-sdk-core (~> 3, >= 3.201.0) 387 | aws-sigv4 (~> 1.5) 388 | aws-sdk-ebs (1.46.0) 389 | aws-sdk-core (~> 3, >= 3.201.0) 390 | aws-sigv4 (~> 1.5) 391 | aws-sdk-ec2 (1.467.0) 392 | aws-sdk-core (~> 3, >= 3.201.0) 393 | aws-sigv4 (~> 1.5) 394 | aws-sdk-ec2instanceconnect (1.45.0) 395 | aws-sdk-core (~> 3, >= 3.201.0) 396 | aws-sigv4 (~> 1.5) 397 | aws-sdk-ecr (1.79.0) 398 | aws-sdk-core (~> 3, >= 3.201.0) 399 | aws-sigv4 (~> 1.5) 400 | aws-sdk-ecrpublic (1.33.0) 401 | aws-sdk-core (~> 3, >= 3.201.0) 402 | aws-sigv4 (~> 1.5) 403 | aws-sdk-ecs (1.151.0) 404 | aws-sdk-core (~> 3, >= 3.201.0) 405 | aws-sigv4 (~> 1.5) 406 | aws-sdk-efs (1.79.0) 407 | aws-sdk-core (~> 3, >= 3.201.0) 408 | aws-sigv4 (~> 1.5) 409 | aws-sdk-eks (1.111.0) 410 | aws-sdk-core (~> 3, >= 3.201.0) 411 | aws-sigv4 (~> 1.5) 412 | aws-sdk-eksauth (1.8.0) 413 | aws-sdk-core (~> 3, >= 3.201.0) 414 | aws-sigv4 (~> 1.1) 415 | aws-sdk-elasticache (1.107.0) 416 | aws-sdk-core (~> 3, >= 3.201.0) 417 | aws-sigv4 (~> 1.5) 418 | aws-sdk-elasticbeanstalk (1.73.0) 419 | aws-sdk-core (~> 3, >= 3.201.0) 420 | aws-sigv4 (~> 1.5) 421 | aws-sdk-elasticinference (1.41.0) 422 | aws-sdk-core (~> 3, >= 3.201.0) 423 | aws-sigv4 (~> 1.5) 424 | aws-sdk-elasticloadbalancing (1.60.0) 425 | aws-sdk-core (~> 3, >= 3.201.0) 426 | aws-sigv4 (~> 1.5) 427 | aws-sdk-elasticloadbalancingv2 (1.109.0) 428 | aws-sdk-core (~> 3, >= 3.201.0) 429 | aws-sigv4 (~> 1.5) 430 | aws-sdk-elasticsearchservice (1.89.0) 431 | aws-sdk-core (~> 3, >= 3.201.0) 432 | aws-sigv4 (~> 1.5) 433 | aws-sdk-elastictranscoder (1.57.0) 434 | aws-sdk-core (~> 3, >= 3.201.0) 435 | aws-sigv4 (~> 1.5) 436 | aws-sdk-emr (1.92.0) 437 | aws-sdk-core (~> 3, >= 3.201.0) 438 | aws-sigv4 (~> 1.5) 439 | aws-sdk-emrcontainers (1.41.0) 440 | aws-sdk-core (~> 3, >= 3.201.0) 441 | aws-sigv4 (~> 1.5) 442 | aws-sdk-emrserverless (1.30.0) 443 | aws-sdk-core (~> 3, >= 3.201.0) 444 | aws-sigv4 (~> 1.1) 445 | aws-sdk-entityresolution (1.15.0) 446 | aws-sdk-core (~> 3, >= 3.201.0) 447 | aws-sigv4 (~> 1.5) 448 | aws-sdk-eventbridge (1.64.0) 449 | aws-sdk-core (~> 3, >= 3.201.0) 450 | aws-sigv4 (~> 1.5) 451 | aws-sdk-finspace (1.38.0) 452 | aws-sdk-core (~> 3, >= 3.201.0) 453 | aws-sigv4 (~> 1.5) 454 | aws-sdk-finspacedata (1.38.0) 455 | aws-sdk-core (~> 3, >= 3.201.0) 456 | aws-sigv4 (~> 1.5) 457 | aws-sdk-firehose (1.76.0) 458 | aws-sdk-core (~> 3, >= 3.201.0) 459 | aws-sigv4 (~> 1.5) 460 | aws-sdk-fis (1.34.0) 461 | aws-sdk-core (~> 3, >= 3.201.0) 462 | aws-sigv4 (~> 1.5) 463 | aws-sdk-fms (1.76.0) 464 | aws-sdk-core (~> 3, >= 3.201.0) 465 | aws-sigv4 (~> 1.5) 466 | aws-sdk-forecastqueryservice (1.41.0) 467 | aws-sdk-core (~> 3, >= 3.201.0) 468 | aws-sigv4 (~> 1.5) 469 | aws-sdk-forecastservice (1.58.0) 470 | aws-sdk-core (~> 3, >= 3.201.0) 471 | aws-sigv4 (~> 1.5) 472 | aws-sdk-frauddetector (1.57.0) 473 | aws-sdk-core (~> 3, >= 3.201.0) 474 | aws-sigv4 (~> 1.5) 475 | aws-sdk-freetier (1.8.0) 476 | aws-sdk-core (~> 3, >= 3.201.0) 477 | aws-sigv4 (~> 1.1) 478 | aws-sdk-fsx (1.94.0) 479 | aws-sdk-core (~> 3, >= 3.201.0) 480 | aws-sigv4 (~> 1.5) 481 | aws-sdk-gamelift (1.85.0) 482 | aws-sdk-core (~> 3, >= 3.201.0) 483 | aws-sigv4 (~> 1.5) 484 | aws-sdk-glacier (1.66.0) 485 | aws-sdk-core (~> 3, >= 3.201.0) 486 | aws-sigv4 (~> 1.5) 487 | aws-sdk-globalaccelerator (1.64.0) 488 | aws-sdk-core (~> 3, >= 3.201.0) 489 | aws-sigv4 (~> 1.5) 490 | aws-sdk-glue (1.185.0) 491 | aws-sdk-core (~> 3, >= 3.201.0) 492 | aws-sigv4 (~> 1.5) 493 | aws-sdk-gluedatabrew (1.42.0) 494 | aws-sdk-core (~> 3, >= 3.201.0) 495 | aws-sigv4 (~> 1.5) 496 | aws-sdk-greengrass (1.69.0) 497 | aws-sdk-core (~> 3, >= 3.201.0) 498 | aws-sigv4 (~> 1.1) 499 | aws-sdk-greengrassv2 (1.43.0) 500 | aws-sdk-core (~> 3, >= 3.201.0) 501 | aws-sigv4 (~> 1.5) 502 | aws-sdk-groundstation (1.52.0) 503 | aws-sdk-core (~> 3, >= 3.201.0) 504 | aws-sigv4 (~> 1.5) 505 | aws-sdk-guardduty (1.96.0) 506 | aws-sdk-core (~> 3, >= 3.201.0) 507 | aws-sigv4 (~> 1.5) 508 | aws-sdk-health (1.67.0) 509 | aws-sdk-core (~> 3, >= 3.201.0) 510 | aws-sigv4 (~> 1.5) 511 | aws-sdk-healthlake (1.33.0) 512 | aws-sdk-core (~> 3, >= 3.201.0) 513 | aws-sigv4 (~> 1.5) 514 | aws-sdk-iam (1.103.0) 515 | aws-sdk-core (~> 3, >= 3.201.0) 516 | aws-sigv4 (~> 1.5) 517 | aws-sdk-identitystore (1.40.0) 518 | aws-sdk-core (~> 3, >= 3.201.0) 519 | aws-sigv4 (~> 1.1) 520 | aws-sdk-imagebuilder (1.65.0) 521 | aws-sdk-core (~> 3, >= 3.201.0) 522 | aws-sigv4 (~> 1.5) 523 | aws-sdk-importexport (1.49.0) 524 | aws-sdk-core (~> 3, >= 3.201.0) 525 | aws-sigv2 (~> 1.0) 526 | aws-sdk-inspector (1.62.0) 527 | aws-sdk-core (~> 3, >= 3.201.0) 528 | aws-sigv4 (~> 1.5) 529 | aws-sdk-inspector2 (1.34.0) 530 | aws-sdk-core (~> 3, >= 3.201.0) 531 | aws-sigv4 (~> 1.1) 532 | aws-sdk-inspectorscan (1.9.0) 533 | aws-sdk-core (~> 3, >= 3.201.0) 534 | aws-sigv4 (~> 1.1) 535 | aws-sdk-internetmonitor (1.23.0) 536 | aws-sdk-core (~> 3, >= 3.201.0) 537 | aws-sigv4 (~> 1.1) 538 | aws-sdk-iot (1.128.0) 539 | aws-sdk-core (~> 3, >= 3.201.0) 540 | aws-sigv4 (~> 1.5) 541 | aws-sdk-iot1clickdevicesservice (1.55.0) 542 | aws-sdk-core (~> 3, >= 3.201.0) 543 | aws-sigv4 (~> 1.1) 544 | aws-sdk-iot1clickprojects (1.56.0) 545 | aws-sdk-core (~> 3, >= 3.201.0) 546 | aws-sigv4 (~> 1.5) 547 | aws-sdk-iotanalytics (1.68.0) 548 | aws-sdk-core (~> 3, >= 3.201.0) 549 | aws-sigv4 (~> 1.5) 550 | aws-sdk-iotdataplane (1.61.0) 551 | aws-sdk-core (~> 3, >= 3.201.0) 552 | aws-sigv4 (~> 1.5) 553 | aws-sdk-iotdeviceadvisor (1.36.0) 554 | aws-sdk-core (~> 3, >= 3.201.0) 555 | aws-sigv4 (~> 1.5) 556 | aws-sdk-iotevents (1.53.0) 557 | aws-sdk-core (~> 3, >= 3.201.0) 558 | aws-sigv4 (~> 1.5) 559 | aws-sdk-ioteventsdata (1.46.0) 560 | aws-sdk-core (~> 3, >= 3.201.0) 561 | aws-sigv4 (~> 1.5) 562 | aws-sdk-iotfleethub (1.31.0) 563 | aws-sdk-core (~> 3, >= 3.201.0) 564 | aws-sigv4 (~> 1.5) 565 | aws-sdk-iotfleetwise (1.28.0) 566 | aws-sdk-core (~> 3, >= 3.201.0) 567 | aws-sigv4 (~> 1.1) 568 | aws-sdk-iotjobsdataplane (1.55.0) 569 | aws-sdk-core (~> 3, >= 3.201.0) 570 | aws-sigv4 (~> 1.5) 571 | aws-sdk-iotsecuretunneling (1.40.0) 572 | aws-sdk-core (~> 3, >= 3.201.0) 573 | aws-sigv4 (~> 1.5) 574 | aws-sdk-iotsitewise (1.68.0) 575 | aws-sdk-core (~> 3, >= 3.201.0) 576 | aws-sigv4 (~> 1.5) 577 | aws-sdk-iotthingsgraph (1.43.0) 578 | aws-sdk-core (~> 3, >= 3.201.0) 579 | aws-sigv4 (~> 1.5) 580 | aws-sdk-iottwinmaker (1.28.0) 581 | aws-sdk-core (~> 3, >= 3.201.0) 582 | aws-sigv4 (~> 1.1) 583 | aws-sdk-iotwireless (1.53.0) 584 | aws-sdk-core (~> 3, >= 3.201.0) 585 | aws-sigv4 (~> 1.5) 586 | aws-sdk-ivs (1.53.0) 587 | aws-sdk-core (~> 3, >= 3.201.0) 588 | aws-sigv4 (~> 1.5) 589 | aws-sdk-ivschat (1.28.0) 590 | aws-sdk-core (~> 3, >= 3.201.0) 591 | aws-sigv4 (~> 1.5) 592 | aws-sdk-ivsrealtime (1.24.0) 593 | aws-sdk-core (~> 3, >= 3.201.0) 594 | aws-sigv4 (~> 1.1) 595 | aws-sdk-kafka (1.77.0) 596 | aws-sdk-core (~> 3, >= 3.201.0) 597 | aws-sigv4 (~> 1.1) 598 | aws-sdk-kafkaconnect (1.26.0) 599 | aws-sdk-core (~> 3, >= 3.201.0) 600 | aws-sigv4 (~> 1.1) 601 | aws-sdk-kendra (1.85.0) 602 | aws-sdk-core (~> 3, >= 3.201.0) 603 | aws-sigv4 (~> 1.5) 604 | aws-sdk-kendraranking (1.18.0) 605 | aws-sdk-core (~> 3, >= 3.201.0) 606 | aws-sigv4 (~> 1.5) 607 | aws-sdk-keyspaces (1.24.0) 608 | aws-sdk-core (~> 3, >= 3.201.0) 609 | aws-sigv4 (~> 1.1) 610 | aws-sdk-kinesis (1.62.0) 611 | aws-sdk-core (~> 3, >= 3.201.0) 612 | aws-sigv4 (~> 1.5) 613 | aws-sdk-kinesisanalytics (1.59.0) 614 | aws-sdk-core (~> 3, >= 3.201.0) 615 | aws-sigv4 (~> 1.5) 616 | aws-sdk-kinesisanalyticsv2 (1.62.0) 617 | aws-sdk-core (~> 3, >= 3.201.0) 618 | aws-sigv4 (~> 1.5) 619 | aws-sdk-kinesisvideo (1.67.0) 620 | aws-sdk-core (~> 3, >= 3.201.0) 621 | aws-sigv4 (~> 1.5) 622 | aws-sdk-kinesisvideoarchivedmedia (1.64.0) 623 | aws-sdk-core (~> 3, >= 3.201.0) 624 | aws-sigv4 (~> 1.5) 625 | aws-sdk-kinesisvideomedia (1.56.0) 626 | aws-sdk-core (~> 3, >= 3.201.0) 627 | aws-sigv4 (~> 1.5) 628 | aws-sdk-kinesisvideosignalingchannels (1.38.0) 629 | aws-sdk-core (~> 3, >= 3.201.0) 630 | aws-sigv4 (~> 1.5) 631 | aws-sdk-kinesisvideowebrtcstorage (1.18.0) 632 | aws-sdk-core (~> 3, >= 3.201.0) 633 | aws-sigv4 (~> 1.1) 634 | aws-sdk-kms (1.88.0) 635 | aws-sdk-core (~> 3, >= 3.201.0) 636 | aws-sigv4 (~> 1.5) 637 | aws-sdk-lakeformation (1.56.0) 638 | aws-sdk-core (~> 3, >= 3.201.0) 639 | aws-sigv4 (~> 1.5) 640 | aws-sdk-lambda (1.125.0) 641 | aws-sdk-core (~> 3, >= 3.201.0) 642 | aws-sigv4 (~> 1.5) 643 | aws-sdk-lambdapreview (1.49.0) 644 | aws-sdk-core (~> 3, >= 3.201.0) 645 | aws-sigv4 (~> 1.1) 646 | aws-sdk-launchwizard (1.10.0) 647 | aws-sdk-core (~> 3, >= 3.201.0) 648 | aws-sigv4 (~> 1.1) 649 | aws-sdk-lex (1.65.0) 650 | aws-sdk-core (~> 3, >= 3.201.0) 651 | aws-sigv4 (~> 1.5) 652 | aws-sdk-lexmodelbuildingservice (1.76.0) 653 | aws-sdk-core (~> 3, >= 3.201.0) 654 | aws-sigv4 (~> 1.5) 655 | aws-sdk-lexmodelsv2 (1.56.0) 656 | aws-sdk-core (~> 3, >= 3.201.0) 657 | aws-sigv4 (~> 1.5) 658 | aws-sdk-lexruntimev2 (1.38.0) 659 | aws-sdk-core (~> 3, >= 3.201.0) 660 | aws-sigv4 (~> 1.5) 661 | aws-sdk-licensemanager (1.60.0) 662 | aws-sdk-core (~> 3, >= 3.201.0) 663 | aws-sigv4 (~> 1.1) 664 | aws-sdk-licensemanagerlinuxsubscriptions (1.18.0) 665 | aws-sdk-core (~> 3, >= 3.201.0) 666 | aws-sigv4 (~> 1.5) 667 | aws-sdk-licensemanagerusersubscriptions (1.19.0) 668 | aws-sdk-core (~> 3, >= 3.201.0) 669 | aws-sigv4 (~> 1.1) 670 | aws-sdk-lightsail (1.96.0) 671 | aws-sdk-core (~> 3, >= 3.201.0) 672 | aws-sigv4 (~> 1.5) 673 | aws-sdk-locationservice (1.55.0) 674 | aws-sdk-core (~> 3, >= 3.201.0) 675 | aws-sigv4 (~> 1.1) 676 | aws-sdk-lookoutequipment (1.36.0) 677 | aws-sdk-core (~> 3, >= 3.201.0) 678 | aws-sigv4 (~> 1.5) 679 | aws-sdk-lookoutforvision (1.36.0) 680 | aws-sdk-core (~> 3, >= 3.201.0) 681 | aws-sigv4 (~> 1.5) 682 | aws-sdk-lookoutmetrics (1.41.0) 683 | aws-sdk-core (~> 3, >= 3.201.0) 684 | aws-sigv4 (~> 1.5) 685 | aws-sdk-machinelearning (1.57.0) 686 | aws-sdk-core (~> 3, >= 3.201.0) 687 | aws-sigv4 (~> 1.5) 688 | aws-sdk-macie2 (1.73.0) 689 | aws-sdk-core (~> 3, >= 3.201.0) 690 | aws-sigv4 (~> 1.5) 691 | aws-sdk-mailmanager (1.5.0) 692 | aws-sdk-core (~> 3, >= 3.201.0) 693 | aws-sigv4 (~> 1.1) 694 | aws-sdk-mainframemodernization (1.22.0) 695 | aws-sdk-core (~> 3, >= 3.201.0) 696 | aws-sigv4 (~> 1.1) 697 | aws-sdk-managedblockchain (1.57.0) 698 | aws-sdk-core (~> 3, >= 3.201.0) 699 | aws-sigv4 (~> 1.5) 700 | aws-sdk-managedblockchainquery (1.16.0) 701 | aws-sdk-core (~> 3, >= 3.201.0) 702 | aws-sigv4 (~> 1.1) 703 | aws-sdk-managedgrafana (1.33.0) 704 | aws-sdk-core (~> 3, >= 3.201.0) 705 | aws-sigv4 (~> 1.1) 706 | aws-sdk-marketplaceagreement (1.7.0) 707 | aws-sdk-core (~> 3, >= 3.201.0) 708 | aws-sigv4 (~> 1.1) 709 | aws-sdk-marketplacecatalog (1.47.0) 710 | aws-sdk-core (~> 3, >= 3.201.0) 711 | aws-sigv4 (~> 1.5) 712 | aws-sdk-marketplacecommerceanalytics (1.61.0) 713 | aws-sdk-core (~> 3, >= 3.201.0) 714 | aws-sigv4 (~> 1.5) 715 | aws-sdk-marketplacedeployment (1.7.0) 716 | aws-sdk-core (~> 3, >= 3.201.0) 717 | aws-sigv4 (~> 1.1) 718 | aws-sdk-marketplaceentitlementservice (1.56.0) 719 | aws-sdk-core (~> 3, >= 3.201.0) 720 | aws-sigv4 (~> 1.5) 721 | aws-sdk-marketplacemetering (1.63.0) 722 | aws-sdk-core (~> 3, >= 3.201.0) 723 | aws-sigv4 (~> 1.5) 724 | aws-sdk-mediaconnect (1.65.0) 725 | aws-sdk-core (~> 3, >= 3.201.0) 726 | aws-sigv4 (~> 1.5) 727 | aws-sdk-mediaconvert (1.134.0) 728 | aws-sdk-core (~> 3, >= 3.201.0) 729 | aws-sigv4 (~> 1.5) 730 | aws-sdk-medialive (1.127.0) 731 | aws-sdk-core (~> 3, >= 3.201.0) 732 | aws-sigv4 (~> 1.5) 733 | aws-sdk-mediapackage (1.76.0) 734 | aws-sdk-core (~> 3, >= 3.201.0) 735 | aws-sigv4 (~> 1.1) 736 | aws-sdk-mediapackagev2 (1.22.0) 737 | aws-sdk-core (~> 3, >= 3.201.0) 738 | aws-sigv4 (~> 1.1) 739 | aws-sdk-mediapackagevod (1.58.0) 740 | aws-sdk-core (~> 3, >= 3.201.0) 741 | aws-sigv4 (~> 1.1) 742 | aws-sdk-mediastore (1.60.0) 743 | aws-sdk-core (~> 3, >= 3.201.0) 744 | aws-sigv4 (~> 1.5) 745 | aws-sdk-mediastoredata (1.57.0) 746 | aws-sdk-core (~> 3, >= 3.201.0) 747 | aws-sigv4 (~> 1.5) 748 | aws-sdk-mediatailor (1.84.0) 749 | aws-sdk-core (~> 3, >= 3.201.0) 750 | aws-sigv4 (~> 1.1) 751 | aws-sdk-medicalimaging (1.15.0) 752 | aws-sdk-core (~> 3, >= 3.201.0) 753 | aws-sigv4 (~> 1.5) 754 | aws-sdk-memorydb (1.29.0) 755 | aws-sdk-core (~> 3, >= 3.201.0) 756 | aws-sigv4 (~> 1.5) 757 | aws-sdk-mgn (1.36.0) 758 | aws-sdk-core (~> 3, >= 3.201.0) 759 | aws-sigv4 (~> 1.1) 760 | aws-sdk-migrationhub (1.59.0) 761 | aws-sdk-core (~> 3, >= 3.201.0) 762 | aws-sigv4 (~> 1.5) 763 | aws-sdk-migrationhubconfig (1.40.0) 764 | aws-sdk-core (~> 3, >= 3.201.0) 765 | aws-sigv4 (~> 1.5) 766 | aws-sdk-migrationhuborchestrator (1.19.0) 767 | aws-sdk-core (~> 3, >= 3.201.0) 768 | aws-sigv4 (~> 1.1) 769 | aws-sdk-migrationhubrefactorspaces (1.28.0) 770 | aws-sdk-core (~> 3, >= 3.201.0) 771 | aws-sigv4 (~> 1.1) 772 | aws-sdk-migrationhubstrategyrecommendations (1.26.0) 773 | aws-sdk-core (~> 3, >= 3.201.0) 774 | aws-sigv4 (~> 1.1) 775 | aws-sdk-mq (1.66.0) 776 | aws-sdk-core (~> 3, >= 3.201.0) 777 | aws-sigv4 (~> 1.5) 778 | aws-sdk-mturk (1.59.0) 779 | aws-sdk-core (~> 3, >= 3.201.0) 780 | aws-sigv4 (~> 1.5) 781 | aws-sdk-mwaa (1.42.0) 782 | aws-sdk-core (~> 3, >= 3.201.0) 783 | aws-sigv4 (~> 1.1) 784 | aws-sdk-neptune (1.70.0) 785 | aws-sdk-core (~> 3, >= 3.201.0) 786 | aws-sigv4 (~> 1.5) 787 | aws-sdk-neptunedata (1.14.0) 788 | aws-sdk-core (~> 3, >= 3.201.0) 789 | aws-sigv4 (~> 1.1) 790 | aws-sdk-neptunegraph (1.14.0) 791 | aws-sdk-core (~> 3, >= 3.201.0) 792 | aws-sigv4 (~> 1.5) 793 | aws-sdk-networkfirewall (1.48.0) 794 | aws-sdk-core (~> 3, >= 3.201.0) 795 | aws-sigv4 (~> 1.5) 796 | aws-sdk-networkmanager (1.48.0) 797 | aws-sdk-core (~> 3, >= 3.201.0) 798 | aws-sigv4 (~> 1.5) 799 | aws-sdk-networkmonitor (1.8.0) 800 | aws-sdk-core (~> 3, >= 3.201.0) 801 | aws-sigv4 (~> 1.1) 802 | aws-sdk-nimblestudio (1.34.0) 803 | aws-sdk-core (~> 3, >= 3.201.0) 804 | aws-sigv4 (~> 1.1) 805 | aws-sdk-oam (1.20.0) 806 | aws-sdk-core (~> 3, >= 3.201.0) 807 | aws-sigv4 (~> 1.1) 808 | aws-sdk-omics (1.31.0) 809 | aws-sdk-core (~> 3, >= 3.201.0) 810 | aws-sigv4 (~> 1.1) 811 | aws-sdk-opensearchserverless (1.21.0) 812 | aws-sdk-core (~> 3, >= 3.201.0) 813 | aws-sigv4 (~> 1.1) 814 | aws-sdk-opensearchservice (1.50.0) 815 | aws-sdk-core (~> 3, >= 3.201.0) 816 | aws-sigv4 (~> 1.5) 817 | aws-sdk-opsworks (1.61.0) 818 | aws-sdk-core (~> 3, >= 3.201.0) 819 | aws-sigv4 (~> 1.5) 820 | aws-sdk-opsworkscm (1.71.0) 821 | aws-sdk-core (~> 3, >= 3.201.0) 822 | aws-sigv4 (~> 1.5) 823 | aws-sdk-organizations (1.95.0) 824 | aws-sdk-core (~> 3, >= 3.201.0) 825 | aws-sigv4 (~> 1.5) 826 | aws-sdk-osis (1.20.0) 827 | aws-sdk-core (~> 3, >= 3.201.0) 828 | aws-sigv4 (~> 1.5) 829 | aws-sdk-outposts (1.63.0) 830 | aws-sdk-core (~> 3, >= 3.201.0) 831 | aws-sigv4 (~> 1.5) 832 | aws-sdk-panorama (1.29.0) 833 | aws-sdk-core (~> 3, >= 3.201.0) 834 | aws-sigv4 (~> 1.1) 835 | aws-sdk-paymentcryptography (1.19.0) 836 | aws-sdk-core (~> 3, >= 3.201.0) 837 | aws-sigv4 (~> 1.5) 838 | aws-sdk-paymentcryptographydata (1.18.0) 839 | aws-sdk-core (~> 3, >= 3.201.0) 840 | aws-sigv4 (~> 1.5) 841 | aws-sdk-pcaconnectorad (1.10.0) 842 | aws-sdk-core (~> 3, >= 3.201.0) 843 | aws-sigv4 (~> 1.1) 844 | aws-sdk-pcaconnectorscep (1.3.0) 845 | aws-sdk-core (~> 3, >= 3.201.0) 846 | aws-sigv4 (~> 1.1) 847 | aws-sdk-personalize (1.68.0) 848 | aws-sdk-core (~> 3, >= 3.201.0) 849 | aws-sigv4 (~> 1.5) 850 | aws-sdk-personalizeevents (1.49.0) 851 | aws-sdk-core (~> 3, >= 3.201.0) 852 | aws-sigv4 (~> 1.5) 853 | aws-sdk-personalizeruntime (1.56.0) 854 | aws-sdk-core (~> 3, >= 3.201.0) 855 | aws-sigv4 (~> 1.5) 856 | aws-sdk-pi (1.62.0) 857 | aws-sdk-core (~> 3, >= 3.201.0) 858 | aws-sigv4 (~> 1.5) 859 | aws-sdk-pinpoint (1.95.0) 860 | aws-sdk-core (~> 3, >= 3.201.0) 861 | aws-sigv4 (~> 1.5) 862 | aws-sdk-pinpointemail (1.54.0) 863 | aws-sdk-core (~> 3, >= 3.201.0) 864 | aws-sigv4 (~> 1.5) 865 | aws-sdk-pinpointsmsvoice (1.50.0) 866 | aws-sdk-core (~> 3, >= 3.201.0) 867 | aws-sigv4 (~> 1.1) 868 | aws-sdk-pinpointsmsvoicev2 (1.21.0) 869 | aws-sdk-core (~> 3, >= 3.201.0) 870 | aws-sigv4 (~> 1.5) 871 | aws-sdk-pipes (1.24.0) 872 | aws-sdk-core (~> 3, >= 3.201.0) 873 | aws-sigv4 (~> 1.1) 874 | aws-sdk-polly (1.90.0) 875 | aws-sdk-core (~> 3, >= 3.201.0) 876 | aws-sigv4 (~> 1.5) 877 | aws-sdk-pricing (1.62.0) 878 | aws-sdk-core (~> 3, >= 3.201.0) 879 | aws-sigv4 (~> 1.1) 880 | aws-sdk-privatenetworks (1.20.0) 881 | aws-sdk-core (~> 3, >= 3.201.0) 882 | aws-sigv4 (~> 1.1) 883 | aws-sdk-prometheusservice (1.36.0) 884 | aws-sdk-core (~> 3, >= 3.201.0) 885 | aws-sigv4 (~> 1.1) 886 | aws-sdk-proton (1.41.0) 887 | aws-sdk-core (~> 3, >= 3.201.0) 888 | aws-sigv4 (~> 1.1) 889 | aws-sdk-qapps (1.0.0) 890 | aws-sdk-core (~> 3, >= 3.201.0) 891 | aws-sigv4 (~> 1.5) 892 | aws-sdk-qbusiness (1.11.0) 893 | aws-sdk-core (~> 3, >= 3.201.0) 894 | aws-sigv4 (~> 1.5) 895 | aws-sdk-qconnect (1.14.0) 896 | aws-sdk-core (~> 3, >= 3.201.0) 897 | aws-sigv4 (~> 1.1) 898 | aws-sdk-qldb (1.45.0) 899 | aws-sdk-core (~> 3, >= 3.201.0) 900 | aws-sigv4 (~> 1.5) 901 | aws-sdk-qldbsession (1.41.0) 902 | aws-sdk-core (~> 3, >= 3.201.0) 903 | aws-sigv4 (~> 1.5) 904 | aws-sdk-quicksight (1.119.0) 905 | aws-sdk-core (~> 3, >= 3.201.0) 906 | aws-sigv4 (~> 1.5) 907 | aws-sdk-ram (1.60.0) 908 | aws-sdk-core (~> 3, >= 3.201.0) 909 | aws-sigv4 (~> 1.5) 910 | aws-sdk-rds (1.240.0) 911 | aws-sdk-core (~> 3, >= 3.201.0) 912 | aws-sigv4 (~> 1.5) 913 | aws-sdk-rdsdataservice (1.57.0) 914 | aws-sdk-core (~> 3, >= 3.201.0) 915 | aws-sigv4 (~> 1.1) 916 | aws-sdk-recyclebin (1.26.0) 917 | aws-sdk-core (~> 3, >= 3.201.0) 918 | aws-sigv4 (~> 1.5) 919 | aws-sdk-redshift (1.119.0) 920 | aws-sdk-core (~> 3, >= 3.201.0) 921 | aws-sigv4 (~> 1.5) 922 | aws-sdk-redshiftdataapiservice (1.41.0) 923 | aws-sdk-core (~> 3, >= 3.201.0) 924 | aws-sigv4 (~> 1.1) 925 | aws-sdk-redshiftserverless (1.33.0) 926 | aws-sdk-core (~> 3, >= 3.201.0) 927 | aws-sigv4 (~> 1.1) 928 | aws-sdk-rekognition (1.102.0) 929 | aws-sdk-core (~> 3, >= 3.201.0) 930 | aws-sigv4 (~> 1.5) 931 | aws-sdk-repostspace (1.8.0) 932 | aws-sdk-core (~> 3, >= 3.201.0) 933 | aws-sigv4 (~> 1.1) 934 | aws-sdk-resiliencehub (1.32.0) 935 | aws-sdk-core (~> 3, >= 3.201.0) 936 | aws-sigv4 (~> 1.1) 937 | aws-sdk-resourceexplorer2 (1.22.0) 938 | aws-sdk-core (~> 3, >= 3.201.0) 939 | aws-sigv4 (~> 1.1) 940 | aws-sdk-resourcegroups (1.66.0) 941 | aws-sdk-core (~> 3, >= 3.201.0) 942 | aws-sigv4 (~> 1.5) 943 | aws-sdk-resourcegroupstaggingapi (1.66.0) 944 | aws-sdk-core (~> 3, >= 3.201.0) 945 | aws-sigv4 (~> 1.5) 946 | aws-sdk-resources (3.199.0) 947 | aws-sdk-accessanalyzer (~> 1) 948 | aws-sdk-account (~> 1) 949 | aws-sdk-acm (~> 1) 950 | aws-sdk-acmpca (~> 1) 951 | aws-sdk-amplify (~> 1) 952 | aws-sdk-amplifybackend (~> 1) 953 | aws-sdk-amplifyuibuilder (~> 1) 954 | aws-sdk-apigateway (~> 1) 955 | aws-sdk-apigatewaymanagementapi (~> 1) 956 | aws-sdk-apigatewayv2 (~> 1) 957 | aws-sdk-appconfig (~> 1) 958 | aws-sdk-appconfigdata (~> 1) 959 | aws-sdk-appfabric (~> 1) 960 | aws-sdk-appflow (~> 1) 961 | aws-sdk-appintegrationsservice (~> 1) 962 | aws-sdk-applicationautoscaling (~> 1) 963 | aws-sdk-applicationcostprofiler (~> 1) 964 | aws-sdk-applicationdiscoveryservice (~> 1) 965 | aws-sdk-applicationinsights (~> 1) 966 | aws-sdk-applicationsignals (~> 1) 967 | aws-sdk-appmesh (~> 1) 968 | aws-sdk-appregistry (~> 1) 969 | aws-sdk-apprunner (~> 1) 970 | aws-sdk-appstream (~> 1) 971 | aws-sdk-appsync (~> 1) 972 | aws-sdk-apptest (~> 1) 973 | aws-sdk-arczonalshift (~> 1) 974 | aws-sdk-artifact (~> 1) 975 | aws-sdk-athena (~> 1) 976 | aws-sdk-auditmanager (~> 1) 977 | aws-sdk-augmentedairuntime (~> 1) 978 | aws-sdk-autoscaling (~> 1) 979 | aws-sdk-autoscalingplans (~> 1) 980 | aws-sdk-b2bi (~> 1) 981 | aws-sdk-backup (~> 1) 982 | aws-sdk-backupgateway (~> 1) 983 | aws-sdk-batch (~> 1) 984 | aws-sdk-bcmdataexports (~> 1) 985 | aws-sdk-bedrock (~> 1) 986 | aws-sdk-bedrockagent (~> 1) 987 | aws-sdk-bedrockagentruntime (~> 1) 988 | aws-sdk-bedrockruntime (~> 1) 989 | aws-sdk-billingconductor (~> 1) 990 | aws-sdk-braket (~> 1) 991 | aws-sdk-budgets (~> 1) 992 | aws-sdk-chatbot (~> 1) 993 | aws-sdk-chime (~> 1) 994 | aws-sdk-chimesdkidentity (~> 1) 995 | aws-sdk-chimesdkmediapipelines (~> 1) 996 | aws-sdk-chimesdkmeetings (~> 1) 997 | aws-sdk-chimesdkmessaging (~> 1) 998 | aws-sdk-chimesdkvoice (~> 1) 999 | aws-sdk-cleanrooms (~> 1) 1000 | aws-sdk-cleanroomsml (~> 1) 1001 | aws-sdk-cloud9 (~> 1) 1002 | aws-sdk-cloudcontrolapi (~> 1) 1003 | aws-sdk-clouddirectory (~> 1) 1004 | aws-sdk-cloudformation (~> 1) 1005 | aws-sdk-cloudfront (~> 1) 1006 | aws-sdk-cloudfrontkeyvaluestore (~> 1) 1007 | aws-sdk-cloudhsm (~> 1) 1008 | aws-sdk-cloudhsmv2 (~> 1) 1009 | aws-sdk-cloudsearch (~> 1) 1010 | aws-sdk-cloudsearchdomain (~> 1) 1011 | aws-sdk-cloudtrail (~> 1) 1012 | aws-sdk-cloudtraildata (~> 1) 1013 | aws-sdk-cloudwatch (~> 1) 1014 | aws-sdk-cloudwatchevents (~> 1) 1015 | aws-sdk-cloudwatchevidently (~> 1) 1016 | aws-sdk-cloudwatchlogs (~> 1) 1017 | aws-sdk-cloudwatchrum (~> 1) 1018 | aws-sdk-codeartifact (~> 1) 1019 | aws-sdk-codebuild (~> 1) 1020 | aws-sdk-codecatalyst (~> 1) 1021 | aws-sdk-codecommit (~> 1) 1022 | aws-sdk-codeconnections (~> 1) 1023 | aws-sdk-codedeploy (~> 1) 1024 | aws-sdk-codeguruprofiler (~> 1) 1025 | aws-sdk-codegurureviewer (~> 1) 1026 | aws-sdk-codegurusecurity (~> 1) 1027 | aws-sdk-codepipeline (~> 1) 1028 | aws-sdk-codestar (~> 1) 1029 | aws-sdk-codestarconnections (~> 1) 1030 | aws-sdk-codestarnotifications (~> 1) 1031 | aws-sdk-cognitoidentity (~> 1) 1032 | aws-sdk-cognitoidentityprovider (~> 1) 1033 | aws-sdk-cognitosync (~> 1) 1034 | aws-sdk-comprehend (~> 1) 1035 | aws-sdk-comprehendmedical (~> 1) 1036 | aws-sdk-computeoptimizer (~> 1) 1037 | aws-sdk-configservice (~> 1) 1038 | aws-sdk-connect (~> 1) 1039 | aws-sdk-connectcampaignservice (~> 1) 1040 | aws-sdk-connectcases (~> 1) 1041 | aws-sdk-connectcontactlens (~> 1) 1042 | aws-sdk-connectparticipant (~> 1) 1043 | aws-sdk-connectwisdomservice (~> 1) 1044 | aws-sdk-controlcatalog (~> 1) 1045 | aws-sdk-controltower (~> 1) 1046 | aws-sdk-costandusagereportservice (~> 1) 1047 | aws-sdk-costexplorer (~> 1) 1048 | aws-sdk-costoptimizationhub (~> 1) 1049 | aws-sdk-customerprofiles (~> 1) 1050 | aws-sdk-databasemigrationservice (~> 1) 1051 | aws-sdk-dataexchange (~> 1) 1052 | aws-sdk-datapipeline (~> 1) 1053 | aws-sdk-datasync (~> 1) 1054 | aws-sdk-datazone (~> 1) 1055 | aws-sdk-dax (~> 1) 1056 | aws-sdk-deadline (~> 1) 1057 | aws-sdk-detective (~> 1) 1058 | aws-sdk-devicefarm (~> 1) 1059 | aws-sdk-devopsguru (~> 1) 1060 | aws-sdk-directconnect (~> 1) 1061 | aws-sdk-directoryservice (~> 1) 1062 | aws-sdk-dlm (~> 1) 1063 | aws-sdk-docdb (~> 1) 1064 | aws-sdk-docdbelastic (~> 1) 1065 | aws-sdk-drs (~> 1) 1066 | aws-sdk-dynamodb (~> 1) 1067 | aws-sdk-dynamodbstreams (~> 1) 1068 | aws-sdk-ebs (~> 1) 1069 | aws-sdk-ec2 (~> 1) 1070 | aws-sdk-ec2instanceconnect (~> 1) 1071 | aws-sdk-ecr (~> 1) 1072 | aws-sdk-ecrpublic (~> 1) 1073 | aws-sdk-ecs (~> 1) 1074 | aws-sdk-efs (~> 1) 1075 | aws-sdk-eks (~> 1) 1076 | aws-sdk-eksauth (~> 1) 1077 | aws-sdk-elasticache (~> 1) 1078 | aws-sdk-elasticbeanstalk (~> 1) 1079 | aws-sdk-elasticinference (~> 1) 1080 | aws-sdk-elasticloadbalancing (~> 1) 1081 | aws-sdk-elasticloadbalancingv2 (~> 1) 1082 | aws-sdk-elasticsearchservice (~> 1) 1083 | aws-sdk-elastictranscoder (~> 1) 1084 | aws-sdk-emr (~> 1) 1085 | aws-sdk-emrcontainers (~> 1) 1086 | aws-sdk-emrserverless (~> 1) 1087 | aws-sdk-entityresolution (~> 1) 1088 | aws-sdk-eventbridge (~> 1) 1089 | aws-sdk-finspace (~> 1) 1090 | aws-sdk-finspacedata (~> 1) 1091 | aws-sdk-firehose (~> 1) 1092 | aws-sdk-fis (~> 1) 1093 | aws-sdk-fms (~> 1) 1094 | aws-sdk-forecastqueryservice (~> 1) 1095 | aws-sdk-forecastservice (~> 1) 1096 | aws-sdk-frauddetector (~> 1) 1097 | aws-sdk-freetier (~> 1) 1098 | aws-sdk-fsx (~> 1) 1099 | aws-sdk-gamelift (~> 1) 1100 | aws-sdk-glacier (~> 1) 1101 | aws-sdk-globalaccelerator (~> 1) 1102 | aws-sdk-glue (~> 1) 1103 | aws-sdk-gluedatabrew (~> 1) 1104 | aws-sdk-greengrass (~> 1) 1105 | aws-sdk-greengrassv2 (~> 1) 1106 | aws-sdk-groundstation (~> 1) 1107 | aws-sdk-guardduty (~> 1) 1108 | aws-sdk-health (~> 1) 1109 | aws-sdk-healthlake (~> 1) 1110 | aws-sdk-iam (~> 1) 1111 | aws-sdk-identitystore (~> 1) 1112 | aws-sdk-imagebuilder (~> 1) 1113 | aws-sdk-importexport (~> 1) 1114 | aws-sdk-inspector (~> 1) 1115 | aws-sdk-inspector2 (~> 1) 1116 | aws-sdk-inspectorscan (~> 1) 1117 | aws-sdk-internetmonitor (~> 1) 1118 | aws-sdk-iot (~> 1) 1119 | aws-sdk-iot1clickdevicesservice (~> 1) 1120 | aws-sdk-iot1clickprojects (~> 1) 1121 | aws-sdk-iotanalytics (~> 1) 1122 | aws-sdk-iotdataplane (~> 1) 1123 | aws-sdk-iotdeviceadvisor (~> 1) 1124 | aws-sdk-iotevents (~> 1) 1125 | aws-sdk-ioteventsdata (~> 1) 1126 | aws-sdk-iotfleethub (~> 1) 1127 | aws-sdk-iotfleetwise (~> 1) 1128 | aws-sdk-iotjobsdataplane (~> 1) 1129 | aws-sdk-iotsecuretunneling (~> 1) 1130 | aws-sdk-iotsitewise (~> 1) 1131 | aws-sdk-iotthingsgraph (~> 1) 1132 | aws-sdk-iottwinmaker (~> 1) 1133 | aws-sdk-iotwireless (~> 1) 1134 | aws-sdk-ivs (~> 1) 1135 | aws-sdk-ivschat (~> 1) 1136 | aws-sdk-ivsrealtime (~> 1) 1137 | aws-sdk-kafka (~> 1) 1138 | aws-sdk-kafkaconnect (~> 1) 1139 | aws-sdk-kendra (~> 1) 1140 | aws-sdk-kendraranking (~> 1) 1141 | aws-sdk-keyspaces (~> 1) 1142 | aws-sdk-kinesis (~> 1) 1143 | aws-sdk-kinesisanalytics (~> 1) 1144 | aws-sdk-kinesisanalyticsv2 (~> 1) 1145 | aws-sdk-kinesisvideo (~> 1) 1146 | aws-sdk-kinesisvideoarchivedmedia (~> 1) 1147 | aws-sdk-kinesisvideomedia (~> 1) 1148 | aws-sdk-kinesisvideosignalingchannels (~> 1) 1149 | aws-sdk-kinesisvideowebrtcstorage (~> 1) 1150 | aws-sdk-kms (~> 1) 1151 | aws-sdk-lakeformation (~> 1) 1152 | aws-sdk-lambda (~> 1) 1153 | aws-sdk-lambdapreview (~> 1) 1154 | aws-sdk-launchwizard (~> 1) 1155 | aws-sdk-lex (~> 1) 1156 | aws-sdk-lexmodelbuildingservice (~> 1) 1157 | aws-sdk-lexmodelsv2 (~> 1) 1158 | aws-sdk-lexruntimev2 (~> 1) 1159 | aws-sdk-licensemanager (~> 1) 1160 | aws-sdk-licensemanagerlinuxsubscriptions (~> 1) 1161 | aws-sdk-licensemanagerusersubscriptions (~> 1) 1162 | aws-sdk-lightsail (~> 1) 1163 | aws-sdk-locationservice (~> 1) 1164 | aws-sdk-lookoutequipment (~> 1) 1165 | aws-sdk-lookoutforvision (~> 1) 1166 | aws-sdk-lookoutmetrics (~> 1) 1167 | aws-sdk-machinelearning (~> 1) 1168 | aws-sdk-macie2 (~> 1) 1169 | aws-sdk-mailmanager (~> 1) 1170 | aws-sdk-mainframemodernization (~> 1) 1171 | aws-sdk-managedblockchain (~> 1) 1172 | aws-sdk-managedblockchainquery (~> 1) 1173 | aws-sdk-managedgrafana (~> 1) 1174 | aws-sdk-marketplaceagreement (~> 1) 1175 | aws-sdk-marketplacecatalog (~> 1) 1176 | aws-sdk-marketplacecommerceanalytics (~> 1) 1177 | aws-sdk-marketplacedeployment (~> 1) 1178 | aws-sdk-marketplaceentitlementservice (~> 1) 1179 | aws-sdk-marketplacemetering (~> 1) 1180 | aws-sdk-mediaconnect (~> 1) 1181 | aws-sdk-mediaconvert (~> 1) 1182 | aws-sdk-medialive (~> 1) 1183 | aws-sdk-mediapackage (~> 1) 1184 | aws-sdk-mediapackagev2 (~> 1) 1185 | aws-sdk-mediapackagevod (~> 1) 1186 | aws-sdk-mediastore (~> 1) 1187 | aws-sdk-mediastoredata (~> 1) 1188 | aws-sdk-mediatailor (~> 1) 1189 | aws-sdk-medicalimaging (~> 1) 1190 | aws-sdk-memorydb (~> 1) 1191 | aws-sdk-mgn (~> 1) 1192 | aws-sdk-migrationhub (~> 1) 1193 | aws-sdk-migrationhubconfig (~> 1) 1194 | aws-sdk-migrationhuborchestrator (~> 1) 1195 | aws-sdk-migrationhubrefactorspaces (~> 1) 1196 | aws-sdk-migrationhubstrategyrecommendations (~> 1) 1197 | aws-sdk-mq (~> 1) 1198 | aws-sdk-mturk (~> 1) 1199 | aws-sdk-mwaa (~> 1) 1200 | aws-sdk-neptune (~> 1) 1201 | aws-sdk-neptunedata (~> 1) 1202 | aws-sdk-neptunegraph (~> 1) 1203 | aws-sdk-networkfirewall (~> 1) 1204 | aws-sdk-networkmanager (~> 1) 1205 | aws-sdk-networkmonitor (~> 1) 1206 | aws-sdk-nimblestudio (~> 1) 1207 | aws-sdk-oam (~> 1) 1208 | aws-sdk-omics (~> 1) 1209 | aws-sdk-opensearchserverless (~> 1) 1210 | aws-sdk-opensearchservice (~> 1) 1211 | aws-sdk-opsworks (~> 1) 1212 | aws-sdk-opsworkscm (~> 1) 1213 | aws-sdk-organizations (~> 1) 1214 | aws-sdk-osis (~> 1) 1215 | aws-sdk-outposts (~> 1) 1216 | aws-sdk-panorama (~> 1) 1217 | aws-sdk-paymentcryptography (~> 1) 1218 | aws-sdk-paymentcryptographydata (~> 1) 1219 | aws-sdk-pcaconnectorad (~> 1) 1220 | aws-sdk-pcaconnectorscep (~> 1) 1221 | aws-sdk-personalize (~> 1) 1222 | aws-sdk-personalizeevents (~> 1) 1223 | aws-sdk-personalizeruntime (~> 1) 1224 | aws-sdk-pi (~> 1) 1225 | aws-sdk-pinpoint (~> 1) 1226 | aws-sdk-pinpointemail (~> 1) 1227 | aws-sdk-pinpointsmsvoice (~> 1) 1228 | aws-sdk-pinpointsmsvoicev2 (~> 1) 1229 | aws-sdk-pipes (~> 1) 1230 | aws-sdk-polly (~> 1) 1231 | aws-sdk-pricing (~> 1) 1232 | aws-sdk-privatenetworks (~> 1) 1233 | aws-sdk-prometheusservice (~> 1) 1234 | aws-sdk-proton (~> 1) 1235 | aws-sdk-qapps (~> 1) 1236 | aws-sdk-qbusiness (~> 1) 1237 | aws-sdk-qconnect (~> 1) 1238 | aws-sdk-qldb (~> 1) 1239 | aws-sdk-qldbsession (~> 1) 1240 | aws-sdk-quicksight (~> 1) 1241 | aws-sdk-ram (~> 1) 1242 | aws-sdk-rds (~> 1) 1243 | aws-sdk-rdsdataservice (~> 1) 1244 | aws-sdk-recyclebin (~> 1) 1245 | aws-sdk-redshift (~> 1) 1246 | aws-sdk-redshiftdataapiservice (~> 1) 1247 | aws-sdk-redshiftserverless (~> 1) 1248 | aws-sdk-rekognition (~> 1) 1249 | aws-sdk-repostspace (~> 1) 1250 | aws-sdk-resiliencehub (~> 1) 1251 | aws-sdk-resourceexplorer2 (~> 1) 1252 | aws-sdk-resourcegroups (~> 1) 1253 | aws-sdk-resourcegroupstaggingapi (~> 1) 1254 | aws-sdk-robomaker (~> 1) 1255 | aws-sdk-rolesanywhere (~> 1) 1256 | aws-sdk-route53 (~> 1) 1257 | aws-sdk-route53domains (~> 1) 1258 | aws-sdk-route53profiles (~> 1) 1259 | aws-sdk-route53recoverycluster (~> 1) 1260 | aws-sdk-route53recoverycontrolconfig (~> 1) 1261 | aws-sdk-route53recoveryreadiness (~> 1) 1262 | aws-sdk-route53resolver (~> 1) 1263 | aws-sdk-s3 (~> 1) 1264 | aws-sdk-s3control (~> 1) 1265 | aws-sdk-s3outposts (~> 1) 1266 | aws-sdk-sagemaker (~> 1) 1267 | aws-sdk-sagemakeredgemanager (~> 1) 1268 | aws-sdk-sagemakerfeaturestoreruntime (~> 1) 1269 | aws-sdk-sagemakergeospatial (~> 1) 1270 | aws-sdk-sagemakermetrics (~> 1) 1271 | aws-sdk-sagemakerruntime (~> 1) 1272 | aws-sdk-savingsplans (~> 1) 1273 | aws-sdk-scheduler (~> 1) 1274 | aws-sdk-schemas (~> 1) 1275 | aws-sdk-secretsmanager (~> 1) 1276 | aws-sdk-securityhub (~> 1) 1277 | aws-sdk-securitylake (~> 1) 1278 | aws-sdk-serverlessapplicationrepository (~> 1) 1279 | aws-sdk-servicecatalog (~> 1) 1280 | aws-sdk-servicediscovery (~> 1) 1281 | aws-sdk-servicequotas (~> 1) 1282 | aws-sdk-ses (~> 1) 1283 | aws-sdk-sesv2 (~> 1) 1284 | aws-sdk-shield (~> 1) 1285 | aws-sdk-signer (~> 1) 1286 | aws-sdk-simpledb (~> 1) 1287 | aws-sdk-simspaceweaver (~> 1) 1288 | aws-sdk-sms (~> 1) 1289 | aws-sdk-snowball (~> 1) 1290 | aws-sdk-snowdevicemanagement (~> 1) 1291 | aws-sdk-sns (~> 1) 1292 | aws-sdk-sqs (~> 1) 1293 | aws-sdk-ssm (~> 1) 1294 | aws-sdk-ssmcontacts (~> 1) 1295 | aws-sdk-ssmincidents (~> 1) 1296 | aws-sdk-ssmsap (~> 1) 1297 | aws-sdk-ssoadmin (~> 1) 1298 | aws-sdk-states (~> 1) 1299 | aws-sdk-storagegateway (~> 1) 1300 | aws-sdk-supplychain (~> 1) 1301 | aws-sdk-support (~> 1) 1302 | aws-sdk-supportapp (~> 1) 1303 | aws-sdk-swf (~> 1) 1304 | aws-sdk-synthetics (~> 1) 1305 | aws-sdk-taxsettings (~> 1) 1306 | aws-sdk-textract (~> 1) 1307 | aws-sdk-timestreaminfluxdb (~> 1) 1308 | aws-sdk-timestreamquery (~> 1) 1309 | aws-sdk-timestreamwrite (~> 1) 1310 | aws-sdk-tnb (~> 1) 1311 | aws-sdk-transcribeservice (~> 1) 1312 | aws-sdk-transcribestreamingservice (~> 1) 1313 | aws-sdk-transfer (~> 1) 1314 | aws-sdk-translate (~> 1) 1315 | aws-sdk-trustedadvisor (~> 1) 1316 | aws-sdk-verifiedpermissions (~> 1) 1317 | aws-sdk-voiceid (~> 1) 1318 | aws-sdk-vpclattice (~> 1) 1319 | aws-sdk-waf (~> 1) 1320 | aws-sdk-wafregional (~> 1) 1321 | aws-sdk-wafv2 (~> 1) 1322 | aws-sdk-wellarchitected (~> 1) 1323 | aws-sdk-workdocs (~> 1) 1324 | aws-sdk-worklink (~> 1) 1325 | aws-sdk-workmail (~> 1) 1326 | aws-sdk-workmailmessageflow (~> 1) 1327 | aws-sdk-workspaces (~> 1) 1328 | aws-sdk-workspacesthinclient (~> 1) 1329 | aws-sdk-workspacesweb (~> 1) 1330 | aws-sdk-xray (~> 1) 1331 | aws-sdk-robomaker (1.70.0) 1332 | aws-sdk-core (~> 3, >= 3.201.0) 1333 | aws-sigv4 (~> 1.5) 1334 | aws-sdk-rolesanywhere (1.22.0) 1335 | aws-sdk-core (~> 3, >= 3.201.0) 1336 | aws-sigv4 (~> 1.1) 1337 | aws-sdk-route53 (1.94.0) 1338 | aws-sdk-core (~> 3, >= 3.201.0) 1339 | aws-sigv4 (~> 1.5) 1340 | aws-sdk-route53domains (1.63.0) 1341 | aws-sdk-core (~> 3, >= 3.201.0) 1342 | aws-sigv4 (~> 1.5) 1343 | aws-sdk-route53profiles (1.7.0) 1344 | aws-sdk-core (~> 3, >= 3.201.0) 1345 | aws-sigv4 (~> 1.1) 1346 | aws-sdk-route53recoverycluster (1.31.0) 1347 | aws-sdk-core (~> 3, >= 3.201.0) 1348 | aws-sigv4 (~> 1.5) 1349 | aws-sdk-route53recoverycontrolconfig (1.30.0) 1350 | aws-sdk-core (~> 3, >= 3.201.0) 1351 | aws-sigv4 (~> 1.1) 1352 | aws-sdk-route53recoveryreadiness (1.28.0) 1353 | aws-sdk-core (~> 3, >= 3.201.0) 1354 | aws-sigv4 (~> 1.1) 1355 | aws-sdk-route53resolver (1.64.0) 1356 | aws-sdk-core (~> 3, >= 3.201.0) 1357 | aws-sigv4 (~> 1.5) 1358 | aws-sdk-s3 (1.156.0) 1359 | aws-sdk-core (~> 3, >= 3.201.0) 1360 | aws-sdk-kms (~> 1) 1361 | aws-sigv4 (~> 1.5) 1362 | aws-sdk-s3control (1.87.0) 1363 | aws-sdk-core (~> 3, >= 3.201.0) 1364 | aws-sigv4 (~> 1.5) 1365 | aws-sdk-s3outposts (1.35.0) 1366 | aws-sdk-core (~> 3, >= 3.201.0) 1367 | aws-sigv4 (~> 1.5) 1368 | aws-sdk-sagemaker (1.254.0) 1369 | aws-sdk-core (~> 3, >= 3.201.0) 1370 | aws-sigv4 (~> 1.5) 1371 | aws-sdk-sagemakeredgemanager (1.31.0) 1372 | aws-sdk-core (~> 3, >= 3.201.0) 1373 | aws-sigv4 (~> 1.5) 1374 | aws-sdk-sagemakerfeaturestoreruntime (1.36.0) 1375 | aws-sdk-core (~> 3, >= 3.201.0) 1376 | aws-sigv4 (~> 1.5) 1377 | aws-sdk-sagemakergeospatial (1.19.0) 1378 | aws-sdk-core (~> 3, >= 3.201.0) 1379 | aws-sigv4 (~> 1.1) 1380 | aws-sdk-sagemakermetrics (1.19.0) 1381 | aws-sdk-core (~> 3, >= 3.201.0) 1382 | aws-sigv4 (~> 1.5) 1383 | aws-sdk-sagemakerruntime (1.68.0) 1384 | aws-sdk-core (~> 3, >= 3.201.0) 1385 | aws-sigv4 (~> 1.5) 1386 | aws-sdk-savingsplans (1.46.0) 1387 | aws-sdk-core (~> 3, >= 3.201.0) 1388 | aws-sigv4 (~> 1.5) 1389 | aws-sdk-scheduler (1.19.0) 1390 | aws-sdk-core (~> 3, >= 3.201.0) 1391 | aws-sigv4 (~> 1.1) 1392 | aws-sdk-schemas (1.41.0) 1393 | aws-sdk-core (~> 3, >= 3.201.0) 1394 | aws-sigv4 (~> 1.1) 1395 | aws-sdk-secretsmanager (1.102.0) 1396 | aws-sdk-core (~> 3, >= 3.201.0) 1397 | aws-sigv4 (~> 1.5) 1398 | aws-sdk-securityhub (1.113.0) 1399 | aws-sdk-core (~> 3, >= 3.201.0) 1400 | aws-sigv4 (~> 1.5) 1401 | aws-sdk-securitylake (1.24.0) 1402 | aws-sdk-core (~> 3, >= 3.201.0) 1403 | aws-sigv4 (~> 1.5) 1404 | aws-sdk-serverlessapplicationrepository (1.62.0) 1405 | aws-sdk-core (~> 3, >= 3.201.0) 1406 | aws-sigv4 (~> 1.1) 1407 | aws-sdk-servicecatalog (1.99.0) 1408 | aws-sdk-core (~> 3, >= 3.201.0) 1409 | aws-sigv4 (~> 1.5) 1410 | aws-sdk-servicediscovery (1.69.0) 1411 | aws-sdk-core (~> 3, >= 3.201.0) 1412 | aws-sigv4 (~> 1.5) 1413 | aws-sdk-servicequotas (1.42.0) 1414 | aws-sdk-core (~> 3, >= 3.201.0) 1415 | aws-sigv4 (~> 1.5) 1416 | aws-sdk-ses (1.68.0) 1417 | aws-sdk-core (~> 3, >= 3.201.0) 1418 | aws-sigv4 (~> 1.5) 1419 | aws-sdk-sesv2 (1.55.0) 1420 | aws-sdk-core (~> 3, >= 3.201.0) 1421 | aws-sigv4 (~> 1.5) 1422 | aws-sdk-shield (1.68.0) 1423 | aws-sdk-core (~> 3, >= 3.201.0) 1424 | aws-sigv4 (~> 1.5) 1425 | aws-sdk-signer (1.59.0) 1426 | aws-sdk-core (~> 3, >= 3.201.0) 1427 | aws-sigv4 (~> 1.5) 1428 | aws-sdk-simpledb (1.51.0) 1429 | aws-sdk-core (~> 3, >= 3.201.0) 1430 | aws-sigv2 (~> 1.0) 1431 | aws-sdk-simspaceweaver (1.20.0) 1432 | aws-sdk-core (~> 3, >= 3.201.0) 1433 | aws-sigv4 (~> 1.1) 1434 | aws-sdk-sms (1.59.0) 1435 | aws-sdk-core (~> 3, >= 3.201.0) 1436 | aws-sigv4 (~> 1.1) 1437 | aws-sdk-snowball (1.73.0) 1438 | aws-sdk-core (~> 3, >= 3.201.0) 1439 | aws-sigv4 (~> 1.5) 1440 | aws-sdk-snowdevicemanagement (1.25.0) 1441 | aws-sdk-core (~> 3, >= 3.201.0) 1442 | aws-sigv4 (~> 1.1) 1443 | aws-sdk-sns (1.82.0) 1444 | aws-sdk-core (~> 3, >= 3.201.0) 1445 | aws-sigv4 (~> 1.5) 1446 | aws-sdk-sqs (1.80.0) 1447 | aws-sdk-core (~> 3, >= 3.201.0) 1448 | aws-sigv4 (~> 1.5) 1449 | aws-sdk-ssm (1.173.0) 1450 | aws-sdk-core (~> 3, >= 3.201.0) 1451 | aws-sigv4 (~> 1.5) 1452 | aws-sdk-ssmcontacts (1.34.0) 1453 | aws-sdk-core (~> 3, >= 3.201.0) 1454 | aws-sigv4 (~> 1.5) 1455 | aws-sdk-ssmincidents (1.39.0) 1456 | aws-sdk-core (~> 3, >= 3.201.0) 1457 | aws-sigv4 (~> 1.1) 1458 | aws-sdk-ssmsap (1.23.0) 1459 | aws-sdk-core (~> 3, >= 3.201.0) 1460 | aws-sigv4 (~> 1.1) 1461 | aws-sdk-ssoadmin (1.41.0) 1462 | aws-sdk-core (~> 3, >= 3.201.0) 1463 | aws-sigv4 (~> 1.1) 1464 | aws-sdk-states (1.73.0) 1465 | aws-sdk-core (~> 3, >= 3.201.0) 1466 | aws-sigv4 (~> 1.5) 1467 | aws-sdk-storagegateway (1.91.0) 1468 | aws-sdk-core (~> 3, >= 3.201.0) 1469 | aws-sigv4 (~> 1.5) 1470 | aws-sdk-supplychain (1.8.0) 1471 | aws-sdk-core (~> 3, >= 3.201.0) 1472 | aws-sigv4 (~> 1.1) 1473 | aws-sdk-support (1.63.0) 1474 | aws-sdk-core (~> 3, >= 3.201.0) 1475 | aws-sigv4 (~> 1.5) 1476 | aws-sdk-supportapp (1.20.0) 1477 | aws-sdk-core (~> 3, >= 3.201.0) 1478 | aws-sigv4 (~> 1.1) 1479 | aws-sdk-swf (1.59.0) 1480 | aws-sdk-core (~> 3, >= 3.201.0) 1481 | aws-sigv4 (~> 1.5) 1482 | aws-sdk-synthetics (1.47.0) 1483 | aws-sdk-core (~> 3, >= 3.201.0) 1484 | aws-sigv4 (~> 1.5) 1485 | aws-sdk-taxsettings (1.5.0) 1486 | aws-sdk-core (~> 3, >= 3.201.0) 1487 | aws-sigv4 (~> 1.5) 1488 | aws-sdk-textract (1.63.0) 1489 | aws-sdk-core (~> 3, >= 3.201.0) 1490 | aws-sigv4 (~> 1.5) 1491 | aws-sdk-timestreaminfluxdb (1.6.0) 1492 | aws-sdk-core (~> 3, >= 3.201.0) 1493 | aws-sigv4 (~> 1.1) 1494 | aws-sdk-timestreamquery (1.38.0) 1495 | aws-sdk-core (~> 3, >= 3.201.0) 1496 | aws-sigv4 (~> 1.5) 1497 | aws-sdk-timestreamwrite (1.34.0) 1498 | aws-sdk-core (~> 3, >= 3.201.0) 1499 | aws-sigv4 (~> 1.1) 1500 | aws-sdk-tnb (1.17.0) 1501 | aws-sdk-core (~> 3, >= 3.201.0) 1502 | aws-sigv4 (~> 1.1) 1503 | aws-sdk-transcribeservice (1.103.0) 1504 | aws-sdk-core (~> 3, >= 3.201.0) 1505 | aws-sigv4 (~> 1.5) 1506 | aws-sdk-transcribestreamingservice (1.64.0) 1507 | aws-sdk-core (~> 3, >= 3.201.0) 1508 | aws-sigv4 (~> 1.5) 1509 | aws-sdk-transfer (1.98.0) 1510 | aws-sdk-core (~> 3, >= 3.201.0) 1511 | aws-sigv4 (~> 1.1) 1512 | aws-sdk-translate (1.70.0) 1513 | aws-sdk-core (~> 3, >= 3.201.0) 1514 | aws-sigv4 (~> 1.5) 1515 | aws-sdk-trustedadvisor (1.10.0) 1516 | aws-sdk-core (~> 3, >= 3.201.0) 1517 | aws-sigv4 (~> 1.1) 1518 | aws-sdk-verifiedpermissions (1.28.0) 1519 | aws-sdk-core (~> 3, >= 3.201.0) 1520 | aws-sigv4 (~> 1.1) 1521 | aws-sdk-voiceid (1.29.0) 1522 | aws-sdk-core (~> 3, >= 3.201.0) 1523 | aws-sigv4 (~> 1.1) 1524 | aws-sdk-vpclattice (1.17.0) 1525 | aws-sdk-core (~> 3, >= 3.201.0) 1526 | aws-sigv4 (~> 1.1) 1527 | aws-sdk-waf (1.66.0) 1528 | aws-sdk-core (~> 3, >= 3.201.0) 1529 | aws-sigv4 (~> 1.5) 1530 | aws-sdk-wafregional (1.67.0) 1531 | aws-sdk-core (~> 3, >= 3.201.0) 1532 | aws-sigv4 (~> 1.5) 1533 | aws-sdk-wafv2 (1.88.0) 1534 | aws-sdk-core (~> 3, >= 3.201.0) 1535 | aws-sigv4 (~> 1.5) 1536 | aws-sdk-wellarchitected (1.41.0) 1537 | aws-sdk-core (~> 3, >= 3.201.0) 1538 | aws-sigv4 (~> 1.5) 1539 | aws-sdk-workdocs (1.62.0) 1540 | aws-sdk-core (~> 3, >= 3.201.0) 1541 | aws-sigv4 (~> 1.5) 1542 | aws-sdk-worklink (1.52.0) 1543 | aws-sdk-core (~> 3, >= 3.201.0) 1544 | aws-sigv4 (~> 1.5) 1545 | aws-sdk-workmail (1.70.0) 1546 | aws-sdk-core (~> 3, >= 3.201.0) 1547 | aws-sigv4 (~> 1.5) 1548 | aws-sdk-workmailmessageflow (1.40.0) 1549 | aws-sdk-core (~> 3, >= 3.201.0) 1550 | aws-sigv4 (~> 1.5) 1551 | aws-sdk-workspaces (1.110.0) 1552 | aws-sdk-core (~> 3, >= 3.201.0) 1553 | aws-sigv4 (~> 1.5) 1554 | aws-sdk-workspacesthinclient (1.11.0) 1555 | aws-sdk-core (~> 3, >= 3.201.0) 1556 | aws-sigv4 (~> 1.5) 1557 | aws-sdk-workspacesweb (1.26.0) 1558 | aws-sdk-core (~> 3, >= 3.201.0) 1559 | aws-sigv4 (~> 1.1) 1560 | aws-sdk-xray (1.70.0) 1561 | aws-sdk-core (~> 3, >= 3.201.0) 1562 | aws-sigv4 (~> 1.5) 1563 | aws-sigv2 (1.2.0) 1564 | aws-sigv4 (1.9.0) 1565 | aws-eventstream (~> 1, >= 1.0.2) 1566 | aws_config (0.1.1) 1567 | awsecrets (1.15.1) 1568 | aws-sdk (>= 2, < 4) 1569 | aws_config (~> 0.1.0) 1570 | awspec (1.30.0) 1571 | addressable 1572 | aws-sdk (~> 3) 1573 | awsecrets (~> 1) 1574 | dry-inflector 1575 | ipaddress 1576 | rspec (~> 3.0) 1577 | rspec-its 1578 | term-ansicolor 1579 | thor 1580 | base64 (0.2.0) 1581 | bigdecimal (3.1.8) 1582 | colored2 (3.1.2) 1583 | concurrent-ruby (1.3.3) 1584 | confidante (0.28.0) 1585 | activesupport (>= 4) 1586 | hiera (~> 3.3) 1587 | shikashi (~> 0.6) 1588 | vault (~> 0.17) 1589 | connection_pool (2.4.1) 1590 | diff-lcs (1.5.1) 1591 | down (5.4.1) 1592 | addressable (~> 2.8) 1593 | drb (2.2.1) 1594 | dry-inflector (1.1.0) 1595 | evalhook (0.6.0) 1596 | partialruby (~> 0.3) 1597 | sexp_processor (~> 4.0) 1598 | excon (0.111.0) 1599 | faraday (2.10.0) 1600 | faraday-net_http (>= 2.0, < 3.2) 1601 | logger 1602 | faraday-net_http (3.1.1) 1603 | net-http 1604 | getsource (0.2.2) 1605 | git (1.19.1) 1606 | addressable (~> 2.8) 1607 | rchardet (~> 1.8) 1608 | hamster (3.0.0) 1609 | concurrent-ruby (~> 1.0) 1610 | hiera (3.12.0) 1611 | i18n (1.14.5) 1612 | concurrent-ruby (~> 1.0) 1613 | immutable-struct (2.4.1) 1614 | ipaddress (0.8.3) 1615 | jmespath (1.6.2) 1616 | json (2.9.1) 1617 | language_server-protocol (3.17.0.3) 1618 | lino (3.1.0) 1619 | hamster (~> 3.0) 1620 | open4 (~> 1.3) 1621 | logger (1.6.0) 1622 | minitar (0.9) 1623 | minitest (5.24.1) 1624 | mize (0.6.0) 1625 | mutex_m (0.2.0) 1626 | net-http (0.4.1) 1627 | uri 1628 | net-ssh (7.2.3) 1629 | netaddr (2.0.6) 1630 | octokit (8.1.0) 1631 | base64 1632 | faraday (>= 1, < 3) 1633 | sawyer (~> 0.9) 1634 | open4 (1.3.4) 1635 | parallel (1.26.3) 1636 | parser (3.3.7.0) 1637 | ast (~> 2.4.1) 1638 | racc 1639 | partialruby (0.3.0) 1640 | ruby2ruby (~> 2) 1641 | ruby_parser (~> 3) 1642 | public_suffix (6.0.1) 1643 | racc (1.8.1) 1644 | rainbow (3.1.1) 1645 | rake (13.2.1) 1646 | rake_circle_ci (0.13.0) 1647 | colored2 (~> 3.1) 1648 | excon (~> 0.72) 1649 | rake_factory (~> 0.33) 1650 | sshkey (~> 2.0) 1651 | rake_dependencies (3.5.0) 1652 | down (~> 5.3) 1653 | hamster (~> 3.0) 1654 | minitar (~> 0.9) 1655 | rake_factory (~> 0.23) 1656 | rubyzip (>= 1.3) 1657 | rake_factory (0.34.0.pre.2) 1658 | activesupport (>= 4) 1659 | rake (~> 13.0) 1660 | rake_git (0.1.0.pre.11) 1661 | colored2 (~> 3.1) 1662 | git (~> 1.13, >= 1.13.2) 1663 | rake_factory (>= 0.32.0.pre.2) 1664 | rake_git_crypt (0.1.0.pre.33) 1665 | colored2 (~> 3.1) 1666 | rake_factory (>= 0.32.0.pre.2) 1667 | ruby_git_crypt (>= 0.1.0.pre.8) 1668 | ruby_gpg2 (>= 0.11.0.pre.6) 1669 | rake_github (0.15.0) 1670 | colored2 (~> 3.1) 1671 | octokit (>= 4.16, < 9.0) 1672 | rake_factory (~> 0.33) 1673 | sshkey (~> 2.0) 1674 | rake_gpg (0.18.0) 1675 | rake_factory (~> 0.23) 1676 | ruby_gpg2 (~> 0.6) 1677 | rake_ssh (0.10.0) 1678 | colored2 (~> 3.1) 1679 | rake_factory (~> 0.23) 1680 | sshkey (~> 2.0) 1681 | rake_terraform (1.23.0) 1682 | colored2 (~> 3.1) 1683 | rake_dependencies (~> 3.1) 1684 | rake_factory (~> 0.23) 1685 | ruby-terraform (~> 1.4) 1686 | rchardet (1.8.0) 1687 | regexp_parser (2.10.0) 1688 | rexml (3.4.0) 1689 | rspec (3.13.0) 1690 | rspec-core (~> 3.13.0) 1691 | rspec-expectations (~> 3.13.0) 1692 | rspec-mocks (~> 3.13.0) 1693 | rspec-core (3.13.0) 1694 | rspec-support (~> 3.13.0) 1695 | rspec-expectations (3.13.1) 1696 | diff-lcs (>= 1.2.0, < 2.0) 1697 | rspec-support (~> 3.13.0) 1698 | rspec-its (1.3.0) 1699 | rspec-core (>= 3.0.0) 1700 | rspec-expectations (>= 3.0.0) 1701 | rspec-mocks (3.13.1) 1702 | diff-lcs (>= 1.2.0, < 2.0) 1703 | rspec-support (~> 3.13.0) 1704 | rspec-support (3.13.1) 1705 | rspec-terraform (0.4.0) 1706 | confidante (>= 0.27) 1707 | rspec (>= 3.0) 1708 | ruby-terraform (= 1.7.0.pre.18) 1709 | rubocop (1.65.1) 1710 | json (~> 2.3) 1711 | language_server-protocol (>= 3.17.0) 1712 | parallel (~> 1.10) 1713 | parser (>= 3.3.0.2) 1714 | rainbow (>= 2.2.2, < 4.0) 1715 | regexp_parser (>= 2.4, < 3.0) 1716 | rexml (>= 3.2.5, < 4.0) 1717 | rubocop-ast (>= 1.31.1, < 2.0) 1718 | ruby-progressbar (~> 1.7) 1719 | unicode-display_width (>= 2.4.0, < 3.0) 1720 | rubocop-ast (1.37.0) 1721 | parser (>= 3.3.1.0) 1722 | rubocop-rake (0.6.0) 1723 | rubocop (~> 1.0) 1724 | rubocop-rspec (3.4.0) 1725 | rubocop (~> 1.61) 1726 | ruby-progressbar (1.13.0) 1727 | ruby-terraform (1.7.0.pre.18) 1728 | immutable-struct (~> 2.4) 1729 | lino (~> 3.0) 1730 | ruby2ruby (2.5.0) 1731 | ruby_parser (~> 3.1) 1732 | sexp_processor (~> 4.6) 1733 | ruby_git_crypt (0.1.0.pre.8) 1734 | immutable-struct (~> 2.4) 1735 | lino (>= 3.1) 1736 | ruby_gpg2 (0.11.0.pre.6) 1737 | lino (>= 3.1) 1738 | ruby_parser (3.20.3) 1739 | sexp_processor (~> 4.16) 1740 | rubyzip (2.4.1) 1741 | sawyer (0.9.2) 1742 | addressable (>= 2.3.5) 1743 | faraday (>= 0.17.3, < 3) 1744 | semantic (1.6.1) 1745 | sexp_processor (4.17.0) 1746 | shikashi (0.6.0) 1747 | evalhook (>= 0.6.0) 1748 | getsource (>= 0.1.0) 1749 | sshkey (2.0.0) 1750 | sync (0.5.0) 1751 | term-ansicolor (1.10.4) 1752 | mize (~> 0.5) 1753 | tins (~> 1.0) 1754 | thor (1.3.1) 1755 | tins (1.33.0) 1756 | bigdecimal 1757 | sync 1758 | tzinfo (2.0.6) 1759 | concurrent-ruby (~> 1.0) 1760 | unicode-display_width (2.6.0) 1761 | uri (0.13.0) 1762 | vault (0.18.1) 1763 | aws-sigv4 1764 | 1765 | PLATFORMS 1766 | arm64-darwin-21 1767 | arm64-darwin-22 1768 | ruby 1769 | x86_64-darwin-19 1770 | x86_64-darwin-21 1771 | x86_64-linux 1772 | 1773 | DEPENDENCIES 1774 | awspec 1775 | confidante 1776 | git 1777 | net-ssh 1778 | netaddr 1779 | rake 1780 | rake_circle_ci 1781 | rake_git 1782 | rake_git_crypt 1783 | rake_github 1784 | rake_gpg 1785 | rake_ssh 1786 | rake_terraform 1787 | rspec 1788 | rspec-terraform 1789 | rubocop 1790 | rubocop-rake 1791 | rubocop-rspec 1792 | rubyzip 1793 | semantic 1794 | 1795 | BUNDLED WITH 1796 | 2.5.15 1797 | --------------------------------------------------------------------------------