diff --git a/hcp-consul-hcp-vault-ca/Makefile b/hcp-consul-hcp-vault-ca/Makefile new file mode 100644 index 0000000..d368f33 --- /dev/null +++ b/hcp-consul-hcp-vault-ca/Makefile @@ -0,0 +1,11 @@ +.PHONY: init +init: + bash set_region_for_makefile.sh && terraform init && terraform -chdir=./working-environment init + +.PHONY: apply +apply: + terraform apply && terraform -chdir=./working-environment apply + +.PHONY: destroy +destroy: + terraform -chdir=./working-environment destroy && terraform destroy diff --git a/hcp-consul-hcp-vault-ca/confirm.sh b/hcp-consul-hcp-vault-ca/confirm.sh new file mode 100644 index 0000000..f3a6c75 --- /dev/null +++ b/hcp-consul-hcp-vault-ca/confirm.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +export WORKBENCH=$(kubectl get pods -l app=tutorial -o json | jq -r ".items[0].metadata.name") +rc=$(echo $?) + +if [ $rc -ne 0 ] +then + echo "Workbench Pod not found. Use kubectl manually, or try again in a moment" + exit 1 +fi + +echo "Confirming access to Kubernetes resources" +kubectl exec -it "${WORKBENCH}" -- kubectl get pods --all-namespaces >/dev/null +krc=$(echo $?) +sleep 2 +echo "Confirming access to HCP Consul" +kubectl exec -it "${WORKBENCH}" -- consul members >/dev/null +crc=$(echo $?) +sleep 2 +echo "Confirming access to HCP Vault" +kubectl exec -it "${WORKBENCH}" -- vault status >/dev/null +vrc=$(echo $?) +if [ $krc -ne 0 ] || [ $crc -ne 0 ] || [ $vrc -ne 0 ] +then + echo "Access failed to one or more resources." + echo "Kubernetes returned: $krc" + echo "Consul returned $crc" + echo "Vault returned $vrc" + exit 1 +else + echo "Access confirmed!" + exit 0 +fi diff --git a/hcp-consul-hcp-vault-ca/data.tf b/hcp-consul-hcp-vault-ca/data.tf index ebd5266..6814dd8 100644 --- a/hcp-consul-hcp-vault-ca/data.tf +++ b/hcp-consul-hcp-vault-ca/data.tf @@ -1,2 +1,9 @@ # The identity of the AWS User running this terraform project -data "aws_caller_identity" "current" {} \ No newline at end of file +data "aws_caller_identity" "current" {} + +data "aws_availability_zones" "current" { + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} \ No newline at end of file diff --git a/hcp-consul-hcp-vault-ca/main.tf b/hcp-consul-hcp-vault-ca/main.tf index 41fb2a8..b0cf14d 100644 --- a/hcp-consul-hcp-vault-ca/main.tf +++ b/hcp-consul-hcp-vault-ca/main.tf @@ -1,30 +1,43 @@ +resource "random_string" "identifier" { + length = 6 + upper = false + special = false + numeric = true +} + +locals { + identifier = random_string.identifier.id + hvn_name = "${var.hcp_hvn_config.name}-${local.identifier}" + peer_id = "${var.hcp_peering_identifier}-${local.identifier}" + vault_cluster = "${var.hcp_vault_cluster_name}-${local.identifier}" + consul_datacenter = "${var.hcp_consul_datacenter_name}-${local.identifier}" + eks_name = "${var.cluster_and_vpc_info.name}-${local.identifier}" + policy_name = "${var.cluster_and_vpc_info.policy_name}-${local.identifier}" +} + # Builds the base VPC for the AWS EKS Cluster module "aws_vpc" { # Full URL due to this issue: https://github.com/VladRassokhin/intellij-hcl/issues/365 source = "registry.terraform.io/terraform-aws-modules/vpc/aws" version = "3.11.5" - name = var.cluster_and_vpc_info.vpc_name + name = local.identifier cidr = var.aws_cidr_block.allocation - azs = var.availability_zones + azs = data.aws_availability_zones.current.names private_subnets = var.aws_cidr_block.subnets.private public_subnets = var.aws_cidr_block.subnets.public enable_nat_gateway = true enable_vpn_gateway = false # internal-lb: Permits internal Load Balancer creation when a LoadBalancer type is passed. private_subnet_tags = { - "kubernetes.io/cluster/${var.cluster_and_vpc_info.name}" = "shared" - "kubernetes.io/role/internal-elb" = "1" + "kubernetes.io/cluster/${local.eks_name}" = "shared" + "kubernetes.io/role/internal-elb" = "1" } #elb: Permits Elastic Load Balancer creation when a LoadBalancer type is passed. public_subnet_tags = { - "kubernetes.io/cluster/${var.cluster_and_vpc_info.name}" = "shared" - "kubernetes.io/role/elb" = "1" + "kubernetes.io/cluster/${local.eks_name}" = "shared" + "kubernetes.io/role/elb" = "1" } - tags = { - Terraform = "true" - Environment = var.cluster_and_vpc_info.stage - } } # Creates required AWS Services to complete the peering relationship. If needed later @@ -41,7 +54,7 @@ module "hcp_networking_primitives" { source = "./modules/hcp_networking_primitives" cloud_provider = var.cloud_provider hcp_region = var.hcp_region - hvn_name = var.hcp_hvn_config.name + hvn_name = local.hvn_name cidr_block = var.hcp_hvn_config.allocation } @@ -56,7 +69,7 @@ module "hcp_networking" { aws_vpc_cidr_block = var.aws_cidr_block.allocation hvn_link = module.hcp_networking_primitives.hvn_link hvn_name = module.hcp_networking_primitives.hcp_vpn_id - hvn_peering_identifier = var.hcp_peering_identifier + hvn_peering_identifier = local.peer_id hcp_hvn_cidr_block = var.hcp_hvn_config.allocation public_route_table_ids = module.aws_vpc.public_route_table_ids private_route_table_ids = module.aws_vpc.private_route_table_ids @@ -66,8 +79,8 @@ module "hcp_networking" { module "hcp_applications" { source = "./modules/hcp_applications" hvn_id = module.hcp_networking_primitives.hcp_vpn_id - consul_cluster_datacenter = var.hcp_consul_datacenter_name - vault_cluster_name = var.hcp_vault_cluster_name + consul_cluster_datacenter = local.consul_datacenter + vault_cluster_name = local.vault_cluster hcp_consul_tier = var.hcp_hvn_config.consul_tier hcp_vault_tier = var.hcp_hvn_config.vault_tier } @@ -77,11 +90,9 @@ module "eks" { # Full URL due to this issue: https://github.com/VladRassokhin/intellij-hcl/issues/365 source = "registry.terraform.io/terraform-aws-modules/eks/aws" version = "18.9.0" - cluster_name = var.cluster_and_vpc_info.name + cluster_name = local.eks_name cluster_endpoint_private_access = true cluster_endpoint_public_access = true - - cluster_addons = { coredns = { resolve_conflicts = "OVERWRITE" @@ -117,35 +128,29 @@ module "eks" { tags = { Environment = var.cluster_and_vpc_info.stage } - } # Update local environment's kubeconfig file. resource "null_resource" "update_kubeconfig" { provisioner "local-exec" { - # Create empty kubeconfig if it doesn't exist. If it does exist, touch does nothing, but back up the config in either case and use a kubeconfig that is unique for this tutorial. Is deleted during terraform destroy - command = "mv ~/.kube/config ~/.kube/config.bkp && aws eks --region ${var.cluster_and_vpc_info.region} update-kubeconfig --name ${module.eks.cluster_id} --alias ${var.cluster_and_vpc_info.name}" + command = "aws eks --region ${var.region} update-kubeconfig --name ${module.eks.cluster_id} --alias ${module.eks.cluster_id}" } depends_on = [module.eks] } -# This module's resources only run when `terraform destroy` is invoked by the user. A "start_cleanup" variable -# is used to make sure cleanup scripts are not run during a follow up terraform apply, and only during terraform destroy. -# This is passed as TF_VAR_start_cleanup=true to the main project by the reader at the end of the tutorial: -# export TF_VAR_run_cleanup=true; terraform destroy -auto-approve module "cleanup" { source = "./modules/cleanup" vpc_id = module.aws_vpc.vpc_id region = var.region - cluster_name = var.cluster_and_vpc_info.name + cluster_name = local.eks_name start_cleanup = var.run_cleanup } # This module does all the OIDC magic for ServiceAccount -> IAM Role mapping module "iam_role_for_service_accounts" { source = "registry.terraform.io/terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = lower(var.cluster_and_vpc_info.name) + role_name = local.eks_name version = "4.14.0" oidc_providers = { @@ -162,7 +167,7 @@ module "eks_iam" { source = "./modules/iam" cluster_arn = module.eks.cluster_arn description = var.cluster_and_vpc_info.policy_description - policy_name = var.cluster_and_vpc_info.policy_name + policy_name = local.policy_name role_name = module.iam_role_for_service_accounts.iam_role_name } @@ -186,12 +191,12 @@ consul_secret_id ="${module.hcp_applications.consul_root_token_secret_id}" vault_addr="${module.hcp_applications.vault_cluster_host}" vault_namespace="${var.hcp_vault_default_namespace}" vault_token="${module.hcp_applications.vault_admin_token}" -kube_context="${var.cluster_and_vpc_info.name}" +kube_context="${local.eks_name}" role_arn="${module.iam_role_for_service_accounts.iam_role_arn}" profile_name="${var.profile_name}" cluster_service_account_name="${var.kube_service_account_name}" -cluster_name="${var.cluster_and_vpc_info.name}" -cluster_region="${var.cluster_and_vpc_info.region}" +cluster_name="${local.eks_name}" +cluster_region="${var.region}" +consul_datacenter="${module.hcp_applications.consul_datacenter}" CONFIGURATION } - diff --git a/hcp-consul-hcp-vault-ca/modules/hcp_applications/outputs.tf b/hcp-consul-hcp-vault-ca/modules/hcp_applications/outputs.tf index adae22d..62b19e6 100644 --- a/hcp-consul-hcp-vault-ca/modules/hcp_applications/outputs.tf +++ b/hcp-consul-hcp-vault-ca/modules/hcp_applications/outputs.tf @@ -3,6 +3,11 @@ output "consul_cluster_host" { value = hcp_consul_cluster.server.consul_private_endpoint_url } +# Consul datacenter name +output "consul_datacenter" { + value = hcp_consul_cluster.server.datacenter +} + # The Consul config file to setup the working environment output "consul_config_file" { value = hcp_consul_cluster.server.consul_config_file diff --git a/hcp-consul-hcp-vault-ca/modules/kubernetes/main.tf b/hcp-consul-hcp-vault-ca/modules/kubernetes/main.tf index e639d1f..0ec340e 100644 --- a/hcp-consul-hcp-vault-ca/modules/kubernetes/main.tf +++ b/hcp-consul-hcp-vault-ca/modules/kubernetes/main.tf @@ -43,6 +43,21 @@ resource "kubernetes_config_map" "aws_profile_config" { } } +resource "kubernetes_config_map" "consul_values" { + metadata { + name = "consul-values.yaml" + } + data = { + "consul-values.yaml" = templatefile("${path.module}/template_scripts/consul-values.tftpl", { + vault_addr = var.vault_addr + # remove "https://" from the URL for correct format in helm chart + hcp_consul_addr = substr(var.consul_http_addr, 8, -1) + kube_control_plane = var.consul_k8s_api_aws, + datacenter = var.consul_datacenter + }) + } +} + # Create a service account for this pod resource "kubernetes_service_account" "tutorial" { metadata { @@ -125,6 +140,13 @@ resource "kubernetes_deployment" "workingEnvironment" { default_mode = var.aws_creds_options.file_permissions } } + volume { + name = "consul-values" + config_map { + name = "consul-values.yaml" + default_mode = "0755" + } + } volume { name = var.aws_profile_config_options.volume_name config_map { @@ -192,6 +214,12 @@ resource "kubernetes_deployment" "workingEnvironment" { sub_path = var.aws_creds_options.config_map_filename read_only = true } + volume_mount { + mount_path = "/consul-values.yaml" + name = "consul-values" + sub_path = "consul-values.yaml" + read_only = false + } volume_mount { mount_path = var.aws_profile_config_options.mount_path name = var.aws_profile_config_options.volume_name diff --git a/hcp-consul-hcp-vault-ca/modules/kubernetes/template_scripts/consul-values.tftpl b/hcp-consul-hcp-vault-ca/modules/kubernetes/template_scripts/consul-values.tftpl new file mode 100644 index 0000000..2d40d7d --- /dev/null +++ b/hcp-consul-hcp-vault-ca/modules/kubernetes/template_scripts/consul-values.tftpl @@ -0,0 +1,64 @@ +global: + name: consul + enabled: false + datacenter: ${datacenter} + image: hashicorp/consul-enterprise:1.11.4-ent + enableConsulNamespaces: true + acls: + manageSystemACLs: true + bootstrapToken: + secretName: consul-bootstrap-token + secretKey: token + gossipEncryption: + secretName: consul-gossip-key + secretKey: key + tls: + enabled: true + enableAutoEncrypt: true + caCert: + secretName: consul-ca-cert + secretKey: tls.crt +externalServers: + enabled: true + hosts: + - ${hcp_consul_addr} + httpsPort: 443 + useSystemRoots: true + k8sAuthMethodHost: ${kube_control_plane} +client: + enabled: true + grpc: true + join: + - ${hcp_consul_addr} +connectInject: + enabled: true + envoyExtraArgs: "--component-log-level upstream:debug,http:debug,router:debug,config:debug" + aclBindingRuleSelector: '' + consulNamespaces: + mirroringK8S: true + transparentProxy: + defaultEnabled: false +controller: + enabled: true +secretsBackend: + vault: + enabled: true + # https://www.consul.io/docs/k8s/helm#v-global-secretsbackend-vault-consulclientrole + consulClientRole: consul-consul-client + consulCARole: consul-consul-client + # https://www.consul.io/docs/k8s/helm#v-global-secretsbackend-vault-connectca + connectCA: + address: ${vault_addr} + rootPKIPath: /connect_root + intermediatePKIPath: /connect_inter + additionalConfig: | + { + "connect": [{ + "ca_config": [{ + "leaf_cert_ttl": "72h", + "intermediate_cert_ttl": "8760h", + "rotation_period": "2160h", + "namespace": "admin" + }] + }] + } \ No newline at end of file diff --git a/hcp-consul-hcp-vault-ca/modules/kubernetes/variables.tf b/hcp-consul-hcp-vault-ca/modules/kubernetes/variables.tf index 3eaa759..422d9d8 100644 --- a/hcp-consul-hcp-vault-ca/modules/kubernetes/variables.tf +++ b/hcp-consul-hcp-vault-ca/modules/kubernetes/variables.tf @@ -141,3 +141,8 @@ variable "kube_context" { description = "The name of the kube context to set in the config file for kubectl" } +variable "consul_datacenter" { + type = string + description = "The name of the Consul datacenter" +} + diff --git a/hcp-consul-hcp-vault-ca/provider.tf b/hcp-consul-hcp-vault-ca/provider.tf index f9b2f51..e4713c4 100644 --- a/hcp-consul-hcp-vault-ca/provider.tf +++ b/hcp-consul-hcp-vault-ca/provider.tf @@ -14,9 +14,6 @@ terraform { provider "aws" { region = var.region - default_tags { - tags = var.default_tags - } } provider "hcp" {} diff --git a/hcp-consul-hcp-vault-ca/set_region_for_makefile.sh b/hcp-consul-hcp-vault-ca/set_region_for_makefile.sh new file mode 100644 index 0000000..96fc025 --- /dev/null +++ b/hcp-consul-hcp-vault-ca/set_region_for_makefile.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +TF_VAR_region=$(read -p "Enter the AWS Region to use for this project (e.g. us-east-1) > ") + +if [ ! $TF_VAR_region ]; then + # Set val if user skips entering any intput, and default to us-east-1 + TF_VAR_region="us-east-1" + echo "region=\"${TF_VAR_region}\"" > terraform.tfvars + echo "hcp_region=\"${TF_VAR_region}\"" >> terraform.tfvars +fi + +exit 0 \ No newline at end of file diff --git a/hcp-consul-hcp-vault-ca/variables.tf b/hcp-consul-hcp-vault-ca/variables.tf index 66738e0..89b7c2b 100644 --- a/hcp-consul-hcp-vault-ca/variables.tf +++ b/hcp-consul-hcp-vault-ca/variables.tf @@ -1,6 +1,27 @@ variable "region" { - default = "us-east-1" - description = "Region in which to run this terraform code for the AWS Provider" + description = "AWS Region to deploy this terraform code" + validation { + condition = contains([ + "eu-north-1", + "ap-south-1", + "eu-west-3", + "eu-west-2", + "eu-west-1", + "ap-northeast-3", + "ap-northeast-2", + "ap-northeast-1", + "sa-east-1", + "ca-central-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2" + ], var.region) + error_message = "The name of the region you entered is invalid. Please try again. \n Reference: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html ." + } } variable "node_group_configuration" { @@ -16,12 +37,6 @@ variable "node_group_configuration" { } } -variable "availability_zones" { - description = "Availability Zones for the EKS Cluster deployed in main.tf" - default = ["us-east-1a", "us-east-1b", "us-east-1c"] - type = list(string) -} - variable "default_tags" { type = map(string) description = "Default tags to pass to AWS resources" @@ -60,7 +75,6 @@ variable "hcp_peering_identifier" { variable "hcp_region" { description = "HCP region for HCP-created resources" type = string - default = "us-east-1" } variable "cloud_provider" { @@ -83,7 +97,6 @@ variable "hcp_vault_cluster_name" { variable "cluster_and_vpc_info" { default = { - region = "us-east-1" name = "tutorialCluster" vpc_name = "hcpTutorialAwsVpc" policy_name = "workingenvironmentpolicy" diff --git a/hcp-consul-hcp-vault-ca/working-environment/main.tf b/hcp-consul-hcp-vault-ca/working-environment/main.tf index 0d07a5a..0914f5b 100644 --- a/hcp-consul-hcp-vault-ca/working-environment/main.tf +++ b/hcp-consul-hcp-vault-ca/working-environment/main.tf @@ -18,4 +18,5 @@ module "kubernetes" { cluster_service_account_name = var.cluster_service_account_name cluster_name = var.cluster_name cluster_region = var.cluster_region + consul_datacenter = var.consul_datacenter } \ No newline at end of file diff --git a/hcp-consul-hcp-vault-ca/working-environment/variables.tf b/hcp-consul-hcp-vault-ca/working-environment/variables.tf index af09c2b..024ecf5 100644 --- a/hcp-consul-hcp-vault-ca/working-environment/variables.tf +++ b/hcp-consul-hcp-vault-ca/working-environment/variables.tf @@ -87,4 +87,9 @@ variable "cluster_region" { variable "cluster_service_account_name" { type = string description = "Service account name for the Pod" +} + +variable "consul_datacenter" { + type = string + description = "Name of the HCP Consul datacenter" } \ No newline at end of file