| # Copyright (c) HashiCorp, Inc. |
| # SPDX-License-Identifier: BUSL-1.1 |
| |
| terraform { |
| required_version = ">= 1.0" |
| |
| required_providers { |
| enos = { |
| source = "app.terraform.io/hashicorp-qti/enos" |
| } |
| |
| helm = { |
| source = "hashicorp/helm" |
| version = "2.6.0" |
| } |
| } |
| } |
| |
| locals { |
| helm_chart_settings = { |
| "server.ha.enabled" = "true" |
| "server.ha.replicas" = var.vault_instance_count |
| "server.ha.raft.enabled" = "true" |
| "server.affinity" = "" |
| "server.image.repository" = var.image_repository |
| "server.image.tag" = var.image_tag |
| "server.image.pullPolicy" = "Never" # Forces local image use |
| "server.resources.requests.cpu" = "50m" |
| "server.limits.memory" = "200m" |
| "server.limits.cpu" = "200m" |
| "server.ha.raft.config" = file("${abspath(path.module)}/raft-config.hcl") |
| "server.dataStorage.size" = "100m" |
| "server.logLevel" = var.vault_log_level |
| } |
| all_helm_chart_settings = var.ent_license == null ? local.helm_chart_settings : merge(local.helm_chart_settings, { |
| "server.extraEnvironmentVars.VAULT_LICENSE" = var.ent_license |
| }) |
| |
| vault_address = "http://127.0.0.1:8200" |
| |
| instance_indexes = [for idx in range(var.vault_instance_count) : tostring(idx)] |
| |
| leader_idx = local.instance_indexes[0] |
| followers_idx = toset(slice(local.instance_indexes, 1, var.vault_instance_count)) |
| } |
| |
| resource "helm_release" "vault" { |
| name = "vault" |
| |
| repository = "https://helm.releases.hashicorp.com" |
| chart = "vault" |
| |
| dynamic "set" { |
| for_each = local.all_helm_chart_settings |
| |
| content { |
| name = set.key |
| value = set.value |
| } |
| } |
| } |
| |
| data "enos_kubernetes_pods" "vault_pods" { |
| kubeconfig_base64 = var.kubeconfig_base64 |
| context_name = var.context_name |
| namespace = helm_release.vault.namespace |
| label_selectors = [ |
| "app.kubernetes.io/name=vault", |
| "component=server" |
| ] |
| |
| depends_on = [helm_release.vault] |
| } |
| |
| resource "enos_vault_init" "leader" { |
| bin_path = "/bin/vault" |
| vault_addr = local.vault_address |
| |
| key_shares = 5 |
| key_threshold = 3 |
| |
| transport = { |
| kubernetes = { |
| kubeconfig_base64 = var.kubeconfig_base64 |
| context_name = var.context_name |
| pod = data.enos_kubernetes_pods.vault_pods.pods[local.leader_idx].name |
| namespace = data.enos_kubernetes_pods.vault_pods.pods[local.leader_idx].namespace |
| } |
| } |
| } |
| |
| resource "enos_vault_unseal" "leader" { |
| bin_path = "/bin/vault" |
| vault_addr = local.vault_address |
| seal_type = "shamir" |
| unseal_keys = enos_vault_init.leader.unseal_keys_b64 |
| |
| transport = { |
| kubernetes = { |
| kubeconfig_base64 = var.kubeconfig_base64 |
| context_name = var.context_name |
| pod = data.enos_kubernetes_pods.vault_pods.pods[local.leader_idx].name |
| namespace = data.enos_kubernetes_pods.vault_pods.pods[local.leader_idx].namespace |
| } |
| } |
| |
| depends_on = [enos_vault_init.leader] |
| } |
| |
| // We need to manually join the followers since the join request must only happen after the leader |
| // has been initialized. We could use retry join, but in that case we'd need to restart the follower |
| // pods once the leader is setup. The default helm deployment configuration for an HA cluster as |
| // documented here: https://learn.hashicorp.com/tutorials/vault/kubernetes-raft-deployment-guide#configure-vault-helm-chart |
| // uses a liveness probe that automatically restarts nodes that are not healthy. This works well for |
| // clusters that are configured with auto-unseal as eventually the nodes would join and unseal. |
| resource "enos_remote_exec" "raft_join" { |
| for_each = local.followers_idx |
| |
| inline = [ |
| // asserts that vault is ready |
| "for i in 1 2 3 4 5; do vault status > /dev/null 2>&1 && break || sleep 5; done", |
| // joins the follower to the leader |
| "vault operator raft join http://vault-0.vault-internal:8200" |
| ] |
| |
| transport = { |
| kubernetes = { |
| kubeconfig_base64 = var.kubeconfig_base64 |
| context_name = var.context_name |
| pod = data.enos_kubernetes_pods.vault_pods.pods[each.key].name |
| namespace = data.enos_kubernetes_pods.vault_pods.pods[each.key].namespace |
| } |
| } |
| |
| depends_on = [enos_vault_unseal.leader] |
| } |
| |
| |
| resource "enos_vault_unseal" "followers" { |
| for_each = local.followers_idx |
| |
| bin_path = "/bin/vault" |
| vault_addr = local.vault_address |
| seal_type = "shamir" |
| unseal_keys = enos_vault_init.leader.unseal_keys_b64 |
| |
| transport = { |
| kubernetes = { |
| kubeconfig_base64 = var.kubeconfig_base64 |
| context_name = var.context_name |
| pod = data.enos_kubernetes_pods.vault_pods.pods[each.key].name |
| namespace = data.enos_kubernetes_pods.vault_pods.pods[each.key].namespace |
| } |
| } |
| |
| depends_on = [enos_remote_exec.raft_join] |
| } |
| |
| output "vault_root_token" { |
| value = enos_vault_init.leader.root_token |
| } |
| |
| output "vault_pods" { |
| value = data.enos_kubernetes_pods.vault_pods.pods |
| } |