├── .circleci ├── config.yml └── gpg.private.enc ├── .envrc ├── .git-crypt ├── .gitattributes └── keys │ └── default │ └── 0 │ ├── 41D2606F66C3FF28874362B61A16916844CE9D82.gpg │ ├── 7BC0C4093952576143FA498EDA263CA4F28AB668.gpg │ ├── 933E3994686DC15C99D1369844037399AEDB1D8D.gpg │ └── D164A61C69E23C0F74475FBE5FFE76AD095FCA07.gpg ├── .gitattributes ├── .github └── dependabot.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .ruby-version ├── .tool-versions ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── config ├── defaults.yaml ├── gpg │ ├── jonas.gpg.public │ ├── liam.gpg.public │ └── toby.gpg.public ├── hiera.yaml ├── roles │ ├── full.yaml │ ├── prerequisites.yaml │ └── root.yaml └── secrets │ ├── .unlocked │ ├── bastion │ ├── ssh.private │ └── ssh.public │ ├── ci │ ├── aws-credentials.sh │ ├── encryption.passphrase │ ├── gpg.private │ ├── gpg.public │ ├── ssh.private │ └── ssh.public │ ├── circle_ci │ └── config.yaml │ └── github │ └── config.yaml ├── defaults.tf ├── docs ├── architecture.graffle │ ├── data.plist │ ├── image1.pdf │ ├── image18.pdf │ ├── image21.pdf │ └── image24.pdf └── architecture.png ├── examples └── full │ ├── .terraform.lock.hcl │ ├── bastion.tf │ ├── outputs.tf │ ├── prerequisites.tf │ ├── providers.tf │ ├── terraform.tf │ └── variables.tf ├── go ├── lib ├── paths.rb └── version.rb ├── main.tf ├── outputs.tf ├── scripts └── ci │ ├── common │ ├── configure-git.sh │ ├── install-git-crypt.sh │ ├── install-gpg-key.sh │ └── install-orb-deps.sh │ └── steps │ ├── build.sh │ ├── merge-pull-request.sh │ ├── prerelease.sh │ ├── release.sh │ └── test.sh ├── spec ├── integration │ ├── full_spec.rb │ └── spec_helper.rb └── unit │ ├── autoscaling_group_spec.rb │ ├── from_security_group_spec.rb │ ├── infra │ ├── prerequisites │ │ ├── .terraform.lock.hcl │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── provider.tf │ │ ├── terraform.tf │ │ └── variables.tf │ └── root │ │ ├── .terraform.lock.hcl │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── provider.tf │ │ ├── terraform.tf │ │ └── variables.tf │ ├── key_pair_spec.rb │ ├── launch_configuration_spec.rb │ ├── spec_helper.rb │ └── to_security_group_spec.rb ├── terraform.tf └── variables.tf /.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 | -------------------------------------------------------------------------------- /.circleci/gpg.private.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/.circleci/gpg.private.enc -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/41D2606F66C3FF28874362B61A16916844CE9D82.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/.git-crypt/keys/default/0/41D2606F66C3FF28874362B61A16916844CE9D82.gpg -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/7BC0C4093952576143FA498EDA263CA4F28AB668.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/.git-crypt/keys/default/0/7BC0C4093952576143FA498EDA263CA4F28AB668.gpg -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/933E3994686DC15C99D1369844037399AEDB1D8D.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/.git-crypt/keys/default/0/933E3994686DC15C99D1369844037399AEDB1D8D.gpg -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/D164A61C69E23C0F74475FBE5FFE76AD095FCA07.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/.git-crypt/keys/default/0/D164A61C69E23C0F74475FBE5FFE76AD095FCA07.gpg -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | config/secrets/** filter=git-crypt diff=git-crypt 2 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.1.1 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 3.1.1 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | 3 | ## 3.0.0 (Dec 29th, 2022) 4 | 5 | ENHANCEMENTS 6 | 7 | * This module can now be used with version 4 of the AWS provider. 8 | * An `associate_public_ip_address` var has been added, specifying whether 9 | a public IP address should be associated with the bastion instance(s). 10 | 11 | BACKWARDS INCOMPATIBILITIES / NOTES: 12 | 13 | * This module is now compatible with Terraform 1.0 and higher. 14 | 15 | 16 | ## 2.0.0 (May 27th, 2021) 17 | 18 | BACKWARDS INCOMPATIBILITIES / NOTES: 19 | 20 | * This module is now compatible with Terraform 0.14 and higher. 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'awspec' 6 | gem 'confidante' 7 | gem 'git' 8 | gem 'net-ssh' 9 | gem 'rake' 10 | gem 'rake_circle_ci' 11 | gem 'rake_git' 12 | gem 'rake_git_crypt' 13 | gem 'rake_github' 14 | gem 'rake_gpg' 15 | gem 'rake_ssh' 16 | gem 'rake_terraform' 17 | gem 'rspec' 18 | gem 'rspec-terraform' 19 | gem 'rubocop' 20 | gem 'rubocop-rake' 21 | gem 'rubocop-rspec' 22 | gem 'rubyzip' 23 | gem 'semantic' 24 | -------------------------------------------------------------------------------- /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.6.2) 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.1) 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.3.0) 1629 | octokit (8.1.0) 1630 | base64 1631 | faraday (>= 1, < 3) 1632 | sawyer (~> 0.9) 1633 | open4 (1.3.4) 1634 | parallel (1.26.3) 1635 | parser (3.3.7.0) 1636 | ast (~> 2.4.1) 1637 | racc 1638 | partialruby (0.3.0) 1639 | ruby2ruby (~> 2) 1640 | ruby_parser (~> 3) 1641 | public_suffix (6.0.1) 1642 | racc (1.8.1) 1643 | rainbow (3.1.1) 1644 | rake (13.3.0) 1645 | rake_circle_ci (0.13.0) 1646 | colored2 (~> 3.1) 1647 | excon (~> 0.72) 1648 | rake_factory (~> 0.33) 1649 | sshkey (~> 2.0) 1650 | rake_dependencies (3.5.0) 1651 | down (~> 5.3) 1652 | hamster (~> 3.0) 1653 | minitar (~> 0.9) 1654 | rake_factory (~> 0.23) 1655 | rubyzip (>= 1.3) 1656 | rake_factory (0.34.0.pre.2) 1657 | activesupport (>= 4) 1658 | rake (~> 13.0) 1659 | rake_git (0.3.0.pre.2) 1660 | colored2 (~> 3.1) 1661 | git (~> 1.13, >= 1.13.2) 1662 | rake_factory (~> 0.33) 1663 | rake_git_crypt (0.1.0.pre.33) 1664 | colored2 (~> 3.1) 1665 | rake_factory (>= 0.32.0.pre.2) 1666 | ruby_git_crypt (>= 0.1.0.pre.8) 1667 | ruby_gpg2 (>= 0.11.0.pre.6) 1668 | rake_github (0.15.0) 1669 | colored2 (~> 3.1) 1670 | octokit (>= 4.16, < 9.0) 1671 | rake_factory (~> 0.33) 1672 | sshkey (~> 2.0) 1673 | rake_gpg (0.18.0) 1674 | rake_factory (~> 0.23) 1675 | ruby_gpg2 (~> 0.6) 1676 | rake_ssh (0.12.0) 1677 | colored2 (~> 3.1) 1678 | rake_factory (~> 0.33) 1679 | sshkey (~> 2.0) 1680 | rake_terraform (1.23.0) 1681 | colored2 (~> 3.1) 1682 | rake_dependencies (~> 3.1) 1683 | rake_factory (~> 0.23) 1684 | ruby-terraform (~> 1.4) 1685 | rchardet (1.8.0) 1686 | regexp_parser (2.10.0) 1687 | rexml (3.4.0) 1688 | rspec (3.13.1) 1689 | rspec-core (~> 3.13.0) 1690 | rspec-expectations (~> 3.13.0) 1691 | rspec-mocks (~> 3.13.0) 1692 | rspec-core (3.13.4) 1693 | rspec-support (~> 3.13.0) 1694 | rspec-expectations (3.13.5) 1695 | diff-lcs (>= 1.2.0, < 2.0) 1696 | rspec-support (~> 3.13.0) 1697 | rspec-its (1.3.0) 1698 | rspec-core (>= 3.0.0) 1699 | rspec-expectations (>= 3.0.0) 1700 | rspec-mocks (3.13.5) 1701 | diff-lcs (>= 1.2.0, < 2.0) 1702 | rspec-support (~> 3.13.0) 1703 | rspec-support (3.13.4) 1704 | rspec-terraform (0.4.0) 1705 | confidante (>= 0.27) 1706 | rspec (>= 3.0) 1707 | ruby-terraform (= 1.7.0.pre.18) 1708 | rubocop (1.65.1) 1709 | json (~> 2.3) 1710 | language_server-protocol (>= 3.17.0) 1711 | parallel (~> 1.10) 1712 | parser (>= 3.3.0.2) 1713 | rainbow (>= 2.2.2, < 4.0) 1714 | regexp_parser (>= 2.4, < 3.0) 1715 | rexml (>= 3.2.5, < 4.0) 1716 | rubocop-ast (>= 1.31.1, < 2.0) 1717 | ruby-progressbar (~> 1.7) 1718 | unicode-display_width (>= 2.4.0, < 3.0) 1719 | rubocop-ast (1.37.0) 1720 | parser (>= 3.3.1.0) 1721 | rubocop-rake (0.6.0) 1722 | rubocop (~> 1.0) 1723 | rubocop-rspec (3.4.0) 1724 | rubocop (~> 1.61) 1725 | ruby-progressbar (1.13.0) 1726 | ruby-terraform (1.7.0.pre.18) 1727 | immutable-struct (~> 2.4) 1728 | lino (~> 3.0) 1729 | ruby2ruby (2.5.0) 1730 | ruby_parser (~> 3.1) 1731 | sexp_processor (~> 4.6) 1732 | ruby_git_crypt (0.1.0.pre.8) 1733 | immutable-struct (~> 2.4) 1734 | lino (>= 3.1) 1735 | ruby_gpg2 (0.11.0.pre.6) 1736 | lino (>= 3.1) 1737 | ruby_parser (3.20.3) 1738 | sexp_processor (~> 4.16) 1739 | rubyzip (2.4.1) 1740 | sawyer (0.9.2) 1741 | addressable (>= 2.3.5) 1742 | faraday (>= 0.17.3, < 3) 1743 | semantic (1.6.1) 1744 | sexp_processor (4.17.0) 1745 | shikashi (0.6.0) 1746 | evalhook (>= 0.6.0) 1747 | getsource (>= 0.1.0) 1748 | sshkey (2.0.0) 1749 | sync (0.5.0) 1750 | term-ansicolor (1.10.4) 1751 | mize (~> 0.5) 1752 | tins (~> 1.0) 1753 | thor (1.3.1) 1754 | tins (1.33.0) 1755 | bigdecimal 1756 | sync 1757 | tzinfo (2.0.6) 1758 | concurrent-ruby (~> 1.0) 1759 | unicode-display_width (2.6.0) 1760 | uri (0.13.2) 1761 | vault (0.18.1) 1762 | aws-sigv4 1763 | 1764 | PLATFORMS 1765 | arm64-darwin-21 1766 | arm64-darwin-22 1767 | ruby 1768 | x86_64-darwin-19 1769 | x86_64-darwin-21 1770 | x86_64-linux 1771 | 1772 | DEPENDENCIES 1773 | awspec 1774 | confidante 1775 | git 1776 | net-ssh 1777 | rake 1778 | rake_circle_ci 1779 | rake_git 1780 | rake_git_crypt 1781 | rake_github 1782 | rake_gpg 1783 | rake_ssh 1784 | rake_terraform 1785 | rspec 1786 | rspec-terraform 1787 | rubocop 1788 | rubocop-rake 1789 | rubocop-rspec 1790 | rubyzip 1791 | semantic 1792 | 1793 | BUNDLED WITH 1794 | 2.3.7 1795 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Terraform AWS Bastion 2 | ===================== 3 | 4 | [![Version](https://img.shields.io/github/v/tag/infrablocks/terraform-aws-bastion?label=version&sort=semver)](https://github.com/infrablocks/terraform-aws-bastion/tags) 5 | [![Build Pipeline](https://img.shields.io/circleci/build/github/infrablocks/terraform-aws-bastion/main?label=build-pipeline)](https://app.circleci.com/pipelines/github/infrablocks/terraform-aws-bastion?filter=all) 6 | [![Maintainer](https://img.shields.io/badge/maintainer-go--atomic.io-red)](https://go-atomic.io) 7 | 8 | A Terraform module for deploying a supervised bastion into a base network in 9 | AWS. 10 | 11 | The bastion requires: 12 | 13 | * An existing base network 14 | * One or more existing load balancers 15 | 16 | The bastion consists of: 17 | 18 | * An autoscaling group and launch configuration for bastion instances 19 | configured with the supplied SSH key updating the supplied load balancers. 20 | * A security group allowing SSH access to the bastion from the load balancers. 21 | * A security group allowing SSH access from the bastion, for assigning to 22 | protected instances. 23 | 24 | ![Diagram of infrastructure managed by this module](https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/main/docs/architecture.png) 25 | 26 | Usage 27 | ----- 28 | 29 | To use the module, include something like the following in your Terraform 30 | configuration: 31 | 32 | ```terraform 33 | module "bastion" { 34 | source = "infrablocks/bastion/aws" 35 | version = "3.0.0" 36 | 37 | vpc_id = "vpc-fb7dc365" 38 | subnet_ids = ["subnet-ae4533c4", "subnet-443e6b12"] 39 | 40 | component = "important-component" 41 | deployment_identifier = "production" 42 | 43 | ami = "ami-bb373ddf" 44 | instance_type = "t2.micro" 45 | 46 | ssh_public_key_path = "~/.ssh/id_rsa.pub" 47 | 48 | allowed_cidrs = ["100.10.10.0/24", "200.20.0.0/16"] 49 | egress_cidrs = "10.0.0.0/16" 50 | 51 | load_balancer_names = ["lb-12345678"] 52 | 53 | minimum_instances = 1 54 | maximum_instances = 3 55 | desired_instances = 2 56 | } 57 | ``` 58 | 59 | As mentioned above, the bastion deploys into an existing base network. 60 | Whilst the base network can be created using any mechanism you like, the 61 | [AWS Base Networking](https://github.com/infrablocks/terraform-aws-base-networking) 62 | module will create everything you need. See the 63 | [docs](https://github.com/infrablocks/terraform-aws-base-networking/blob/main/README.md) 64 | for usage instructions. 65 | 66 | Similarly, the bastion is reachable through one or more load balancers. 67 | Whilst the load balancers can be created using any mechanism you like, the 68 | [AWS Classic Load Balancer](https://github.com/infrablocks/terraform-aws-classic-load-balancer) 69 | module will create everything you need. See the 70 | [docs](https://github.com/infrablocks/terraform-aws-classic-load-balancer/blob/main/README.md) 71 | for usage instructions. 72 | 73 | See the 74 | [Terraform registry entry](https://registry.terraform.io/modules/infrablocks/bastion/aws/latest) 75 | for more details. 76 | 77 | ### Inputs 78 | 79 | | Name | Description | Default | Required | 80 | |-------------------------------|----------------------------------------------------------------------------|:-----------:|:--------:| 81 | | `vpc_id` | The ID of the VPC the bastion should be deployed into. | - | Yes | 82 | | `subnet_ids` | The IDs of the subnets the bastion should deploy into. | - | Yes | 83 | | `component` | The name of this component. | - | Yes | 84 | | `deployment_identifier` | An identifier for this instantiation. | - | Yes | 85 | | `ami` | The ID of the AMI for the bastion instances. | - | Yes | 86 | | `instance_type` | The instance type of the bastion instances. | `"t2.nano"` | No | 87 | | `ssh_public_key_path` | The absolute path of the SSH public key to use for bastion access. | - | Yes | 88 | | `allowed_cidrs` | A list of CIDRs that are allowed to access the bastion. | - | Yes | 89 | | `egress_cidrs` | A list of CIDRs that are reachable from the bastion. | - | Yes | 90 | | `load_balancer_names` | The names of the load balancers to update on autoscaling events. | `[]` | No | 91 | | `minimum_instances` | The minimum number of bastion instances. | `1` | No | 92 | | `maximum_instances` | The maximum number of bastion instances. | `1` | No | 93 | | `desired_instances` | The desired number of bastion instances. | `1` | No | 94 | | `associate_public_ip_address` | Whether or not to associate a public IP address with an instance in a VPC. | `false` | No | 95 | 96 | ### Outputs 97 | 98 | | Name | Description | 99 | |--------------------------------------------|-----------------------------------------------------------------------| 100 | | `launch_configuration_name` | The name of the launch configuration for bastion instances. | 101 | | `allow_ssh_to_bastion_security_group_id` | The ID of the security group that allows ssh access to the bastion. | 102 | | `allow_ssh_from_bastion_security_group_id` | The ID of the security group that allows ssh access from the bastion. | 103 | 104 | ### Compatibility 105 | 106 | This module is compatible with Terraform versions greater than or equal to 107 | Terraform 1.0 and Terraform AWS provider versions greater than or equal to 3.29. 108 | 109 | Development 110 | ----------- 111 | 112 | ### Machine Requirements 113 | 114 | In order for the build to run correctly, a few tools will need to be installed 115 | on your development machine: 116 | 117 | * Ruby (3.1) 118 | * Bundler 119 | * git 120 | * git-crypt 121 | * gnupg 122 | * direnv 123 | * aws-vault 124 | 125 | #### Mac OS X Setup 126 | 127 | Installing the required tools is best managed by [homebrew](http://brew.sh). 128 | 129 | To install homebrew: 130 | 131 | ```shell 132 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 133 | ``` 134 | 135 | Then, to install the required tools: 136 | 137 | ```shell 138 | # ruby 139 | brew install rbenv 140 | brew install ruby-build 141 | echo 'eval "$(rbenv init - bash)"' >> ~/.bash_profile 142 | echo 'eval "$(rbenv init - zsh)"' >> ~/.zshrc 143 | eval "$(rbenv init -)" 144 | rbenv install 3.1.1 145 | rbenv rehash 146 | rbenv local 3.1.1 147 | gem install bundler 148 | 149 | # git, git-crypt, gnupg 150 | brew install git 151 | brew install git-crypt 152 | brew install gnupg 153 | 154 | # aws-vault 155 | brew cask install 156 | 157 | # direnv 158 | brew install direnv 159 | echo "$(direnv hook bash)" >> ~/.bash_profile 160 | echo "$(direnv hook zsh)" >> ~/.zshrc 161 | eval "$(direnv hook $SHELL)" 162 | 163 | direnv allow 164 | ``` 165 | 166 | ### Running the build 167 | 168 | Running the build requires an AWS account and AWS credentials. You are free to 169 | configure credentials however you like as long as an access key ID and secret 170 | access key are available. These instructions utilise 171 | [aws-vault](https://github.com/99designs/aws-vault) which makes credential 172 | management easy and secure. 173 | 174 | To run the full build, including unit and integration tests, execute: 175 | 176 | ```shell 177 | aws-vault exec -- ./go 178 | ``` 179 | 180 | To run the unit tests, execute: 181 | 182 | ```shell 183 | aws-vault exec -- ./go test:unit 184 | ``` 185 | 186 | To run the integration tests, execute: 187 | 188 | ```shell 189 | aws-vault exec -- ./go test:integration 190 | ``` 191 | 192 | To provision the module prerequisites: 193 | 194 | ```shell 195 | aws-vault exec -- ./go deployment:prerequisites:provision[] 196 | ``` 197 | 198 | To provision the module contents: 199 | 200 | ```shell 201 | aws-vault exec -- ./go deployment:root:provision[] 202 | ``` 203 | 204 | To destroy the module contents: 205 | 206 | ```shell 207 | aws-vault exec -- ./go deployment:root:destroy[] 208 | ``` 209 | 210 | To destroy the module prerequisites: 211 | 212 | ```shell 213 | aws-vault exec -- ./go deployment:prerequisites:destroy[] 214 | ``` 215 | 216 | Configuration parameters can be overridden via environment variables. For 217 | example, to run the unit tests with a seed of `"testing"`, execute: 218 | 219 | ```shell 220 | SEED=testing aws-vault exec -- ./go test:unit 221 | ``` 222 | 223 | When a seed is provided via an environment variable, infrastructure will not be 224 | destroyed at the end of test execution. This can be useful during development 225 | to avoid lengthy provision and destroy cycles. 226 | 227 | To subsequently destroy unit test infrastructure for a given seed: 228 | 229 | ```shell 230 | FORCE_DESTROY=yes SEED=testing aws-vault exec -- ./go test:unit 231 | ``` 232 | 233 | ### Common Tasks 234 | 235 | #### Generating an SSH key pair 236 | 237 | To generate an SSH key pair: 238 | 239 | ```shell 240 | ssh-keygen -m PEM -t rsa -b 4096 -C integration-test@example.com -N '' -f config/secrets/keys/bastion/ssh 241 | ``` 242 | 243 | #### Generating a self-signed certificate 244 | 245 | To generate a self signed certificate: 246 | 247 | ```shell 248 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 249 | ``` 250 | 251 | To decrypt the resulting key: 252 | 253 | ```shell 254 | openssl rsa -in key.pem -out ssl.key 255 | ``` 256 | 257 | #### Managing CircleCI keys 258 | 259 | To encrypt a GPG key for use by CircleCI: 260 | 261 | ```shell 262 | openssl aes-256-cbc \ 263 | -e \ 264 | -md sha1 \ 265 | -in ./config/secrets/ci/gpg.private \ 266 | -out ./.circleci/gpg.private.enc \ 267 | -k "" 268 | ``` 269 | 270 | To check decryption is working correctly: 271 | 272 | ```shell 273 | openssl aes-256-cbc \ 274 | -d \ 275 | -md sha1 \ 276 | -in ./.circleci/gpg.private.enc \ 277 | -k "" 278 | ``` 279 | 280 | Contributing 281 | ------------ 282 | 283 | Bug reports and pull requests are welcome on GitHub at 284 | https://github.com/infrablocks/terraform-aws-bastion. 285 | This project is intended to be a safe, welcoming space for collaboration, and 286 | contributors are expected to adhere to the 287 | [Contributor Covenant](http://contributor-covenant.org) code of conduct. 288 | 289 | License 290 | ------- 291 | 292 | The library is available as open source under the terms of the 293 | [MIT License](http://opensource.org/licenses/MIT). 294 | -------------------------------------------------------------------------------- /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 :bastion do 95 | RakeSSH.define_key_tasks( 96 | path: 'config/secrets/bastion/', 97 | comment: 'maintainers@infrablocks.io' 98 | ) 99 | end 100 | 101 | namespace :secrets do 102 | namespace :gpg do 103 | RakeGPG.define_generate_key_task( 104 | output_directory: 'config/secrets/ci', 105 | name_prefix: 'gpg', 106 | owner_name: 'InfraBlocks Maintainers', 107 | owner_email: 'maintainers@infrablocks.io', 108 | owner_comment: 'terraform-aws-bastion CI Key' 109 | ) 110 | end 111 | 112 | task generate: ['gpg:generate'] 113 | end 114 | end 115 | 116 | namespace :secrets do 117 | namespace :directory do 118 | desc 'Ensure secrets directory exists and is set up correctly' 119 | task :ensure do 120 | FileUtils.mkdir_p('config/secrets') 121 | unless File.exist?('config/secrets/.unlocked') 122 | File.write('config/secrets/.unlocked', 'true') 123 | end 124 | end 125 | end 126 | 127 | desc 'Generate all generatable secrets.' 128 | task generate: %w[ 129 | directory:ensure 130 | encryption:passphrase:generate 131 | keys:deploy:generate 132 | keys:bastion:generate 133 | keys:secrets:generate 134 | ] 135 | 136 | desc 'Provision all secrets.' 137 | task provision: [:generate] 138 | 139 | desc 'Delete all secrets.' 140 | task :destroy do 141 | rm_rf 'config/secrets' 142 | end 143 | 144 | desc 'Rotate all secrets.' 145 | task rotate: [:'git_crypt:reinstall'] 146 | end 147 | 148 | RakeCircleCI.define_project_tasks( 149 | namespace: :circle_ci, 150 | project_slug: 'github/infrablocks/terraform-aws-bastion' 151 | ) do |t| 152 | circle_ci_config = 153 | YAML.load_file('config/secrets/circle_ci/config.yaml') 154 | 155 | t.api_token = circle_ci_config['circle_ci_api_token'] 156 | t.environment_variables = { 157 | ENCRYPTION_PASSPHRASE: 158 | File.read('config/secrets/ci/encryption.passphrase') 159 | .chomp, 160 | CIRCLECI_API_KEY: 161 | YAML.load_file( 162 | 'config/secrets/circle_ci/config.yaml' 163 | )['circle_ci_api_token'] 164 | } 165 | t.checkout_keys = [] 166 | t.ssh_keys = [ 167 | { 168 | hostname: 'github.com', 169 | private_key: File.read('config/secrets/ci/ssh.private') 170 | } 171 | ] 172 | end 173 | 174 | RakeGithub.define_repository_tasks( 175 | namespace: :github, 176 | repository: 'infrablocks/terraform-aws-bastion' 177 | ) do |t| 178 | github_config = 179 | YAML.load_file('config/secrets/github/config.yaml') 180 | 181 | t.access_token = github_config['github_personal_access_token'] 182 | t.deploy_keys = [ 183 | { 184 | title: 'CircleCI', 185 | public_key: File.read('config/secrets/ci/ssh.public') 186 | } 187 | ] 188 | end 189 | 190 | namespace :pipeline do 191 | desc 'Prepare CircleCI Pipeline' 192 | task prepare: %i[ 193 | circle_ci:env_vars:ensure 194 | circle_ci:checkout_keys:ensure 195 | circle_ci:ssh_keys:ensure 196 | github:deploy_keys:ensure 197 | ] 198 | end 199 | 200 | RuboCop::RakeTask.new 201 | 202 | namespace :test do 203 | namespace :code do 204 | desc 'Run all checks on the test code' 205 | task check: [:rubocop] 206 | 207 | desc 'Attempt to automatically fix issues with the test code' 208 | task fix: [:'rubocop:autocorrect_all'] 209 | end 210 | 211 | desc 'Run module unit tests' 212 | RSpec::Core::RakeTask.new(unit: ['terraform:ensure']) do |t| 213 | t.pattern = 'spec/unit/**{,/*/**}/*_spec.rb' 214 | t.rspec_opts = '-I spec/unit' 215 | 216 | ENV['AWS_REGION'] = configuration.region 217 | end 218 | 219 | desc 'Run module integration tests' 220 | RSpec::Core::RakeTask.new(integration: ['terraform:ensure']) do |t| 221 | t.pattern = 'spec/integration/**{,/*/**}/*_spec.rb' 222 | t.rspec_opts = '-I spec/integration' 223 | 224 | ENV['AWS_REGION'] = configuration.region 225 | end 226 | end 227 | 228 | namespace :deployment do 229 | namespace :prerequisites do 230 | RakeTerraform.define_command_tasks( 231 | configuration_name: 'prerequisites', 232 | argument_names: [:seed] 233 | ) do |t, args| 234 | deployment_configuration = 235 | configuration 236 | .for_scope(role: :prerequisites) 237 | .for_overrides(args.to_h) 238 | 239 | t.source_directory = 'spec/unit/infra/prerequisites' 240 | t.work_directory = 'build/infra' 241 | 242 | t.state_file = deployment_configuration.state_file 243 | t.vars = deployment_configuration.vars 244 | end 245 | end 246 | 247 | namespace :root do 248 | RakeTerraform.define_command_tasks( 249 | configuration_name: 'root', 250 | argument_names: [:seed] 251 | ) do |t, args| 252 | deployment_configuration = 253 | configuration 254 | .for_scope(role: :root) 255 | .for_overrides(args.to_h) 256 | 257 | t.source_directory = 'spec/unit/infra/root' 258 | t.work_directory = 'build/infra' 259 | 260 | t.state_file = deployment_configuration.state_file 261 | t.vars = deployment_configuration.vars 262 | end 263 | end 264 | end 265 | 266 | namespace :version do 267 | desc 'Bump version for specified type (pre, major, minor, patch)' 268 | task :bump, [:type] do |_, args| 269 | next_tag = latest_tag.send("#{args.type}!") 270 | repo.add_tag(next_tag.to_s) 271 | repo.push('origin', 'main', tags: true) 272 | puts "Bumped version to #{next_tag}." 273 | end 274 | 275 | desc 'Release module' 276 | task :release do 277 | next_tag = latest_tag.release! 278 | repo.add_tag(next_tag.to_s) 279 | repo.push('origin', 'main', tags: true) 280 | puts "Released version #{next_tag}." 281 | end 282 | end 283 | -------------------------------------------------------------------------------- /config/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | region: 'eu-west-2' 3 | 4 | component: 'bastion' 5 | deployment_identifier: "%{hiera('seed')}" 6 | 7 | domain_name: 'infrablocks.click' 8 | 9 | allowed_cidrs: 10 | - '0.0.0.0/0' 11 | egress_cidrs: 12 | - '10.0.0.0/8' 13 | - '192.168.0.0/16' 14 | 15 | ami: 'ami-bb373ddf' 16 | instance_user: 'centos' 17 | instance_type: 't2.medium' 18 | ssh_public_key_path: "%{cwd}/config/secrets/bastion/ssh.public" 19 | ssh_private_key_path: "%{cwd}/config/secrets/bastion/ssh.private" 20 | 21 | minimum_instances: 1 22 | maximum_instances: 1 23 | desired_instances: 1 24 | 25 | vpc_cidr: '10.1.0.0/16' 26 | availability_zones: 27 | - "eu-west-2a" 28 | - "eu-west-2b" 29 | 30 | public_zone_id: 'Z08829023L5IPIQBJOX63' 31 | private_zone_id: 'Z0211739BXR21FNN139O' 32 | 33 | listeners: 34 | - lb_port: 22 35 | lb_protocol: "TCP" 36 | instance_port: 22 37 | instance_protocol: "TCP" 38 | ssl_certificate_id: "" 39 | 40 | access_control: 41 | - lb_port: 22 42 | instance_port: 22 43 | allow_cidrs: 44 | - "0.0.0.0/0" 45 | 46 | health_check_target: 'TCP:22' 47 | 48 | include_public_dns_record: "yes" 49 | include_private_dns_record: "yes" 50 | 51 | expose_to_public_internet: "yes" 52 | -------------------------------------------------------------------------------- /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----- -------------------------------------------------------------------------------- /config/gpg/liam.gpg.public: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQGNBGH39r4BDADfCcY6rEDhN1uxj4kgr/xTclNJM0kzDJIv7veMmKylNlw+ePhj 4 | dl9IXIFxd/Sc8NPx/xOCYIbALd8SvDVW9Qfe0Mqq41HbWmAGgoWKN6BfY4UBqKgM 5 | 2lIGAUzdkwsF+ubXMwLooLSDL38homebBG+I/NG1uu1rO9B0c8G/cHz4jBuqYJXj 6 | FrBwDgcoBrJEI2oNpN68qezdzGvB3AJO9oE4rrhIkbqkQouqqxvvZbuqb+FHRxIH 7 | LiAV6zqnZ/3sDq0L90V36auH7rsz4cYxOWl4S6jFzNty5UviIvqJ/zz+g7ouaC+N 8 | qpG3g2Qjk6h7A9fy+wwO0G04oKkYMT3Qi8moqVrlm89atVenvFVI6tNVSnzh0vFG 9 | ycvE5UUXqjw1b+Bwq2GINHmUGBz1u0aw2TbPaETzPJQbMLWxrPPz9ck99+UqXj57 10 | ZY4xk5gfP/FcAZIOeTxp1l5FQzxRmFIM523MnKdkbaJ+Qhm44chnRGRatz9PiJnv 11 | 5eMESRNpOY2lC1EAEQEAAbQnTGlhbSBHcmlmZmluLUpvd2V0dCA8bGlhbUBnby1h 12 | dG9taWMuaW8+iQHYBBMBCABCFiEEkz45lGhtwVyZ0TaYRANzma7bHY0FAmH39r4C 13 | GwMFCQPCZwAFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEEQDc5mu2x2N 14 | 9QkL/i2UALrzcFZazgBOx5Pkng6BLt2pKKv9zE58ZNu2u8hq8QaNHUA7Gwf7qZlL 15 | A5arZ4K8KJ2zuR9BBzQIbjdVbwqsSpOz0wKXzF6Jt4Cas2gxdBob4m2hv9W7E1wz 16 | hFUjZk4GixCp8who4/FUGrBun+COo/b1UkJ/E9DisgIiXyM78AO6HhhVOb/2M+cf 17 | 1vq6OjuN3Cr5p0LnvrEsOsz2sdB6qBarrN3ragd6M2+T7HmkUPbZDf5UIxVzUeWm 18 | I0YXhECSqahNhlyIR24e6CsEAJkILxijU3mvj09DO7gHYKXZsBiAX9Peo+D5qywc 19 | q4TCV9oZuDB1Rbuzmrb/jTCrQMBaYt0kosMo0OTWSGrUFg+Ymwl/cnoZU89ZPYp6 20 | ZvctRHaw3f3eP1AJ1WG+oE1X7e/g4q7Or8z5O8EYoBHVXTjwGi621WTodr5a/gPY 21 | oGYRr68afTsq13KuiMC3lCWfHPFTmo5/Os66fvZHIuxles0Oxy3iHQseZKlCdXhd 22 | ylJu6rkBjQRh9/a+AQwAnFqV8e7RTz47mIK4r+WLh82XnAbMuefyOBajavWOSD1J 23 | YrO29qL/UKD5Td7I9iqCHLwfHr0HNSXZ8MXRW71teOtEwwtNRtntSCkATdBsvgUW 24 | xwwpvB+OpKBR3wv5Q8/7fmDN8bAz1TBaYLxu+Q2q7ziHC6cB+lBfgdPE3b/y0LfK 25 | Ia26+jy+gqA4l/Xlio+WU2SKSVy+usJrNg58rStgND/E8btm+uCXq2VBhhZ+dLqi 26 | Oo3IblTvDcG6BRN/NN9g3YJaOaSdjxNeZp3V6xwtnOEdZ85pEdBsvkRgnpZ0eyHp 27 | yYm1xE2H5Idwtugs4wVC9u33p8P9QgDJ7hS5B+5kowjwiHuSquo1cXrj8a4NL8L+ 28 | duJrRb9lP/fXjjcGk4uaDhIjKTWoGWTAra9/tRdLZpjBh9Y+d70cdQfrzeVGH7ac 29 | VTyvjL9+h17dbP6pYMf42zeB0n667WRyG97L1nD48rUqWf2l38m8S639Q5x9t1L+ 30 | kvOUEISYXtMuv/I15qk3ABEBAAGJAbwEGAEIACYWIQSTPjmUaG3BXJnRNphEA3OZ 31 | rtsdjQUCYff2vgIbDAUJA8JnAAAKCRBEA3OZrtsdjdAPC/sH1zQIUm60bn0N3gHt 32 | E5dW2BBAULA6vxnkWTVNWBYuZm5AHR1wPEB8GvheWIiU0ASIuGbC/vbn0mB23FVz 33 | oxGQhOB4b2QNHVhaHtmr+0m+FQborpoeamnblc1SNtMNLN4a2dgAxhCHA97NMWKr 34 | NrUuyN+qz//Xh44jPyRo04bEkYbCmTk/fca0tb/WCBQHLq8JJy1ZfgWZfj3iC092 35 | wT5MpDL2NvItXBX8mDXZWf7NG+o1yUEm1mA6ZE3ZZPgwP0Uy8myhTLz1EN1aVoqG 36 | 7lUH2fuw0dmSEwPytl9IPqZIYRzzSRWcAiho0BazfKdpubh9hY4AEtPVFR54I9Z9 37 | JiezgHCilIo7haAFTi0rgXMfoQIIXlFRPL8a4QfWvPBmRd/Vm0DKSUOaF7wJtflC 38 | AdYBEuibOElrD9+e3wwBC2qr1zSNmvoGA5o7T3Eq31oZkZmfzm4a4z0558Y7UMZ3 39 | 3rbVfQYlC4lv1cLV+bFV3jEGIOW3H1ueTUv/ClTwB+xCZv8= 40 | =0Zai 41 | -----END PGP PUBLIC KEY BLOCK----- 42 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 7 | vpc_cidr: "%{hiera('vpc_cidr')}" 8 | availability_zones: "%{hiera('availability_zones')}" 9 | 10 | component: "%{hiera('component')}" 11 | deployment_identifier: "%{hiera('deployment_identifier')}" 12 | 13 | domain_name: "%{hiera('domain_name')}" 14 | public_zone_id: "%{hiera('public_zone_id')}" 15 | private_zone_id: "%{hiera('private_zone_id')}" 16 | 17 | listeners: "%{hiera('listeners')}" 18 | 19 | access_control: "%{hiera('access_control')}" 20 | 21 | health_check_target: "%{hiera('health_check_target')}" 22 | 23 | include_public_dns_record: "%{hiera('include_public_dns_record')}" 24 | include_private_dns_record: "%{hiera('include_private_dns_record')}" 25 | 26 | expose_to_public_internet: "%{hiera('expose_to_public_internet')}" 27 | 28 | ssh_public_key_path: "%{hiera('ssh_public_key_path')}" 29 | 30 | allowed_cidrs: "%{hiera('allowed_cidrs')}" 31 | egress_cidrs: "%{hiera('egress_cidrs')}" 32 | -------------------------------------------------------------------------------- /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 | vpc_cidr: "%{hiera('vpc_cidr')}" 8 | availability_zones: "%{hiera('availability_zones')}" 9 | 10 | component: "%{hiera('component')}" 11 | deployment_identifier: "%{hiera('deployment_identifier')}" 12 | 13 | domain_name: "%{hiera('domain_name')}" 14 | public_zone_id: "%{hiera('public_zone_id')}" 15 | private_zone_id: "%{hiera('private_zone_id')}" 16 | 17 | listeners: "%{hiera('listeners')}" 18 | 19 | access_control: "%{hiera('access_control')}" 20 | 21 | health_check_target: "%{hiera('health_check_target')}" 22 | 23 | include_public_dns_record: "%{hiera('include_public_dns_record')}" 24 | include_private_dns_record: "%{hiera('include_private_dns_record')}" 25 | 26 | expose_to_public_internet: "%{hiera('expose_to_public_internet')}" 27 | 28 | egress_cidrs: "%{hiera('egress_cidrs')}" 29 | -------------------------------------------------------------------------------- /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 | 7 | component: "%{hiera('component')}" 8 | deployment_identifier: "%{hiera('deployment_identifier')}" 9 | 10 | ssh_public_key_path: "%{hiera('ssh_public_key_path')}" 11 | 12 | allowed_cidrs: "%{hiera('allowed_cidrs')}" 13 | egress_cidrs: "%{hiera('egress_cidrs')}" 14 | -------------------------------------------------------------------------------- /config/secrets/.unlocked: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/.unlocked -------------------------------------------------------------------------------- /config/secrets/bastion/ssh.private: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/bastion/ssh.private -------------------------------------------------------------------------------- /config/secrets/bastion/ssh.public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/bastion/ssh.public -------------------------------------------------------------------------------- /config/secrets/ci/aws-credentials.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/ci/aws-credentials.sh -------------------------------------------------------------------------------- /config/secrets/ci/encryption.passphrase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/ci/encryption.passphrase -------------------------------------------------------------------------------- /config/secrets/ci/gpg.private: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/ci/gpg.private -------------------------------------------------------------------------------- /config/secrets/ci/gpg.public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/ci/gpg.public -------------------------------------------------------------------------------- /config/secrets/ci/ssh.private: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/ci/ssh.private -------------------------------------------------------------------------------- /config/secrets/ci/ssh.public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/ci/ssh.public -------------------------------------------------------------------------------- /config/secrets/circle_ci/config.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/circle_ci/config.yaml -------------------------------------------------------------------------------- /config/secrets/github/config.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/config/secrets/github/config.yaml -------------------------------------------------------------------------------- /defaults.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | # default for cases when `null` value provided, meaning "use default" 3 | instance_type = var.instance_type == null ? "t4g.nano" : var.instance_type 4 | minimum_instances = var.minimum_instances == null ? 1 : var.minimum_instances 5 | maximum_instances = var.maximum_instances == null ? 1 : var.maximum_instances 6 | desired_instances = var.desired_instances == null ? 1 : var.desired_instances 7 | associate_public_ip_address = var.associate_public_ip_address == null ? false : var.associate_public_ip_address 8 | } 9 | -------------------------------------------------------------------------------- /docs/architecture.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/docs/architecture.graffle/data.plist -------------------------------------------------------------------------------- /docs/architecture.graffle/image1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/docs/architecture.graffle/image1.pdf -------------------------------------------------------------------------------- /docs/architecture.graffle/image18.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/docs/architecture.graffle/image18.pdf -------------------------------------------------------------------------------- /docs/architecture.graffle/image21.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/docs/architecture.graffle/image21.pdf -------------------------------------------------------------------------------- /docs/architecture.graffle/image24.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/docs/architecture.graffle/image24.pdf -------------------------------------------------------------------------------- /docs/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infrablocks/terraform-aws-bastion/59b53c3afc920a7e6ed8f724f59208554502894f/docs/architecture.png -------------------------------------------------------------------------------- /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.39.0, >= 4.0.0, 4.33.0" 7 | hashes = [ 8 | "h1:0S9ZXYg6K0CTOJUTQnoH94YrKuOYyJYEcc+hN5qGafA=", 9 | "zh:421b24e21d7fac4d65d97438d2c0a4effe71d3a1bd15820d6fde2879e49fe817", 10 | "zh:4378a84ca8e2a6990f47abc24367b801e884be928671b37ad7b8e7b656f73e48", 11 | "zh:54e0d7884edf3cefd096715794d32b6532138dca905f0b2fe84fb2117594293c", 12 | "zh:6269a7d0312057db5ded669e9f7f9bd80fb6dcb549b50d8d7f3f3b2a0361b8a5", 13 | "zh:67f57d16aa3db493a3174c3c5f30385c7af9767c4e3cdca14e5a4bf384ff59d9", 14 | "zh:7d4d4a1d963e431ffdc3348e3a578d3ba0fa782b1f4bf55fd5c0e527d24fed81", 15 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 16 | "zh:cd8e3d32485acb49c1b06f63916fec8e73a4caa6cf88ae9c4bf236d6f5d9b914", 17 | "zh:d586fd01195bd3775346495e61806e79b6012e745dc05e31a30b958acf968abe", 18 | "zh:d76122060f25ab87887a743096a42d47ba091c2c019ac13ce6b3973b2babe5a3", 19 | "zh:e917d36fe18eddc42ec743b3152b4dcb4853b75ea7a679abd19bdf271bc48221", 20 | "zh:eb780860d5c04f43a018aef564e76a2d84e9aa68984fa1f968ca8c09d23a611a", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /examples/full/bastion.tf: -------------------------------------------------------------------------------- 1 | module "bastion" { 2 | source = "../../" 3 | 4 | vpc_id = module.base_network.vpc_id 5 | subnet_ids = module.base_network.private_subnet_ids 6 | 7 | component = var.component 8 | deployment_identifier = var.deployment_identifier 9 | 10 | ssh_public_key_path = var.ssh_public_key_path 11 | 12 | allowed_cidrs = var.allowed_cidrs 13 | egress_cidrs = var.egress_cidrs 14 | 15 | load_balancer_names = [ 16 | module.classic_load_balancer.name 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /examples/full/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 | 45 | output "load_balancer_name" { 46 | value = module.classic_load_balancer.name 47 | } 48 | 49 | output "launch_configuration_name" { 50 | value = module.bastion.launch_configuration_name 51 | } 52 | 53 | output "allow_ssh_to_bastion_security_group_id" { 54 | value = module.bastion.allow_ssh_to_bastion_security_group_id 55 | } 56 | 57 | output "allow_ssh_from_bastion_security_group_id" { 58 | value = module.bastion.allow_ssh_from_bastion_security_group_id 59 | } 60 | -------------------------------------------------------------------------------- /examples/full/prerequisites.tf: -------------------------------------------------------------------------------- 1 | module "base_network" { 2 | source = "infrablocks/base-networking/aws" 3 | version = "5.0.0" 4 | 5 | region = var.region 6 | vpc_cidr = var.vpc_cidr 7 | availability_zones = var.availability_zones 8 | 9 | component = "${var.component}-net" 10 | deployment_identifier = var.deployment_identifier 11 | 12 | private_zone_id = var.private_zone_id 13 | 14 | include_nat_gateways = "no" 15 | } 16 | 17 | module "classic_load_balancer" { 18 | source = "infrablocks/classic-load-balancer/aws" 19 | version = "2.0.0" 20 | 21 | vpc_id = module.base_network.vpc_id 22 | subnet_ids = module.base_network.public_subnet_ids 23 | 24 | domain_name = var.domain_name 25 | public_zone_id = var.public_zone_id 26 | private_zone_id = var.private_zone_id 27 | 28 | component = var.component 29 | deployment_identifier = var.deployment_identifier 30 | 31 | listeners = var.listeners 32 | access_control = var.access_control 33 | 34 | egress_cidrs = var.egress_cidrs 35 | 36 | health_check_target = var.health_check_target 37 | 38 | include_public_dns_record = var.include_public_dns_record 39 | include_private_dns_record = var.include_private_dns_record 40 | 41 | expose_to_public_internet = var.expose_to_public_internet 42 | } 43 | -------------------------------------------------------------------------------- /examples/full/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/full/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" {} 2 | variable "vpc_cidr" {} 3 | variable "availability_zones" { 4 | type = list(string) 5 | } 6 | 7 | variable "component" {} 8 | variable "deployment_identifier" {} 9 | 10 | variable "domain_name" {} 11 | variable "public_zone_id" {} 12 | variable "private_zone_id" {} 13 | 14 | variable "listeners" { 15 | type = list(object({ 16 | lb_port: number, 17 | lb_protocol: string, 18 | instance_port: number, 19 | instance_protocol: string 20 | ssl_certificate_id: string 21 | })) 22 | } 23 | variable "access_control" { 24 | type = list(object({ 25 | lb_port: number, 26 | instance_port: number 27 | allow_cidrs: list(string) 28 | })) 29 | } 30 | 31 | variable "health_check_target" {} 32 | 33 | variable "include_public_dns_record" {} 34 | variable "include_private_dns_record" {} 35 | 36 | variable "expose_to_public_internet" {} 37 | 38 | variable "ssh_public_key_path" {} 39 | 40 | variable "allowed_cidrs" { 41 | type = list(string) 42 | } 43 | variable "egress_cidrs" { 44 | type = list(string) 45 | } 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | data "aws_availability_zones" "all" {} 2 | 3 | data "aws_ami" "amazon_linux_2" { 4 | most_recent = true 5 | owners = ["amazon"] 6 | 7 | filter { 8 | name = "name" 9 | values = ["amzn2-ami-ecs-hvm-*-arm64-ebs"] 10 | } 11 | } 12 | 13 | resource "aws_key_pair" "bastion" { 14 | key_name = "bastion-${var.component}-${var.deployment_identifier}" 15 | public_key = file(var.ssh_public_key_path) 16 | } 17 | 18 | resource "aws_launch_configuration" "bastion" { 19 | name_prefix = "${var.component}-${var.deployment_identifier}" 20 | image_id = coalesce(var.ami, data.aws_ami.amazon_linux_2.id) 21 | instance_type = local.instance_type 22 | key_name = aws_key_pair.bastion.key_name 23 | associate_public_ip_address = local.associate_public_ip_address 24 | 25 | security_groups = [ 26 | aws_security_group.allow_ssh_to_bastion.id 27 | ] 28 | 29 | lifecycle { 30 | create_before_destroy = true 31 | } 32 | } 33 | 34 | resource "aws_autoscaling_group" "bastion" { 35 | name = "${var.component}-${var.deployment_identifier}" 36 | 37 | vpc_zone_identifier = coalescelist(var.subnet_ids, data.aws_availability_zones.all.names) 38 | 39 | launch_configuration = aws_launch_configuration.bastion.name 40 | 41 | load_balancers = var.load_balancer_names 42 | 43 | min_size = local.minimum_instances 44 | max_size = local.maximum_instances 45 | desired_capacity = local.desired_instances 46 | 47 | tag { 48 | key = "Name" 49 | value = "bastion-${var.component}-${var.deployment_identifier}" 50 | propagate_at_launch = true 51 | } 52 | 53 | tag { 54 | key = "Component" 55 | value = var.component 56 | propagate_at_launch = true 57 | } 58 | 59 | tag { 60 | key = "DeploymentIdentifier" 61 | value = var.deployment_identifier 62 | propagate_at_launch = true 63 | } 64 | 65 | tag { 66 | key = "Role" 67 | value = "bastion" 68 | propagate_at_launch = true 69 | } 70 | } 71 | 72 | resource "aws_security_group" "allow_ssh_to_bastion" { 73 | name = "allow-ssh-to-bastion-${var.component}-${var.deployment_identifier}" 74 | vpc_id = var.vpc_id 75 | 76 | tags = { 77 | Name = "allow-ssh-to-bastion-${var.component}-${var.deployment_identifier}" 78 | Component = var.component 79 | DeploymentIdentifier = var.deployment_identifier 80 | Role = "bastion" 81 | } 82 | 83 | ingress { 84 | from_port = 22 85 | to_port = 22 86 | protocol = "tcp" 87 | cidr_blocks = var.allowed_cidrs 88 | } 89 | 90 | egress { 91 | from_port = 22 92 | to_port = 22 93 | protocol = "tcp" 94 | cidr_blocks = var.egress_cidrs 95 | } 96 | } 97 | 98 | resource "aws_security_group" "allow_ssh_from_bastion" { 99 | name = "allow-ssh-from-bastion-${var.component}-${var.deployment_identifier}" 100 | vpc_id = var.vpc_id 101 | 102 | tags = { 103 | Name = "allow-ssh-from-bastion-${var.component}-${var.deployment_identifier}" 104 | Component = var.component 105 | DeploymentIdentifier = var.deployment_identifier 106 | Role = "bastion" 107 | } 108 | 109 | ingress { 110 | from_port = 22 111 | to_port = 22 112 | protocol = "tcp" 113 | security_groups = [ 114 | aws_security_group.allow_ssh_to_bastion.id 115 | ] 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "launch_configuration_name" { 2 | description = "The name of the launch configuration for bastion instances." 3 | value = aws_launch_configuration.bastion.name 4 | } 5 | 6 | output "allow_ssh_to_bastion_security_group_id" { 7 | description = "The ID of the security group that allows ssh access to the bastion." 8 | value = aws_security_group.allow_ssh_to_bastion.id 9 | } 10 | 11 | output "allow_ssh_from_bastion_security_group_id" { 12 | description = "The ID of the security group that allows ssh access from the bastion." 13 | value = aws_security_group.allow_ssh_from_bastion.id 14 | } 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/integration/full_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'socket' 5 | require 'net/ssh' 6 | 7 | describe 'full example' do 8 | let(:component) do 9 | var(role: :full, name: 'component') 10 | end 11 | let(:deployment_identifier) do 12 | var(role: :full, name: 'deployment_identifier') 13 | end 14 | let(:vpc_id) do 15 | output(role: :full, name: 'vpc_id') 16 | end 17 | 18 | before(:context) do 19 | apply(role: :full) 20 | end 21 | 22 | after(:context) do 23 | destroy( 24 | role: :full, 25 | only_if: -> { !ENV['FORCE_DESTROY'].nil? || ENV['SEED'].nil? } 26 | ) 27 | end 28 | 29 | describe 'launch configuration' do 30 | subject(:created_launch_configuration) do 31 | launch_configuration( 32 | output(role: :full, name: 'launch_configuration_name') 33 | ) 34 | end 35 | 36 | let(:latest_ami) do 37 | ec2_client 38 | .describe_images( 39 | owners: ['amazon'], 40 | filters: [{ name: 'name', values: ['amzn2-ami-ecs-hvm-*-arm64-ebs'] }] 41 | ) 42 | .images 43 | .max_by(&:creation_date) 44 | end 45 | 46 | it { is_expected.to exist } 47 | its(:instance_type) { is_expected.to eq('t4g.nano') } 48 | its(:image_id) { is_expected.to eq(latest_ami.image_id) } 49 | 50 | its(:key_name) do 51 | is_expected.to eq("bastion-#{component}-#{deployment_identifier}") 52 | end 53 | 54 | it { 55 | expect(created_launch_configuration) 56 | .to(have_security_group( 57 | "allow-ssh-to-bastion-#{component}-#{deployment_identifier}" 58 | )) 59 | } 60 | 61 | # rubocop:disable RSpec/MultipleExpectations 62 | it 'has a name containing the component and deployment_identifier' do 63 | launch_configuration_name = 64 | output(role: :full, name: 'launch_configuration_name') 65 | 66 | expect(launch_configuration_name) 67 | .to(match(/#{component}/)) 68 | expect(launch_configuration_name) 69 | .to(match(/#{deployment_identifier}/)) 70 | end 71 | # rubocop:enable RSpec/MultipleExpectations 72 | end 73 | 74 | describe 'autoscaling group' do 75 | subject(:created_asg) do 76 | autoscaling_group("#{component}-#{deployment_identifier}") 77 | end 78 | 79 | let(:load_balancer_name) do 80 | output(role: :full, name: 'load_balancer_name') 81 | end 82 | let(:private_subnet_ids) do 83 | output(role: :full, name: 'private_subnet_ids') 84 | end 85 | let(:launch_configuration_name) do 86 | output(role: :full, name: 'launch_configuration_name') 87 | end 88 | 89 | it { is_expected.to exist } 90 | 91 | its(:min_size) { is_expected.to eq(1.to_i) } 92 | its(:max_size) { is_expected.to eq(1.to_i) } 93 | its(:desired_capacity) { is_expected.to eq(1.to_i) } 94 | 95 | its(:launch_configuration_name) do 96 | is_expected.to eq(launch_configuration_name) 97 | end 98 | 99 | it 'uses the provided subnets' do 100 | expect(created_asg.vpc_zone_identifier.split(',')) 101 | .to(match_array(private_subnet_ids)) 102 | end 103 | 104 | it 'uses the provided load balancer names' do 105 | expect(created_asg.load_balancer_names) 106 | .to(contain_exactly(load_balancer_name)) 107 | end 108 | 109 | it { 110 | expect(created_asg).to have_tag('Name') 111 | .value("bastion-#{component}-#{deployment_identifier}") 112 | } 113 | 114 | it { 115 | expect(created_asg).to have_tag('Component') 116 | .value(component) 117 | } 118 | 119 | it { 120 | expect(created_asg).to have_tag('DeploymentIdentifier') 121 | .value(deployment_identifier) 122 | } 123 | 124 | it { 125 | expect(created_asg).to have_tag('Role') 126 | .value('bastion') 127 | } 128 | end 129 | 130 | describe 'allow-ssh-to-bastion security group' do 131 | subject(:inbound_security_group) do 132 | security_group(allow_ssh_to_bastion_security_group_id) 133 | end 134 | 135 | let(:allow_ssh_to_bastion_security_group_id) do 136 | output(role: :full, name: 'allow_ssh_to_bastion_security_group_id') 137 | end 138 | let(:allowed_cidrs) do 139 | var(role: :full, name: 'allowed_cidrs') 140 | end 141 | let(:egress_cidrs) do 142 | var(role: :full, name: 'egress_cidrs') 143 | end 144 | let(:security_group_name) do 145 | "allow-ssh-to-bastion-#{component}-#{deployment_identifier}" 146 | end 147 | 148 | it { is_expected.to exist } 149 | 150 | it { is_expected.to have_tag('Name').value(security_group_name) } 151 | it { is_expected.to have_tag('Component').value(component) } 152 | 153 | it { 154 | expect(inbound_security_group) 155 | .to(have_tag('DeploymentIdentifier').value(deployment_identifier)) 156 | } 157 | 158 | its(:vpc_id) { is_expected.to eq(vpc_id) } 159 | 160 | # rubocop:disable RSpec/MultipleExpectations 161 | it 'allows inbound SSH for each supplied CIDR' do 162 | allowed_cidrs.each do |cidr| 163 | ingress_rule = inbound_security_group 164 | .ip_permissions 165 | .find do |perm| 166 | perm.ip_ranges.map(&:cidr_ip).include?(cidr) 167 | end 168 | 169 | expect(ingress_rule.from_port).to(eq(22)) 170 | expect(ingress_rule.to_port).to(eq(22)) 171 | expect(ingress_rule.ip_protocol).to(eq('tcp')) 172 | end 173 | 174 | expect(inbound_security_group.inbound_rule_count) 175 | .to(eq(allowed_cidrs.count)) 176 | end 177 | # rubocop:enable RSpec/MultipleExpectations 178 | 179 | # rubocop:disable RSpec/MultipleExpectations 180 | it 'allows outbound SSH for each supplied CIDR' do 181 | egress_cidrs.each do |cidr| 182 | egress_rule = inbound_security_group 183 | .ip_permissions_egress 184 | .find do |perm| 185 | perm.ip_ranges.map(&:cidr_ip).include?(cidr) 186 | end 187 | 188 | expect(egress_rule.from_port).to(eq(22)) 189 | expect(egress_rule.to_port).to(eq(22)) 190 | expect(egress_rule.ip_protocol).to(eq('tcp')) 191 | end 192 | 193 | expect(inbound_security_group.outbound_rule_count) 194 | .to(eq(egress_cidrs.count)) 195 | end 196 | # rubocop:enable RSpec/MultipleExpectations 197 | end 198 | 199 | describe 'allow-ssh-from-bastion security group' do 200 | subject(:outbound_security_group) do 201 | security_group(allow_ssh_from_bastion_security_group_id) 202 | end 203 | 204 | let(:allow_ssh_from_bastion_security_group_id) do 205 | output(role: :full, name: 'allow_ssh_from_bastion_security_group_id') 206 | end 207 | let(:bastion_security_group) do 208 | security_group(allow_ssh_to_bastion_security_group_id) 209 | end 210 | let(:allow_ssh_to_bastion_security_group_id) do 211 | output(role: :full, name: 'allow_ssh_to_bastion_security_group_id') 212 | end 213 | let(:security_group_name) do 214 | "allow-ssh-from-bastion-#{component}-#{deployment_identifier}" 215 | end 216 | 217 | it { is_expected.to exist } 218 | 219 | it { is_expected.to have_tag('Name').value(security_group_name) } 220 | it { is_expected.to have_tag('Component').value(component) } 221 | 222 | it { 223 | expect(outbound_security_group) 224 | .to(have_tag('DeploymentIdentifier').value(deployment_identifier)) 225 | } 226 | 227 | its(:vpc_id) { is_expected.to eq(vpc_id) } 228 | 229 | # rubocop:disable RSpec/MultipleExpectations 230 | it 'allows inbound SSH from the bastion' do 231 | permission = outbound_security_group 232 | .ip_permissions 233 | .find do |perm| 234 | perm.user_id_group_pairs.find do |pair| 235 | pair.group_id == bastion_security_group.id 236 | end 237 | end 238 | 239 | expect(permission).not_to(be_nil) 240 | expect(permission.from_port).to(eq(22)) 241 | expect(permission.to_port).to(eq(22)) 242 | expect(permission.ip_protocol).to(eq('tcp')) 243 | end 244 | # rubocop:enable RSpec/MultipleExpectations 245 | end 246 | 247 | describe 'connectivity' do 248 | it 'is reachable using the corresponding private SSH key' do 249 | attempts = 10 250 | interval = 30 251 | succeeded = false 252 | exception = nil 253 | 254 | expect do 255 | while !succeeded && attempts > 0 256 | begin 257 | user = 'ec2-user' 258 | ssh_private_key_path = 'config/secrets/bastion/ssh.private' 259 | domain_name = var(role: :full, name: 'domain_name') 260 | address = "#{component}-#{deployment_identifier}.#{domain_name}" 261 | ssh = Net::SSH.start( 262 | address, 263 | user, 264 | { 265 | keys: ssh_private_key_path, 266 | verbose: :info, 267 | paranoid: false 268 | } 269 | ) 270 | ssh.exec!('ls -al') 271 | ssh.close 272 | succeeded = true 273 | rescue StandardError => e 274 | attempts -= 1 275 | exception = e 276 | sleep interval 277 | end 278 | end 279 | 280 | raise exception unless succeeded 281 | end.not_to raise_error 282 | end 283 | end 284 | end 285 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/unit/autoscaling_group_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec::Matchers.define :a_propagated_tag do |key, value| 6 | match do |tag| 7 | tag[:key] == key && 8 | tag[:value] == value && 9 | tag[:propagate_at_launch] == true 10 | end 11 | end 12 | 13 | describe 'autoscaling group' do 14 | let(:component) do 15 | var(role: :root, name: 'component') 16 | end 17 | let(:deployment_identifier) do 18 | var(role: :root, name: 'deployment_identifier') 19 | end 20 | let(:load_balancer_name) do 21 | output(role: :prerequisites, name: 'load_balancer_name') 22 | end 23 | 24 | describe 'by default' do 25 | before(:context) do 26 | @plan = plan(role: :root) 27 | end 28 | 29 | it 'creates an autoscaling group' do 30 | expect(@plan) 31 | .to(include_resource_creation(type: 'aws_autoscaling_group') 32 | .once) 33 | end 34 | 35 | it 'includes the component in the name' do 36 | expect(@plan) 37 | .to(include_resource_creation(type: 'aws_autoscaling_group') 38 | .with_attribute_value(:name, match(/.*#{component}.*/))) 39 | end 40 | 41 | it 'includes the deployment identifier in the name' do 42 | expect(@plan) 43 | .to(include_resource_creation(type: 'aws_autoscaling_group') 44 | .with_attribute_value( 45 | :name, match(/.*#{deployment_identifier}.*/) 46 | )) 47 | end 48 | 49 | it 'uses the provided load balancer names' do 50 | expect(@plan) 51 | .to(include_resource_creation(type: 'aws_autoscaling_group') 52 | .with_attribute_value(:load_balancers, [load_balancer_name])) 53 | end 54 | 55 | it 'uses a minimum size of 1' do 56 | expect(@plan) 57 | .to(include_resource_creation(type: 'aws_autoscaling_group') 58 | .with_attribute_value(:min_size, 1)) 59 | end 60 | 61 | it 'uses a maximum size of 1' do 62 | expect(@plan) 63 | .to(include_resource_creation(type: 'aws_autoscaling_group') 64 | .with_attribute_value(:max_size, 1)) 65 | end 66 | 67 | it 'uses a desired capacity of 1' do 68 | expect(@plan) 69 | .to(include_resource_creation(type: 'aws_autoscaling_group') 70 | .with_attribute_value(:desired_capacity, 1)) 71 | end 72 | 73 | it 'adds a name tag propagated at launch' do 74 | name = "bastion-#{component}-#{deployment_identifier}" 75 | expect(@plan) 76 | .to(include_resource_creation(type: 'aws_autoscaling_group') 77 | .with_attribute_value( 78 | :tag, including(a_propagated_tag('Name', name)) 79 | )) 80 | end 81 | 82 | it 'adds a component tag propagated at launch' do 83 | expect(@plan) 84 | .to(include_resource_creation(type: 'aws_autoscaling_group') 85 | .with_attribute_value( 86 | :tag, including(a_propagated_tag('Component', component)) 87 | )) 88 | end 89 | 90 | it 'adds a deployment identifier tag propagated at launch' do 91 | expect(@plan) 92 | .to(include_resource_creation(type: 'aws_autoscaling_group') 93 | .with_attribute_value( 94 | :tag, 95 | including(a_propagated_tag( 96 | 'DeploymentIdentifier', deployment_identifier 97 | )) 98 | )) 99 | end 100 | 101 | it 'adds a role tag of bastion propagated at launch' do 102 | expect(@plan) 103 | .to(include_resource_creation(type: 'aws_autoscaling_group') 104 | .with_attribute_value( 105 | :tag, including(a_propagated_tag('Role', 'bastion')) 106 | )) 107 | end 108 | end 109 | 110 | describe 'when subnet IDs provided' do 111 | let(:private_subnet_ids) do 112 | output(role: :prerequisites, name: 'private_subnet_ids') 113 | end 114 | 115 | before(:context) do 116 | @plan = plan(role: :root) do |vars| 117 | vars.subnet_ids = 118 | output(role: :prerequisites, name: 'private_subnet_ids') 119 | end 120 | end 121 | 122 | it 'uses the provided subnet IDs as the VPC zone identifier' do 123 | expect(@plan) 124 | .to(include_resource_creation(type: 'aws_autoscaling_group') 125 | .with_attribute_value( 126 | :vpc_zone_identifier, containing_exactly(*private_subnet_ids) 127 | )) 128 | end 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /spec/unit/from_security_group_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'SSH from bastion security group' 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_id) do 13 | output(role: :prerequisites, name: 'vpc_id') 14 | end 15 | 16 | describe 'by default' do 17 | before(:context) do 18 | @plan = plan(role: :root) 19 | end 20 | 21 | it 'create a security group' do 22 | expect(@plan) 23 | .to(include_resource_creation( 24 | type: 'aws_security_group', 25 | name: 'allow_ssh_from_bastion' 26 | ).once) 27 | end 28 | 29 | it 'includes the component and deployment identifier in the name' do 30 | name = "allow-ssh-from-bastion-#{component}-#{deployment_identifier}" 31 | expect(@plan) 32 | .to(include_resource_creation( 33 | type: 'aws_security_group', 34 | name: 'allow_ssh_from_bastion' 35 | ) 36 | .with_attribute_value(:name, name)) 37 | end 38 | 39 | it 'uses the provided VPC ID' do 40 | expect(@plan) 41 | .to(include_resource_creation( 42 | type: 'aws_security_group', 43 | name: 'allow_ssh_from_bastion' 44 | ) 45 | .with_attribute_value(:vpc_id, vpc_id)) 46 | end 47 | 48 | it 'allows inbound SSH connections' do 49 | expect(@plan) 50 | .to(include_resource_creation( 51 | type: 'aws_security_group', 52 | name: 'allow_ssh_from_bastion' 53 | ) 54 | .with_attribute_value( 55 | [:ingress, 0], 56 | a_hash_including( 57 | from_port: 22, 58 | to_port: 22, 59 | protocol: 'tcp' 60 | ) 61 | )) 62 | end 63 | 64 | it 'adds component and deployment identifier tags' do 65 | expect(@plan) 66 | .to(include_resource_creation( 67 | type: 'aws_security_group', 68 | name: 'allow_ssh_from_bastion' 69 | ) 70 | .with_attribute_value( 71 | :tags, 72 | a_hash_including( 73 | Component: component, 74 | DeploymentIdentifier: deployment_identifier 75 | ) 76 | )) 77 | end 78 | 79 | it 'adds a name tag' do 80 | name = "allow-ssh-from-bastion-#{component}-#{deployment_identifier}" 81 | expect(@plan) 82 | .to(include_resource_creation( 83 | type: 'aws_security_group', 84 | name: 'allow_ssh_from_bastion' 85 | ) 86 | .with_attribute_value(:tags, a_hash_including(Name: name))) 87 | end 88 | 89 | it 'adds a role tag of bastion' do 90 | expect(@plan) 91 | .to(include_resource_creation( 92 | type: 'aws_security_group', 93 | name: 'allow_ssh_from_bastion' 94 | ) 95 | .with_attribute_value(:tags, a_hash_including(Role: 'bastion'))) 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /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 = "4.33.0" 7 | hashes = [ 8 | "h1:0S9ZXYg6K0CTOJUTQnoH94YrKuOYyJYEcc+hN5qGafA=", 9 | "zh:421b24e21d7fac4d65d97438d2c0a4effe71d3a1bd15820d6fde2879e49fe817", 10 | "zh:4378a84ca8e2a6990f47abc24367b801e884be928671b37ad7b8e7b656f73e48", 11 | "zh:54e0d7884edf3cefd096715794d32b6532138dca905f0b2fe84fb2117594293c", 12 | "zh:6269a7d0312057db5ded669e9f7f9bd80fb6dcb549b50d8d7f3f3b2a0361b8a5", 13 | "zh:67f57d16aa3db493a3174c3c5f30385c7af9767c4e3cdca14e5a4bf384ff59d9", 14 | "zh:7d4d4a1d963e431ffdc3348e3a578d3ba0fa782b1f4bf55fd5c0e527d24fed81", 15 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 16 | "zh:cd8e3d32485acb49c1b06f63916fec8e73a4caa6cf88ae9c4bf236d6f5d9b914", 17 | "zh:d586fd01195bd3775346495e61806e79b6012e745dc05e31a30b958acf968abe", 18 | "zh:d76122060f25ab87887a743096a42d47ba091c2c019ac13ce6b3973b2babe5a3", 19 | "zh:e917d36fe18eddc42ec743b3152b4dcb4853b75ea7a679abd19bdf271bc48221", 20 | "zh:eb780860d5c04f43a018aef564e76a2d84e9aa68984fa1f968ca8c09d23a611a", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/main.tf: -------------------------------------------------------------------------------- 1 | module "base_network" { 2 | source = "infrablocks/base-networking/aws" 3 | version = "5.0.0" 4 | 5 | region = var.region 6 | vpc_cidr = var.vpc_cidr 7 | availability_zones = var.availability_zones 8 | 9 | component = "${var.component}-net" 10 | deployment_identifier = var.deployment_identifier 11 | 12 | private_zone_id = var.private_zone_id 13 | 14 | include_nat_gateways = "no" 15 | } 16 | 17 | module "classic_load_balancer" { 18 | source = "infrablocks/classic-load-balancer/aws" 19 | version = "2.0.0" 20 | 21 | vpc_id = module.base_network.vpc_id 22 | subnet_ids = module.base_network.public_subnet_ids 23 | 24 | domain_name = var.domain_name 25 | public_zone_id = var.public_zone_id 26 | private_zone_id = var.private_zone_id 27 | 28 | component = var.component 29 | deployment_identifier = var.deployment_identifier 30 | 31 | listeners = var.listeners 32 | access_control = var.access_control 33 | 34 | egress_cidrs = var.egress_cidrs 35 | 36 | health_check_target = var.health_check_target 37 | 38 | include_public_dns_record = var.include_public_dns_record 39 | include_private_dns_record = var.include_private_dns_record 40 | 41 | expose_to_public_internet = var.expose_to_public_internet 42 | } 43 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/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 | 45 | output "load_balancer_name" { 46 | value = module.classic_load_balancer.name 47 | } 48 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/unit/infra/prerequisites/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" {} 2 | variable "vpc_cidr" {} 3 | variable "availability_zones" { 4 | type = list(string) 5 | } 6 | 7 | variable "component" {} 8 | variable "deployment_identifier" {} 9 | 10 | variable "domain_name" {} 11 | variable "public_zone_id" {} 12 | variable "private_zone_id" {} 13 | 14 | variable "listeners" { 15 | type = list(object({ 16 | lb_port: number, 17 | lb_protocol: string, 18 | instance_port: number, 19 | instance_protocol: string 20 | ssl_certificate_id: string 21 | })) 22 | } 23 | variable "access_control" { 24 | type = list(object({ 25 | lb_port: number, 26 | instance_port: number 27 | allow_cidrs: list(string) 28 | })) 29 | } 30 | 31 | variable "health_check_target" {} 32 | 33 | variable "egress_cidrs" { 34 | type = list(string) 35 | } 36 | 37 | variable "include_public_dns_record" {} 38 | variable "include_private_dns_record" {} 39 | 40 | variable "expose_to_public_internet" {} 41 | -------------------------------------------------------------------------------- /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 | "zh:421b24e21d7fac4d65d97438d2c0a4effe71d3a1bd15820d6fde2879e49fe817", 10 | "zh:4378a84ca8e2a6990f47abc24367b801e884be928671b37ad7b8e7b656f73e48", 11 | "zh:54e0d7884edf3cefd096715794d32b6532138dca905f0b2fe84fb2117594293c", 12 | "zh:6269a7d0312057db5ded669e9f7f9bd80fb6dcb549b50d8d7f3f3b2a0361b8a5", 13 | "zh:67f57d16aa3db493a3174c3c5f30385c7af9767c4e3cdca14e5a4bf384ff59d9", 14 | "zh:7d4d4a1d963e431ffdc3348e3a578d3ba0fa782b1f4bf55fd5c0e527d24fed81", 15 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 16 | "zh:cd8e3d32485acb49c1b06f63916fec8e73a4caa6cf88ae9c4bf236d6f5d9b914", 17 | "zh:d586fd01195bd3775346495e61806e79b6012e745dc05e31a30b958acf968abe", 18 | "zh:d76122060f25ab87887a743096a42d47ba091c2c019ac13ce6b3973b2babe5a3", 19 | "zh:e917d36fe18eddc42ec743b3152b4dcb4853b75ea7a679abd19bdf271bc48221", 20 | "zh:eb780860d5c04f43a018aef564e76a2d84e9aa68984fa1f968ca8c09d23a611a", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /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 "bastion" { 10 | source = "../../../.." 11 | 12 | vpc_id = data.terraform_remote_state.prerequisites.outputs.vpc_id 13 | subnet_ids = data.terraform_remote_state.prerequisites.outputs.private_subnet_ids 14 | 15 | component = var.component 16 | deployment_identifier = var.deployment_identifier 17 | 18 | ami = var.ami 19 | instance_type = var.instance_type 20 | 21 | ssh_public_key_path = var.ssh_public_key_path 22 | 23 | allowed_cidrs = var.allowed_cidrs 24 | egress_cidrs = var.egress_cidrs 25 | 26 | load_balancer_names = [data.terraform_remote_state.prerequisites.outputs.load_balancer_name] 27 | 28 | minimum_instances = var.minimum_instances 29 | maximum_instances = var.maximum_instances 30 | desired_instances = var.desired_instances 31 | 32 | associate_public_ip_address = var.associate_public_ip_address 33 | } 34 | -------------------------------------------------------------------------------- /spec/unit/infra/root/outputs.tf: -------------------------------------------------------------------------------- 1 | output "launch_configuration_name" { 2 | value = module.bastion.launch_configuration_name 3 | } 4 | 5 | output "allow_ssh_to_bastion_security_group_id" { 6 | value = module.bastion.allow_ssh_to_bastion_security_group_id 7 | } 8 | 9 | output "allow_ssh_from_bastion_security_group_id" { 10 | value = module.bastion.allow_ssh_from_bastion_security_group_id 11 | } 12 | -------------------------------------------------------------------------------- /spec/unit/infra/root/provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/unit/infra/root/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" {} 2 | 3 | variable "component" {} 4 | variable "deployment_identifier" {} 5 | 6 | variable "subnet_ids" { 7 | type = list(string) 8 | default = null 9 | } 10 | 11 | variable "ami" { 12 | default = null 13 | } 14 | variable "instance_type" { 15 | default = null 16 | } 17 | 18 | variable "ssh_public_key_path" {} 19 | 20 | variable "allowed_cidrs" { 21 | type = list(string) 22 | } 23 | variable "egress_cidrs" { 24 | type = list(string) 25 | } 26 | 27 | variable "minimum_instances" { 28 | default = null 29 | } 30 | variable "maximum_instances" { 31 | default = null 32 | } 33 | variable "desired_instances" { 34 | default = null 35 | } 36 | 37 | variable "associate_public_ip_address" { 38 | default = null 39 | } 40 | -------------------------------------------------------------------------------- /spec/unit/key_pair_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'key pair' 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(:ssh_public_key_path) do 13 | var(role: :root, name: 'ssh_public_key_path') 14 | end 15 | let(:ssh_public_key) do 16 | File.read(ssh_public_key_path) 17 | end 18 | 19 | describe 'by default' do 20 | before(:context) do 21 | @plan = plan(role: :root) 22 | end 23 | 24 | it 'creates a key pair' do 25 | expect(@plan) 26 | .to(include_resource_creation(type: 'aws_key_pair') 27 | .once) 28 | end 29 | 30 | it 'includes the component and the deployment identifier in the key name' do 31 | key_name = "bastion-#{component}-#{deployment_identifier}" 32 | expect(@plan) 33 | .to(include_resource_creation(type: 'aws_key_pair') 34 | .with_attribute_value(:key_name, key_name)) 35 | end 36 | 37 | it 'uses the provided public key' do 38 | expect(@plan) 39 | .to(include_resource_creation(type: 'aws_key_pair') 40 | .with_attribute_value(:public_key, ssh_public_key)) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/unit/launch_configuration_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'launch configuration' 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) 16 | end 17 | 18 | it 'creates a launch configuration' do 19 | expect(@plan) 20 | .to(include_resource_creation(type: 'aws_launch_configuration') 21 | .once) 22 | end 23 | 24 | it 'includes the component in the name prefix' do 25 | expect(@plan) 26 | .to(include_resource_creation(type: 'aws_launch_configuration') 27 | .with_attribute_value(:name_prefix, match(/.*#{component}.*/))) 28 | end 29 | 30 | it 'includes the deployment identifier in the name prefix' do 31 | expect(@plan) 32 | .to(include_resource_creation(type: 'aws_launch_configuration') 33 | .with_attribute_value( 34 | :name_prefix, match(/.*#{deployment_identifier}.*/) 35 | )) 36 | end 37 | 38 | it 'uses an instance type of t4g.nano' do 39 | expect(@plan) 40 | .to(include_resource_creation(type: 'aws_launch_configuration') 41 | .with_attribute_value(:instance_type, 't4g.nano')) 42 | end 43 | 44 | it 'does not associate a public IP address' do 45 | expect(@plan) 46 | .to(include_resource_creation(type: 'aws_launch_configuration') 47 | .with_attribute_value(:associate_public_ip_address, false)) 48 | end 49 | end 50 | 51 | describe 'when AMI is provided' do 52 | before(:context) do 53 | @plan = plan(role: :root) do |vars| 54 | vars.ami = 'ami-12345678' 55 | end 56 | end 57 | 58 | it 'uses the provided AMI ID as the image ID' do 59 | expect(@plan) 60 | .to(include_resource_creation(type: 'aws_launch_configuration') 61 | .with_attribute_value(:image_id, 'ami-12345678')) 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/unit/to_security_group_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'SSH to bastion security group' 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_id) do 13 | output(role: :prerequisites, name: 'vpc_id') 14 | end 15 | let(:allowed_cidrs) do 16 | var(role: :root, name: 'allowed_cidrs') 17 | end 18 | let(:egress_cidrs) do 19 | var(role: :root, name: 'egress_cidrs') 20 | end 21 | 22 | describe 'by default' do 23 | before(:context) do 24 | @plan = plan(role: :root) 25 | end 26 | 27 | it 'create a security group' do 28 | expect(@plan) 29 | .to(include_resource_creation( 30 | type: 'aws_security_group', 31 | name: 'allow_ssh_to_bastion' 32 | ).once) 33 | end 34 | 35 | it 'includes the component and deployment identifier in the name' do 36 | name = "allow-ssh-to-bastion-#{component}-#{deployment_identifier}" 37 | expect(@plan) 38 | .to(include_resource_creation( 39 | type: 'aws_security_group', 40 | name: 'allow_ssh_to_bastion' 41 | ) 42 | .with_attribute_value(:name, name)) 43 | end 44 | 45 | it 'uses the provided VPC ID' do 46 | expect(@plan) 47 | .to(include_resource_creation( 48 | type: 'aws_security_group', 49 | name: 'allow_ssh_to_bastion' 50 | ) 51 | .with_attribute_value(:vpc_id, vpc_id)) 52 | end 53 | 54 | it 'allows inbound SSH connections from the provided CIDRs' do 55 | expect(@plan) 56 | .to(include_resource_creation( 57 | type: 'aws_security_group', 58 | name: 'allow_ssh_to_bastion' 59 | ) 60 | .with_attribute_value( 61 | [:ingress, 0], 62 | a_hash_including( 63 | from_port: 22, 64 | to_port: 22, 65 | protocol: 'tcp', 66 | cidr_blocks: allowed_cidrs 67 | ) 68 | )) 69 | end 70 | 71 | it 'allows outbound SSH connections to the provided CIDRs' do 72 | expect(@plan) 73 | .to(include_resource_creation( 74 | type: 'aws_security_group', 75 | name: 'allow_ssh_to_bastion' 76 | ) 77 | .with_attribute_value( 78 | [:egress, 0], 79 | a_hash_including( 80 | from_port: 22, 81 | to_port: 22, 82 | protocol: 'tcp', 83 | cidr_blocks: egress_cidrs 84 | ) 85 | )) 86 | end 87 | 88 | it 'adds component and deployment identifier tags' do 89 | expect(@plan) 90 | .to(include_resource_creation( 91 | type: 'aws_security_group', 92 | name: 'allow_ssh_to_bastion' 93 | ) 94 | .with_attribute_value( 95 | :tags, 96 | a_hash_including( 97 | Component: component, 98 | DeploymentIdentifier: deployment_identifier 99 | ) 100 | )) 101 | end 102 | 103 | it 'adds a name tag' do 104 | name = "allow-ssh-to-bastion-#{component}-#{deployment_identifier}" 105 | expect(@plan) 106 | .to(include_resource_creation( 107 | type: 'aws_security_group', 108 | name: 'allow_ssh_to_bastion' 109 | ) 110 | .with_attribute_value(:tags, a_hash_including(Name: name))) 111 | end 112 | 113 | it 'adds a role tag of bastion' do 114 | expect(@plan) 115 | .to(include_resource_creation( 116 | type: 'aws_security_group', 117 | name: 'allow_ssh_to_bastion' 118 | ) 119 | .with_attribute_value(:tags, a_hash_including(Role: 'bastion'))) 120 | end 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_id" { 2 | description = "The ID of the VPC the bastion should be deployed into." 3 | type = string 4 | } 5 | variable "subnet_ids" { 6 | description = "The IDs of the subnets the bastion should deploy into." 7 | type = list(string) 8 | default = null 9 | } 10 | 11 | variable "component" { 12 | description = "The name of this component." 13 | type = string 14 | } 15 | variable "deployment_identifier" { 16 | description = "An identifier for this instantiation." 17 | type = string 18 | } 19 | 20 | variable "ami" { 21 | description = "The ID of the AMI for the bastion instances." 22 | type = string 23 | default = null 24 | } 25 | variable "instance_type" { 26 | description = "The instance type of the bastion instances." 27 | type = string 28 | default = "t4g.nano" 29 | } 30 | variable "ssh_public_key_path" { 31 | description = "The absolute path of the SSH public key to use for bastion access." 32 | type = string 33 | } 34 | 35 | variable "allowed_cidrs" { 36 | description = "The CIDRs that are allowed to access the bastion." 37 | type = list(string) 38 | } 39 | variable "egress_cidrs" { 40 | description = "The CIDRs that are reachable from the bastion." 41 | type = list(string) 42 | } 43 | 44 | variable "load_balancer_names" { 45 | description = "The names of the load balancers to update on autoscaling events." 46 | type = list(string) 47 | default = [] 48 | } 49 | 50 | variable "minimum_instances" { 51 | description = "The minimum number of bastion instances." 52 | type = number 53 | default = 1 54 | } 55 | variable "maximum_instances" { 56 | description = "The maximum number of bastion instances." 57 | type = number 58 | default = 1 59 | } 60 | variable "desired_instances" { 61 | description = "The desired number of bastion instances." 62 | type = number 63 | default = 1 64 | } 65 | 66 | variable "associate_public_ip_address" { 67 | description = "Associate a public ip address with an instance in a VPC." 68 | type = bool 69 | default = false 70 | } 71 | --------------------------------------------------------------------------------