├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── assets └── architecture.png ├── main.tf ├── outputs.tf ├── packer ├── build.json └── install_vault.sh ├── user_data.sh ├── variables.tf └── versions.tf /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # .tfvars files 9 | *.tfvars 10 | 11 | test/* 12 | backend.tf 13 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | - repo: git://github.com/antonbabenko/pre-commit-terraform 2 | rev: v1.8.1 3 | hooks: 4 | - id: terraform_validate_no_variables 5 | - id: terraform_fmt 6 | - id: terraform_docs 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Pierce Bartine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Module: Hashicorp Vault (AWS) 2 | 3 | Production-ready Vault in a Terraform module 4 | 5 | ## Goals 6 | 7 | This is a Terraform module for creating a production-grade Hashicorp Vault deployment on AWS. Some stated goals: 8 | 9 | - Maximum security 10 | - Minimal unmanaged dependencies, to reduce operational complexity 11 | - Ease of deployment, able to be used out of the box 12 | 13 | ## Architecture 14 | 15 | ![Vault Architecture Diagram](/assets/architecture.png?raw=true) 16 | 17 | Normally, Hashicorp recommends using Consul (another one of their tools) as both the storage and high-availability (HA) backend for Vault. This presents a number of challenges: 18 | 19 | - Generating Consul agent certificates and Gossip token 20 | - Deploying Consul in an autoscaling group along with IAM resources 21 | - Bootstrapping Consul's ACL system and backing up the master token 22 | - Keeping Consul single-tenant (to prevent data loss/corruption in Vault) 23 | - Monitoring and logging of Consul itself 24 | - Taking periodic Consul snapshots as backup 25 | 26 | In order to reduce operational complexity as much as possible, we instead opt for Amazon S3 for Vault's storage backend and AWS DynamoDB for its HA backend. As these are managed services, we can offload much our SLA onto them and focus on maintaining Vault itself. This also loosens our coupling to Consul and simplifies deployment greatly. 27 | 28 | ## Assumptions 29 | 30 | SSM Parameter Store 31 | 32 | - `/vault/${vault_cluster_name}/sumologic_access_key` 33 | - `/vault/${vault_cluster_name}/sumologic_access_id` 34 | - `/vault/${vault_cluster_name}/dd_api_key` 35 | - `/vault/${vault_cluster_name}/vault.pem` 36 | - `/vault/${vault_cluster_name}/vault-key.pem` 37 | 38 | ## Notes 39 | 40 | In progress: 41 | 42 | - TLS is disabled by default in the user data 43 | - TODO: make Sumo Logic, Datadog, and TLS certs optional at build time 44 | 45 | 46 | ## Inputs 47 | 48 | | Name | Description | Type | Default | Required | 49 | |------|-------------|:----:|:-----:|:-----:| 50 | | ami\_id | AMI ID to launch the Vault auto scaling group with | string | n/a | yes | 51 | | cluster\_name | Name of the Vault cluster | string | `"vault"` | no | 52 | | domain\_name | Domain name of DNS entry to create | string | n/a | yes | 53 | | dynamodb\_read\_capacity | Read capacity for Vault's DynamoDB high availability backend | string | `"5"` | no | 54 | | dynamodb\_write\_capacity | Write capacity for Vault's DynamoDB high availability backend | string | `"5"` | no | 55 | | enable\_termination\_protection | Enable EC2 instance termination protection | string | `"true"` | no | 56 | | instance\_type | EC2 instance type for Vault instances | string | `"t2.medium"` | no | 57 | | internal\_lb | Whether to make the Vault load balancer internal | string | `"true"` | no | 58 | | max\_instances | Maximum number of Vault instances in the auto scaling group | string | `"3"` | no | 59 | | min\_instances | Minimum number of Vault instances in the auto scaling group | string | `"3"` | no | 60 | | ssh\_key\_name | Name of the SSH keypair to use for the Vault EC2 instances | string | n/a | yes | 61 | | subnet\_ids | List of subnet IDs to launch the Vault auto scaling group in | list | n/a | yes | 62 | | tags | Extra tags to add to all resources created by this module | map | `{}` | no | 63 | | vpc\_id | ID of the AWS VPC to create the Vault cluster in | string | n/a | yes | 64 | | zone\_id | Route53 hosted zone ID to create the DNS entry in | string | n/a | yes | 65 | 66 | ## Outputs 67 | 68 | | Name | Description | 69 | |------|-------------| 70 | | kms\_key\_id | ID of the KMS key that Vault uses for Auto-Unseal, S3 encryption, and SSM parameters | 71 | | vault\_client\_sg\_id | ID of the security group used by clients to connect to Vault | 72 | | vault\_cluster\_fqdn | Fully qualified domain name for the Vault cluster | 73 | | vault\_cluster\_role\_arn | ARN of the AWS IAM role that Vault runs as | 74 | | vault\_cluster\_role\_name | Name of the AWS IAM role that Vault runs as | 75 | 76 | 77 | -------------------------------------------------------------------------------- /assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pbar1/terraform-aws-vault/82a392f4a1d2421644d7b5d2100b606e7014c558/assets/architecture.png -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" { 2 | } 3 | 4 | data "aws_caller_identity" "current" { 5 | } 6 | 7 | data "aws_kms_key" "ssm" { 8 | key_id = "alias/aws/ssm" 9 | } 10 | 11 | # KMS 12 | 13 | resource "aws_kms_key" "vault" { 14 | description = "${var.cluster_name} key for S3 storage backend, Auto Unseal, and SSM Parameter Store" 15 | tags = var.tags 16 | } 17 | 18 | resource "aws_kms_alias" "vault" { 19 | name = "alias/${var.cluster_name}" 20 | target_key_id = aws_kms_key.vault.key_id 21 | } 22 | 23 | # S3 24 | 25 | resource "aws_s3_bucket" "vault" { 26 | bucket_prefix = "${var.cluster_name}-storage-" 27 | acl = "private" 28 | force_destroy = false 29 | 30 | versioning { 31 | enabled = true 32 | } 33 | 34 | server_side_encryption_configuration { 35 | rule { 36 | apply_server_side_encryption_by_default { 37 | sse_algorithm = "aws:kms" 38 | kms_master_key_id = aws_kms_key.vault.key_id 39 | } 40 | } 41 | } 42 | 43 | tags = var.tags 44 | } 45 | 46 | resource "aws_s3_bucket_policy" "vault" { 47 | bucket = aws_s3_bucket.vault.id 48 | policy = data.aws_iam_policy_document.vault_bucket_policy.json 49 | } 50 | 51 | data "aws_iam_policy_document" "vault_bucket_policy" { 52 | statement { 53 | sid = "AllowVaultRole" 54 | effect = "Allow" 55 | 56 | principals { 57 | type = "AWS" 58 | identifiers = [aws_iam_role.vault.arn] 59 | } 60 | 61 | actions = ["s3:*"] 62 | 63 | resources = [ 64 | aws_s3_bucket.vault.arn, 65 | "${aws_s3_bucket.vault.arn}/*", 66 | ] 67 | } 68 | } 69 | 70 | # DynamoDB 71 | 72 | resource "aws_dynamodb_table" "vault" { 73 | name = "${var.cluster_name}-ha" 74 | read_capacity = var.dynamodb_read_capacity 75 | write_capacity = var.dynamodb_write_capacity 76 | hash_key = "Path" 77 | range_key = "Key" 78 | 79 | attribute { 80 | name = "Path" 81 | type = "S" 82 | } 83 | 84 | attribute { 85 | name = "Key" 86 | type = "S" 87 | } 88 | 89 | tags = var.tags 90 | } 91 | 92 | # IAM 93 | 94 | data "aws_iam_policy_document" "vault_role_policy" { 95 | statement { 96 | sid = "DynamoHABackend" 97 | effect = "Allow" 98 | 99 | actions = [ 100 | "dynamodb:DescribeLimits", 101 | "dynamodb:DescribeTimeToLive", 102 | "dynamodb:ListTagsOfResource", 103 | "dynamodb:DescribeReservedCapacityOfferings", 104 | "dynamodb:DescribeReservedCapacity", 105 | "dynamodb:ListTables", 106 | "dynamodb:BatchGetItem", 107 | "dynamodb:BatchWriteItem", 108 | "dynamodb:CreateTable", 109 | "dynamodb:DeleteItem", 110 | "dynamodb:GetItem", 111 | "dynamodb:GetRecords", 112 | "dynamodb:PutItem", 113 | "dynamodb:Query", 114 | "dynamodb:UpdateItem", 115 | "dynamodb:Scan", 116 | "dynamodb:DescribeTable", 117 | ] 118 | 119 | resources = [aws_dynamodb_table.vault.arn] 120 | } 121 | 122 | statement { 123 | sid = "KMSAutoUnseal" 124 | effect = "Allow" 125 | 126 | actions = [ 127 | "kms:Encrypt", 128 | "kms:Decrypt", 129 | "kms:DescribeKey", 130 | "kms:GenerateDataKey" 131 | ] 132 | 133 | resources = [ 134 | aws_kms_key.vault.arn, 135 | data.aws_kms_key.ssm.arn, 136 | ] 137 | } 138 | 139 | statement { 140 | sid = "SSMParameters" 141 | effect = "Allow" 142 | actions = ["ssm:GetParameter"] 143 | 144 | resources = [ 145 | "arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/vault/${var.cluster_name}/*", 146 | ] 147 | } 148 | 149 | # statement { 150 | # sid = "AllowS3Access" 151 | # effect = "Allow" 152 | 153 | # actions = ["s3:*"] 154 | 155 | # resources = [ 156 | # aws_s3_bucket.vault.arn, 157 | # "${aws_s3_bucket.vault.arn}/*", 158 | # ] 159 | # } 160 | } 161 | 162 | resource "aws_iam_policy" "vault" { 163 | name = var.cluster_name 164 | description = "DynamoDB, KMS, and SSM access for Vault cluster ${var.cluster_name}" 165 | policy = data.aws_iam_policy_document.vault_role_policy.json 166 | } 167 | 168 | resource "aws_iam_role_policy_attachment" "vault" { 169 | role = aws_iam_role.vault.name 170 | policy_arn = aws_iam_policy.vault.arn 171 | } 172 | 173 | resource "aws_iam_role" "vault" { 174 | name = var.cluster_name 175 | description = "Role that Vault cluster ${var.cluster_name} runs as" 176 | assume_role_policy = data.aws_iam_policy_document.assume_ec2.json 177 | tags = var.tags 178 | } 179 | 180 | data "aws_iam_policy_document" "assume_ec2" { 181 | statement { 182 | effect = "Allow" 183 | actions = ["sts:AssumeRole"] 184 | 185 | principals { 186 | type = "Service" 187 | identifiers = ["ec2.amazonaws.com"] 188 | } 189 | } 190 | } 191 | 192 | resource "aws_iam_instance_profile" "vault" { 193 | name = var.cluster_name 194 | role = aws_iam_role.vault.name 195 | } 196 | 197 | # SG 198 | 199 | resource "aws_security_group" "vault_cluster" { 200 | name = "${var.cluster_name}-cluster" 201 | description = "Vault cluster ${var.cluster_name} internal communication" 202 | vpc_id = var.vpc_id 203 | 204 | ingress { 205 | description = "Vault UI and API connectivity" 206 | from_port = 8200 207 | to_port = 8200 208 | protocol = "tcp" 209 | self = true 210 | } 211 | 212 | ingress { 213 | description = "Vault server to server traffic within a cluster" 214 | from_port = 8201 215 | to_port = 8201 216 | protocol = "tcp" 217 | self = true 218 | } 219 | 220 | ingress { 221 | description = "Vault client security group and allowd cidrs" 222 | from_port = 8200 223 | to_port = 8200 224 | protocol = "tcp" 225 | cidr_blocks = var.allowed_cidrs 226 | security_groups = [aws_security_group.vault_client.id] 227 | } 228 | 229 | egress { 230 | description = "Allow all egress traffic" 231 | from_port = 0 232 | to_port = 0 233 | protocol = "-1" 234 | cidr_blocks = ["0.0.0.0/0"] 235 | } 236 | 237 | tags = var.tags 238 | } 239 | 240 | resource "aws_security_group" "vault_client" { 241 | name = "${var.cluster_name}-client" 242 | description = "Vault cluster ${var.cluster_name} clients" 243 | vpc_id = var.vpc_id 244 | tags = var.tags 245 | } 246 | 247 | # ASG 248 | 249 | data "template_file" "user_data" { 250 | template = file("${path.module}/user_data.sh") 251 | 252 | vars = { 253 | aws_region = data.aws_region.current.name 254 | vault_cluster_name = var.cluster_name 255 | s3_bucket = aws_s3_bucket.vault.id 256 | kms_key_id = aws_kms_key.vault.key_id 257 | dynamodb_table = aws_dynamodb_table.vault.name 258 | } 259 | } 260 | 261 | resource "aws_launch_template" "vault" { 262 | name = var.cluster_name 263 | description = "Vault cluster ${var.cluster_name} launch template" 264 | image_id = var.ami_id 265 | user_data = base64encode(data.template_file.user_data.rendered) 266 | instance_type = var.instance_type 267 | key_name = var.ssh_key_name 268 | vpc_security_group_ids = [aws_security_group.vault_cluster.id] 269 | 270 | iam_instance_profile { 271 | arn = aws_iam_instance_profile.vault.arn 272 | } 273 | 274 | # tag_specifications { 275 | # resource_type = "instance" 276 | # tags = "${var.tags}" 277 | # } 278 | 279 | tags = var.tags 280 | } 281 | 282 | resource "aws_autoscaling_group" "vault" { 283 | name = var.cluster_name 284 | min_size = var.min_instances 285 | max_size = var.max_instances 286 | vpc_zone_identifier = var.subnet_ids 287 | target_group_arns = [aws_lb_target_group.vault.arn] 288 | 289 | launch_template { 290 | id = aws_launch_template.vault.id 291 | version = aws_launch_template.vault.latest_version 292 | } 293 | } 294 | 295 | # LB 296 | 297 | resource "aws_lb_target_group" "vault" { 298 | name = var.cluster_name 299 | port = 8200 300 | protocol = "TCP" 301 | vpc_id = var.vpc_id 302 | 303 | stickiness { 304 | type = "lb_cookie" 305 | enabled = false 306 | } 307 | 308 | health_check { 309 | protocol = "TCP" 310 | } 311 | } 312 | 313 | resource "aws_lb_listener" "vault" { 314 | load_balancer_arn = aws_lb.vault.arn 315 | port = 443 316 | protocol = "TLS" 317 | certificate_arn = var.acm_cert_arn 318 | ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01" 319 | 320 | default_action { 321 | type = "forward" 322 | target_group_arn = aws_lb_target_group.vault.arn 323 | } 324 | } 325 | 326 | resource "aws_lb" "vault" { 327 | name = var.cluster_name 328 | internal = var.internal_lb 329 | load_balancer_type = "network" 330 | enable_deletion_protection = var.enable_termination_protection 331 | subnets = var.subnet_ids 332 | tags = var.tags 333 | } 334 | 335 | # Route 53 336 | 337 | resource "aws_route53_record" "vault" { 338 | zone_id = var.zone_id 339 | name = var.domain_name 340 | type = "A" 341 | 342 | alias { 343 | name = aws_lb.vault.dns_name 344 | zone_id = aws_lb.vault.zone_id 345 | evaluate_target_health = false 346 | } 347 | } 348 | 349 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "vault_cluster_fqdn" { 2 | description = "Fully qualified domain name for the Vault cluster" 3 | value = aws_route53_record.vault.fqdn 4 | } 5 | 6 | output "vault_client_sg_id" { 7 | description = "ID of the security group used by clients to connect to Vault" 8 | value = aws_security_group.vault_client.id 9 | } 10 | 11 | output "vault_cluster_role_arn" { 12 | description = "ARN of the AWS IAM role that Vault runs as" 13 | value = aws_iam_role.vault.arn 14 | } 15 | 16 | output "vault_cluster_role_name" { 17 | description = "Name of the AWS IAM role that Vault runs as" 18 | value = aws_iam_role.vault.name 19 | } 20 | 21 | output "kms_key_id" { 22 | description = "ID of the KMS key that Vault uses for Auto-Unseal, S3 encryption, and SSM parameters" 23 | value = aws_kms_key.vault.key_id 24 | } 25 | 26 | -------------------------------------------------------------------------------- /packer/build.json: -------------------------------------------------------------------------------- 1 | { 2 | "min_packer_version": "0.12.0", 3 | "variables": { 4 | "aws_region": "us-east-2", 5 | "iam_instance_profile": "", 6 | "vault_version": "1.1.2" 7 | }, 8 | "builders": [ 9 | { 10 | "ami_name": "vault-{{user `vault_version`}}-{{isotime | clean_ami_name}}", 11 | "ami_description": "Hashicorp Vault on Amazon Linux 2", 12 | "iam_instance_profile": "{{user `iam_instance_profile`}}", 13 | "instance_type": "t2.micro", 14 | "region": "{{user `aws_region`}}", 15 | "type": "amazon-ebs", 16 | "source_ami_filter": { 17 | "filters": { 18 | "virtualization-type": "hvm", 19 | "architecture": "x86_64", 20 | "name": "amzn2-ami-hvm-2.0.????????-x86_64-gp2", 21 | "block-device-mapping.volume-type": "gp2", 22 | "root-device-type": "ebs" 23 | }, 24 | "owners": ["amazon"], 25 | "most_recent": true 26 | }, 27 | "ssh_username": "ec2-user", 28 | "tags": { 29 | "OS": "Amazon Linux 2", 30 | "SourceAMIName": "{{ .SourceAMIName }}", 31 | "SourceAMI": "{{ .SourceAMI }}", 32 | "VaultVersion": "{{user `vault_version`}}", 33 | "BuildTime": "{{isotime}}", 34 | "CreatedBy": "Packer" 35 | } 36 | } 37 | ], 38 | "provisioners": [ 39 | { 40 | "type": "shell", 41 | "inline": ["sudo yum install -y jq"] 42 | }, 43 | { 44 | "type": "file", 45 | "source": "install_vault.sh", 46 | "destination": "/tmp/install_vault.sh" 47 | }, 48 | { 49 | "type": "shell", 50 | "inline": ["sudo bash /tmp/install_vault.sh {{user `vault_version`}}"] 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /packer/install_vault.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Usage: bash install_vault.sh [VAULT_VERSION] 6 | 7 | VAULT_VERSION="$1" 8 | 9 | # Download Vault and signatures 10 | curl --silent --remote-name https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip 11 | curl --silent --remote-name https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_SHA256SUMS 12 | curl --silent --remote-name https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_SHA256SUMS.sig 13 | 14 | # Install Vault and verify it is on PATH 15 | unzip vault_${VAULT_VERSION}_linux_amd64.zip 16 | chown root:root vault 17 | chmod +x vault 18 | mv vault /usr/local/bin/ 19 | 20 | # Give Vault the ability to use the mlock syscall without running the process as root 21 | setcap cap_ipc_lock=+ep /usr/local/bin/vault 22 | 23 | # Create a unique, non-privileged system user to run Vault 24 | useradd --system --home /etc/vault.d --shell /bin/false vault 25 | 26 | cat < /etc/systemd/system/vault.service 27 | [Unit] 28 | Description="HashiCorp Vault - A tool for managing secrets" 29 | Documentation=https://www.vaultproject.io/docs/ 30 | Requires=network-online.target 31 | After=network-online.target 32 | ConditionFileNotEmpty=/etc/vault.d/vault.hcl 33 | 34 | [Service] 35 | User=vault 36 | Group=vault 37 | ProtectSystem=full 38 | ProtectHome=read-only 39 | PrivateTmp=yes 40 | PrivateDevices=yes 41 | SecureBits=keep-caps 42 | AmbientCapabilities=CAP_IPC_LOCK 43 | Capabilities=CAP_IPC_LOCK+ep 44 | CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK 45 | NoNewPrivileges=yes 46 | ExecStart=/usr/local/bin/vault server -config=/etc/vault.d/vault.hcl 47 | ExecReload=/bin/kill --signal HUP $MAINPID 48 | KillMode=process 49 | KillSignal=SIGINT 50 | Restart=on-failure 51 | RestartSec=5 52 | TimeoutStopSec=30 53 | StartLimitInterval=60 54 | StartLimitBurst=3 55 | LimitNOFILE=65536 56 | 57 | [Install] 58 | WantedBy=multi-user.target 59 | EOF 60 | 61 | mkdir --parents /etc/vault.d 62 | touch /etc/vault.d/vault.hcl 63 | chown --recursive vault:vault /etc/vault.d 64 | chmod 640 /etc/vault.d/vault.hcl 65 | -------------------------------------------------------------------------------- /user_data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Send the log output from this script to user-data.log, syslog, and the console 6 | # From: https://alestic.com/2010/12/ec2-user-data-output/ 7 | exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1 8 | 9 | export AWS_DEFAULT_REGION=${aws_region} 10 | SELF_PRIVATE_IP="$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4)" 11 | 12 | # aws ssm get-parameter \ 13 | # --name "/vault/${vault_cluster_name}/vault.pem" \ 14 | # | jq -r '.Parameter.Value' \ 15 | # > /etc/vault.d/vault.pem 16 | 17 | # aws ssm get-parameter \ 18 | # --name "/vault/${vault_cluster_name}/vault-key.pem" \ 19 | # --with-decryption \ 20 | # | jq -r '.Parameter.Value' \ 21 | # > /etc/vault.d/vault-key.pem 22 | 23 | export DD_API_KEY="$(aws ssm get-parameter --name "/vault/${vault_cluster_name}/dd_api_key" --with-decryption | jq -r '.Parameter.Value')" 24 | bash -c "$(curl -L https://raw.githubusercontent.com/DataDog/datadog-agent/master/cmd/agent/install_script.sh)" 25 | 26 | cat < /etc/vault.d/vault.hcl 27 | ui = true 28 | 29 | storage "s3" { 30 | region = "${aws_region}" 31 | bucket = "${s3_bucket}" 32 | kms_key_id = "${kms_key_id}" 33 | } 34 | 35 | ha_storage "dynamodb" { 36 | ha_enabled = "true" 37 | region = "${aws_region}" 38 | table = "${dynamodb_table}" 39 | } 40 | 41 | seal "awskms" { 42 | region = "${aws_region}" 43 | kms_key_id = "${kms_key_id}" 44 | } 45 | 46 | listener "tcp" { 47 | address = "0.0.0.0:8200" 48 | cluster_address = "0.0.0.0:8201" 49 | # tls_cert_file = "/etc/vault.d/vault.pem" 50 | # tls_key_file = "/etc/vault.d/vault-key.pem" 51 | tls_disable = "true" 52 | } 53 | 54 | telemetry { 55 | dogstatsd_addr = "127.0.0.1:8125" 56 | dogstatsd_tags = [] 57 | } 58 | 59 | cluster_addr = "https://$SELF_PRIVATE_IP:8201" 60 | api_addr = "https://$SELF_PRIVATE_IP:8200" 61 | EOF 62 | 63 | # Enable and start the Vault server agent 64 | systemctl enable vault 65 | systemctl start vault 66 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | description = "Name of the Vault cluster" 3 | default = "vault" 4 | } 5 | 6 | variable "tags" { 7 | description = "Extra tags to add to all resources created by this module" 8 | type = map(string) 9 | default = {} 10 | } 11 | 12 | variable "ami_id" { 13 | description = "AMI ID to launch the Vault auto scaling group with" 14 | } 15 | 16 | variable "instance_type" { 17 | description = "EC2 instance type for Vault instances" 18 | default = "t3.medium" 19 | } 20 | 21 | variable "min_instances" { 22 | description = "Minimum number of Vault instances in the auto scaling group" 23 | default = 3 24 | } 25 | 26 | variable "max_instances" { 27 | description = "Maximum number of Vault instances in the auto scaling group" 28 | default = 3 29 | } 30 | 31 | variable "vpc_id" { 32 | description = "ID of the AWS VPC to create the Vault cluster in" 33 | } 34 | 35 | variable "subnet_ids" { 36 | description = "List of subnet IDs to launch the Vault auto scaling group in" 37 | type = list(string) 38 | } 39 | 40 | variable "ssh_key_name" { 41 | description = "Name of the SSH keypair to use for the Vault EC2 instances" 42 | } 43 | 44 | variable "enable_termination_protection" { 45 | description = "Enable EC2 instance termination protection" 46 | default = true 47 | } 48 | 49 | variable "dynamodb_write_capacity" { 50 | description = "Write capacity for Vault's DynamoDB high availability backend" 51 | default = 5 52 | } 53 | 54 | variable "dynamodb_read_capacity" { 55 | description = "Read capacity for Vault's DynamoDB high availability backend" 56 | default = 5 57 | } 58 | 59 | variable "internal_lb" { 60 | description = "Whether to make the Vault load balancer internal" 61 | default = true 62 | } 63 | 64 | variable "zone_id" { 65 | description = "Route53 hosted zone ID to create the DNS entry in" 66 | } 67 | 68 | variable "domain_name" { 69 | description = "Domain name of DNS entry to create" 70 | } 71 | 72 | variable "acm_cert_arn" { 73 | description = "Cert ARN for NLB TLS termination" 74 | } 75 | 76 | variable "allowed_cidrs" { 77 | type = list(string) 78 | description = "List of CIDRs allowed to access Vault UI and API" 79 | default = [] 80 | } -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | --------------------------------------------------------------------------------