diff --git a/get_sha.sh b/infra/get_sha.sh similarity index 100% rename from get_sha.sh rename to infra/get_sha.sh diff --git a/infra/ipa.tf b/infra/ipa.tf new file mode 100644 index 00000000..c85fc471 --- /dev/null +++ b/infra/ipa.tf @@ -0,0 +1,38 @@ +data "aws_route53_zone" "aws-zone" { + name = lower("${var.aws_account}.indico.io") +} + +output "ns" { + value = data.aws_route53_zone.aws-zone.name_servers +} + +data "external" "git_information" { + program = ["sh", "${path.module}/get_sha.sh"] +} + +output "git_sha" { + value = data.external.git_information.result.sha +} + +output "git_branch" { + value = data.external.git_information.result.branch +} + + + +output "smoketest_chart_version" { + value = "${path.module}/validate_chart.sh terraform-smoketests 0.1.0-${data.external.git_information.result.branch}-${substr(data.external.git_information.result.sha, 0, 8)}" +} + +output "local_registry_password" { + value = htpasswd_password.hash.bcrypt +} + +output "local_registry_username" { + value = "local-user" +} + +#output "zerossl" { +# sensitive = true +# value = data.vault_kv_secret_v2.zerossl_data.data_json +#} diff --git a/infra/locals.tf b/infra/locals.tf new file mode 100644 index 00000000..7d3ab2a9 --- /dev/null +++ b/infra/locals.tf @@ -0,0 +1,5 @@ +# Generating all of our values from the variables + +locals { + dns_name = var.domain_host == "" ? lower("${var.label}.${var.region}.${var.aws_account}.${var.domain_suffix}") : var.domain_host +} diff --git a/infra/main.tf b/infra/main.tf new file mode 100644 index 00000000..8f2ce1f1 --- /dev/null +++ b/infra/main.tf @@ -0,0 +1,375 @@ +module "infra" { + source = "../modules/aws/infra" + + providers = { + aws = aws + aws.dns-control = aws.dns-control + kubernetes = kubernetes + } + + aws_account = var.aws_account + region = var.region + label = var.label + name = var.name + additional_tags = var.additional_tags + default_tags = var.default_tags + dns_name = local.dns_name + is_alternate_account_domain = var.is_alternate_account_domain + domain_suffix = var.domain_suffix + domain_host = var.domain_host + + direct_connect = var.direct_connect + vpc_cidr = var.vpc_cidr + private_subnet_cidrs = var.private_subnet_cidrs + public_subnet_cidrs = var.public_subnet_cidrs + subnet_az_zones = var.subnet_az_zones + + existing_kms_key = var.existing_kms_key + + sqs_sns = var.sqs_sns + + submission_expiry = var.submission_expiry + uploads_expiry = var.uploads_expiry + include_rox = var.include_rox + include_pgbackup = var.include_pgbackup + + include_efs = var.include_efs + include_fsx = var.include_fsx + local_registry_enabled = var.local_registry_enabled + storage_capacity = var.storage_capacity + per_unit_storage_throughput = var.per_unit_storage_throughput + + k8s_version = var.k8s_version + node_groups = var.node_groups + az_count = var.az_count + cluster_node_policies = var.cluster_node_policies + eks_cluster_iam_role = var.eks_cluster_iam_role + snapshot_id = var.snapshot_id + performance_bucket = var.performance_bucket + + oidc_enabled = var.oidc_enabled + + enable_waf = var.enable_waf + + eks_addon_version_guardduty = var.eks_addon_version_guardduty + + aws_primary_dns_role_arn = var.aws_primary_dns_role_arn + + monitoring_enabled = var.monitoring_enabled +} + +resource "null_resource" "stage_one" { + depends_on = [module.infra] + + provisioner "local-exec" { + command = "echo Infrastructure Creation complete, moving on to general charts" + } +} + +module "aws_helm" { + source = "../modules/aws/helm" + + depends_on = [null_resource.stage_one] + + providers = { + kubernetes = kubernetes + helm = helm + } + + dns_name = local.dns_name + k8s_dashboard_chart_version = var.k8s_dashboard_chart_version + ipa_repo = var.ipa_repo + use_static_ssl_certificates = var.use_static_ssl_certificates + ssl_static_secret_name = var.ssl_static_secret_name +} + +module "common_helm" { + source = "../modules/common/infra" + + depends_on = [null_resource.stage_one] + + providers = { + github = github + helm = helm + kubectl = kubectl + vault = vault + } + + aws_account = var.aws_account + region = var.region + label = var.label + dns_name = local.dns_name + + argo_enabled = var.argo_enabled + argo_repo = var.argo_repo + argo_branch = var.argo_branch + argo_path = var.argo_path + message = var.message + + harbor_pull_secret_b64 = var.harbor_pull_secret_b64 + vault_mount_path = var.vault_mount_path + + ipa_repo = var.ipa_repo + infra_crds_version = var.infra_crds_version + infra-crds-values-yaml-b64 = var.crds-values-yaml-b64 + infra_pre_reqs_version = var.infra_pre_reqs_version + infra-pre-reqs-values-yaml-b64 = var.pre-reqs-values-yaml-b64 + + include_efs = var.include_efs + efs_filesystem_id = module.infra.efs_filesystem_id + include_fsx = var.include_fsx + security_group_id = var.include_fsx == true ? tolist(module.infra.fsx-rwx.security_group_ids)[0] : "" + fsx_rwx_subnet_id = module.infra.fsx_storage_fsx_rwx_subnet_id + local_registry_enabled = var.local_registry_enabled + use_static_ssl_certificates = var.use_static_ssl_certificates + is_alternate_account_domain = var.is_alternate_account_domain + aws_primary_dns_role_arn = var.aws_primary_dns_role_arn +} + +module "local-registry" { + source = "../modules/common/local-registry" + + count = var.local_registry_enabled ? 1 : 0 + + depends_on = [ + null_resource.stage_one, + module.common_helm + ] + + providers = { + kubernetes = kubernetes + helm = helm + } + + aws_account = var.aws_account + argo_enabled = var.argo_enabled + ipa_repo = var.ipa_repo + local_registry_version = var.local_registry_version + dns_name = local.dns_name + efs_filesystem_id = module.infra.local_registry_efs_filesystem_id + htpasswd_bcrypt = htpasswd_password.hash.bcrypt + general_password = random_password.password.result +} + +module "vault_secrets_operator" { + source = "../modules/common/vault-secrets-operator-setup" + + depends_on = [ + null_resource.stage_one, + module.common_helm + ] + + providers = { + kubernetes = kubernetes + helm = helm + vault = vault + } + + vault_address = var.vault_address + account = var.aws_account + region = var.region + name = var.label + kubernetes_host = module.infra.kube_host + external_secrets_version = var.external_secrets_version +} + +module "monitoring" { + source = "../modules/common/monitoring" + + count = var.monitoring_enabled ? 1 : 0 + + depends_on = [ + null_resource.stage_one, + module.vault_secrets_operator + ] + + providers = { + helm = helm + kubectl = kubectl + kubectl.thanos-kubectl = kubectl.thanos-kubectl + random = random + } + + aws_account = var.aws_account + region = var.region + label = var.label + + ipa_repo = var.ipa_repo + monitoring_version = var.monitoring_version + keda_version = var.keda_version + opentelemetry-collector_version = var.opentelemetry-collector_version + + thanos_enabled = var.thanos_enabled + argo_enabled = var.argo_enabled + vault_address = var.vault_address + + alerting_enabled = var.alerting_enabled + alerting_slack_enabled = var.alerting_slack_enabled + alerting_pagerduty_enabled = var.alerting_pagerduty_enabled + alerting_email_enabled = var.alerting_email_enabled + alerting_slack_token = var.alerting_slack_token + alerting_slack_channel = var.alerting_slack_channel + alerting_pagerduty_integration_key = var.alerting_pagerduty_integration_key + alerting_email_from = var.alerting_email_from + alerting_email_to = var.alerting_email_to + alerting_email_username = var.alerting_email_username + alerting_email_password = var.alerting_email_password + + use_static_ssl_certificates = var.use_static_ssl_certificates + ssl_static_secret_name = var.ssl_static_secret_name + + dns_name = local.dns_name +} + +resource "null_resource" "stage_two" { + depends_on = [ + module.aws_helm, + module.common_helm, + module.local-registry, + module.vault_secrets_operator, + module.monitoring, + ] + + provisioner "local-exec" { + command = "echo General Indico Cluster configuration complete, moving on to product deployment" + } +} + +module "argo_registration" { + count = var.argo_enabled == true ? 1 : 0 + + depends_on = [ + null_resource.stage_two, + ] + + providers = { + kubernetes = kubernetes, + argocd = argocd + } + + source = "app.terraform.io/indico/indico-argo-registration/mod" + version = "1.2.1" + cluster_name = var.label + region = var.region + argo_password = var.argo_password + argo_username = var.argo_username + argo_namespace = var.argo_namespace + argo_host = var.argo_host + account = var.aws_account + cloud_provider = "aws" + argo_github_team_admin_group = var.argo_github_team_owner + endpoint = module.infra.kube_host + ca_data = base64decode(module.infra.kube_ca_certificate) + indico_dev_cluster = var.aws_account == "Indico-Dev" +} + +module "intake" { + source = "../modules/common/intake" + + depends_on = [ + null_resource.stage_two, + module.argo_registration + ] + + providers = { + argocd = argocd + github = github + helm = helm + kubectl = kubectl + time = time + } + + dns_name = local.dns_name + aws_account = var.aws_account + region = var.region + label = var.label + + use_static_ssl_certificates = var.use_static_ssl_certificates + ssl_static_secret_name = var.ssl_static_secret_name + is_alternate_account_domain = var.is_alternate_account_domain + aws_primary_dns_role_arn = var.aws_primary_dns_role_arn + + ipa_repo = var.ipa_repo + argo_enabled = var.argo_enabled + argo_namespace = var.argo_namespace + argo_repo = var.argo_repo + argo_branch = var.argo_branch + argo_path = var.argo_path + message = var.message + ipa_pre_reqs_version = var.ipa_pre_reqs_version + pre-reqs-values-yaml-b64 = var.pre-reqs-values-yaml-b64 + ipa_version = var.ipa_version + k8s_version = var.k8s_version + ipa_values = var.ipa_values + az_count = var.az_count + key_arn = module.infra.kms_key_arn + s3_role_id = module.infra.s3_role_id + pgbackup_s3_bucket_name = module.infra.pgbackup_s3_bucket_name + enable_waf = var.enable_waf + waf_arn = module.infra.wafv2_arn + use_acm = var.use_acm + acm_arn = module.infra.acm_arn + kubernetes_host = module.infra.kube_host + indico_vpc_id = module.infra.network.indico_vpc_id + public_subnet_ids = module.infra.network.public_subnet_ids + argo_project_name = module.argo_registration[0].argo_project_name + local_registry_enabled = var.local_registry_enabled + on_prem_test = var.on_prem_test + include_efs = var.include_efs + efs_filesystem_id = module.infra.efs_filesystem_id + include_fsx = var.include_fsx + fsx_rwx = module.infra.fsx-rwx + monitoring_password = module.monitoring[0].monitoring-password + + ipa_smoketest_values = var.ipa_smoketest_values + ipa_smoketest_repo = var.ipa_smoketest_repo + ipa_smoketest_version = var.ipa_smoketest_version + ipa_smoketest_enabled = var.ipa_smoketest_enabled + + restore_snapshot_enabled = var.restore_snapshot_enabled + restore_snapshot_name = var.restore_snapshot_name +} + +/* +module "applications" { + source = "../modules/common/applications" + + depends_on = [ + null_resource.stage_two, + ] + + providers = { + kubernetes = kubernetes + helm = helm + vault = vault + } + + vault_address = var.vault_address + account = var.aws_account + region = var.region + name = var.label + kubernetes_host = module.infra.kube_host + external_secrets_version = var.external_secrets_version +} + +module "terraform_smoketest" { + source = "../modules/common/terraform_smoketest" + + depends_on = [ + null_resource.stage_two, + ] + + providers = { + kubernetes = kubernetes + helm = helm + vault = vault + } + + vault_address = var.vault_address + account = var.aws_account + region = var.region + name = var.label + kubernetes_host = module.infra.kube_host + external_secrets_version = var.external_secrets_version +} +*/ diff --git a/infra/outputs.tf b/infra/outputs.tf new file mode 100644 index 00000000..332891ee --- /dev/null +++ b/infra/outputs.tf @@ -0,0 +1,113 @@ + +output "api_models_s3_bucket_name" { + description = "Name of the api-models s3 bucket" + value = module.infra.api_models_s3_bucket_name +} + +output "data_s3_bucket_name" { + description = "Name of the data s3 bucket" + value = module.infra.data_s3_bucket_name +} + +output "s3_role_id" { + description = "ID of the S3 role" + value = module.infra.s3_role_id +} + + +output "efs_filesystem_id" { + description = "ID of the EFS filesystem" + value = module.infra.efs_filesystem_id +} + +output "fsx-rwx" { + description = "Read write filesystem" + value = module.infra.fsx-rwx +} + +output "fsx-rox" { + description = "Read only filesystem" + value = module.infra.fsx-rox +} + +output "key_pem" { + value = module.infra.key_pem + description = "Generated private key for key pair" + sensitive = true +} + +output "fsx_storage_fsx_rwx_dns_name" { + value = module.infra.fsx_storage_fsx_rwx_dns_name +} + +output "fsx_storage_fsx_rwx_mount_name" { + value = module.infra.fsx_storage_fsx_rwx_mount_name +} + +output "fsx_storage_fsx_rwx_volume_handle" { + value = module.infra.fsx_storage_fsx_rwx_volume_handle +} + +output "fsx_storage_fsx_rwx_subnet_id" { + value = module.infra.fsx_storage_fsx_rwx_subnet_id +} + +output "cluster_name" { + value = var.label +} + +output "cluster_region" { + value = var.region +} + +output "dns_name" { + value = local.dns_name +} + +# output "kubeconfig" { +# value = module.cluster.kubectl_config +# } + + +output "kube_host" { + value = module.infra.kube_host +} + +output "kube_ca_certificate" { + value = module.infra.kube_ca_certificate + +} +output "kube_token" { + sensitive = true + value = module.infra.kube_token +} + +#output "harness_delegate_name" { +# value = var.harness_delegate == true && length(module.harness_delegate) > 0 ? module.harness_delegate[0].delegate_name : "" +#} + +output "ipa_version" { + value = var.ipa_version +} + +output "argo_branch" { + value = var.argo_branch +} + +output "argo_path" { + value = var.argo_path +} + +output "argo_repo" { + value = var.argo_repo +} + +output "monitoring_enabled" { + value = var.monitoring_enabled +} + + +#output "harbor-api-token" { +# sensitive = true +# value = var.argo_enabled == true ? jsondecode(data.vault_kv_secret_v2.harbor-api-token[0].data_json)["bearer_token"] : "" +#} diff --git a/infra/providers.tf b/infra/providers.tf new file mode 100644 index 00000000..e7d1f314 --- /dev/null +++ b/infra/providers.tf @@ -0,0 +1,195 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.14.0" + } + time = { + source = "hashicorp/time" + version = "0.9.1" + } + keycloak = { + source = "mrparkers/keycloak" + version = "4.0.1" + } + argocd = { + source = "oboukili/argocd" + version = "6.0.2" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.23.0" + } + kubectl = { + source = "gavinbunney/kubectl" + version = "1.14.0" + } + helm = { + source = "hashicorp/helm" + version = ">= 2.11.0" + } + random = { + source = "hashicorp/random" + version = "~>3.5.1" + } + github = { + source = "integrations/github" + version = "5.34.0" + } + vault = { + source = "hashicorp/vault" + version = "3.22.0" + } + snowflake = { + source = "Snowflake-Labs/snowflake" + version = "0.73.0" + } + htpasswd = { + source = "loafoe/htpasswd" + version = "1.0.4" + } + } +} + +provider "time" {} + +provider "keycloak" { + initial_login = false +} + +provider "vault" { + address = var.vault_address + skip_child_token = true + auth_login_userpass { + username = var.vault_username + password = var.vault_password + } +} + +provider "github" { + token = var.git_pat + owner = "IndicoDataSolutions" +} + +provider "random" {} + +provider "aws" { + access_key = var.aws_access_key + secret_key = var.aws_secret_key + region = var.region + default_tags { + tags = var.default_tags + } +} + +provider "aws" { + access_key = var.is_alternate_account_domain == "true" ? var.indico_aws_access_key_id : var.aws_access_key + secret_key = var.is_alternate_account_domain == "true" ? var.indico_aws_secret_access_key : var.aws_secret_key + region = var.region + alias = "dns-control" + default_tags { + tags = var.default_tags + } +} + +provider "azurerm" { + features {} + alias = "readapi" + client_id = var.azure_readapi_client_id + client_secret = var.azure_readapi_client_secret + subscription_id = var.azure_readapi_subscription_id + tenant_id = var.azure_readapi_tenant_id +} + +provider "azurerm" { + features {} + alias = "indicoio" + client_id = var.azure_indico_io_client_id + client_secret = var.azure_indico_io_client_secret + subscription_id = var.azure_indico_io_subscription_id + tenant_id = var.azure_indico_io_tenant_id +} + +data "vault_kv_secret_v2" "terraform-snowflake" { + mount = var.terraform_vault_mount_path + name = "snowflake" +} + +provider "snowflake" { + role = "ACCOUNTADMIN" + username = var.snowflake_username + account = var.snowflake_account + region = var.snowflake_region + private_key = jsondecode(data.vault_kv_secret_v2.terraform-snowflake.data_json)["snowflake_private_key"] +} + +provider "htpasswd" {} + +# argo +provider "argocd" { + server_addr = var.argo_host + username = var.argo_username + password = var.argo_password +} + +provider "kubernetes" { + host = module.infra.kube_host + cluster_ca_certificate = base64decode(module.infra.kube_ca_certificate) + exec { + api_version = "client.authentication.k8s.io/v1beta1" + args = ["eks", "get-token", "--cluster-name", var.label] + command = "aws" + } +} + +provider "kubectl" { + host = module.infra.kube_host + cluster_ca_certificate = base64decode(module.infra.kube_ca_certificate) + load_config_file = false + exec { + api_version = "client.authentication.k8s.io/v1beta1" + args = ["eks", "get-token", "--cluster-name", var.label] + command = "aws" + } +} + +provider "helm" { + debug = true + kubernetes { + host = module.infra.kube_host + cluster_ca_certificate = base64decode(module.infra.kube_ca_certificate) + exec { + api_version = "client.authentication.k8s.io/v1beta1" + args = ["eks", "get-token", "--cluster-name", var.label] + command = "aws" + } + } +} + + +provider "aws" { + access_key = var.indico_devops_aws_access_key_id + secret_key = var.indico_devops_aws_secret_access_key + region = var.indico_devops_aws_region + alias = "aws-indico-devops" +} + +data "aws_eks_cluster" "thanos" { + count = var.thanos_enabled == true ? 1 : 0 + name = var.thanos_cluster_name + provider = aws.aws-indico-devops +} + +data "aws_eks_cluster_auth" "thanos" { + count = var.thanos_enabled == true ? 1 : 0 + name = var.thanos_cluster_name + provider = aws.aws-indico-devops +} + +provider "kubectl" { + alias = "thanos-kubectl" + host = var.thanos_enabled == true ? data.aws_eks_cluster.thanos[0].endpoint : "" + cluster_ca_certificate = var.thanos_enabled == true ? base64decode(data.aws_eks_cluster.thanos[0].certificate_authority[0].data) : "" + token = var.thanos_enabled == true ? data.aws_eks_cluster_auth.thanos[0].token : "" + load_config_file = false +} diff --git a/infra/secretgen.tf b/infra/secretgen.tf new file mode 100644 index 00000000..d1376d82 --- /dev/null +++ b/infra/secretgen.tf @@ -0,0 +1,12 @@ +resource "random_password" "password" { + length = 12 +} + +resource "random_password" "salt" { + length = 8 +} + +resource "htpasswd_password" "hash" { + password = random_password.password.result + salt = random_password.salt.result +} \ No newline at end of file diff --git a/infra/tf-smoketest-variables.tf b/infra/tf-smoketest-variables.tf new file mode 100644 index 00000000..df0e5520 --- /dev/null +++ b/infra/tf-smoketest-variables.tf @@ -0,0 +1,145 @@ +resource "kubernetes_config_map" "terraform-variables" { + # this file is generated via pre-commit, DO NOT EDIT ! + #depends_on = [null_resource.wait-for-tf-cod-chart-build] + metadata { + name = "terraform-variables" + } + data = { + is_azure = "${jsonencode(var.is_azure)}" + is_aws = "${jsonencode(var.is_aws)}" + label = "${jsonencode(var.label)}" + environment = "${jsonencode(var.environment)}" + message = "${jsonencode(var.message)}" + applications = "${jsonencode(var.applications)}" + region = "${jsonencode(var.region)}" + direct_connect = "${jsonencode(var.direct_connect)}" + additional_tags = "${jsonencode(var.additional_tags)}" + default_tags = "${jsonencode(var.default_tags)}" + vpc_cidr = "${jsonencode(var.vpc_cidr)}" + public_ip = "${jsonencode(var.public_ip)}" + vpc_name = "${jsonencode(var.vpc_name)}" + private_subnet_cidrs = "${jsonencode(var.private_subnet_cidrs)}" + public_subnet_cidrs = "${jsonencode(var.public_subnet_cidrs)}" + subnet_az_zones = "${jsonencode(var.subnet_az_zones)}" + storage_gateway_size = "${jsonencode(var.storage_gateway_size)}" + existing_kms_key = "${jsonencode(var.existing_kms_key)}" + bucket_versioning = "${jsonencode(var.bucket_versioning)}" + submission_expiry = "${jsonencode(var.submission_expiry)}" + uploads_expiry = "${jsonencode(var.uploads_expiry)}" + name = "${jsonencode(var.name)}" + cluster_name = "${jsonencode(var.cluster_name)}" + k8s_version = "${jsonencode(var.k8s_version)}" + node_groups = "${jsonencode(var.node_groups)}" + node_bootstrap_arguments = "${jsonencode(var.node_bootstrap_arguments)}" + node_user_data = "${jsonencode(var.node_user_data)}" + node_disk_size = "${jsonencode(var.node_disk_size)}" + cluster_node_policies = "${jsonencode(var.cluster_node_policies)}" + kms_encrypt_secrets = "${jsonencode(var.kms_encrypt_secrets)}" + enable_readapi = "${jsonencode(var.enable_readapi)}" + azure_readapi_client_id = "${jsonencode(var.azure_readapi_client_id)}" + azure_readapi_subscription_id = "${jsonencode(var.azure_readapi_subscription_id)}" + azure_readapi_tenant_id = "${jsonencode(var.azure_readapi_tenant_id)}" + azure_indico_io_client_id = "${jsonencode(var.azure_indico_io_client_id)}" + azure_indico_io_subscription_id = "${jsonencode(var.azure_indico_io_subscription_id)}" + azure_indico_io_tenant_id = "${jsonencode(var.azure_indico_io_tenant_id)}" + eks_cluster_iam_role = "${jsonencode(var.eks_cluster_iam_role)}" + eks_cluster_nodes_iam_role = "${jsonencode(var.eks_cluster_nodes_iam_role)}" + storage_capacity = "${jsonencode(var.storage_capacity)}" + deletion_protection_enabled = "${jsonencode(var.deletion_protection_enabled)}" + skip_final_snapshot = "${jsonencode(var.skip_final_snapshot)}" + per_unit_storage_throughput = "${jsonencode(var.per_unit_storage_throughput)}" + az_count = "${jsonencode(var.az_count)}" + snapshot_id = "${jsonencode(var.snapshot_id)}" + include_rox = "${jsonencode(var.include_rox)}" + aws_account = "${jsonencode(var.aws_account)}" + argo_enabled = "${jsonencode(var.argo_enabled)}" + argo_host = "${jsonencode(var.argo_host)}" + argo_repo = "${jsonencode(var.argo_repo)}" + argo_branch = "${jsonencode(var.argo_branch)}" + argo_namespace = "${jsonencode(var.argo_namespace)}" + argo_path = "${jsonencode(var.argo_path)}" + argo_github_team_owner = "${jsonencode(var.argo_github_team_owner)}" + ipa_repo = "${jsonencode(var.ipa_repo)}" + ipa_version = "${jsonencode(var.ipa_version)}" + ipa_smoketest_values = "${jsonencode(var.ipa_smoketest_values)}" + ipa_smoketest_repo = "${jsonencode(var.ipa_smoketest_repo)}" + ipa_smoketest_version = "${jsonencode(var.ipa_smoketest_version)}" + ipa_smoketest_enabled = "${jsonencode(var.ipa_smoketest_enabled)}" + monitoring_version = "${jsonencode(var.monitoring_version)}" + ipa_pre_reqs_version = "${jsonencode(var.ipa_pre_reqs_version)}" + ipa_crds_version = "${jsonencode(var.ipa_crds_version)}" + ipa_enabled = "${jsonencode(var.ipa_enabled)}" + ipa_values = "${jsonencode(var.ipa_values)}" + vault_address = "${jsonencode(var.vault_address)}" + vault_username = "${jsonencode(var.vault_username)}" + sqs_sns = "${jsonencode(var.sqs_sns)}" + restore_snapshot_enabled = "${jsonencode(var.restore_snapshot_enabled)}" + restore_snapshot_name = "${jsonencode(var.restore_snapshot_name)}" + oidc_enabled = "${jsonencode(var.oidc_enabled)}" + oidc_client_id = "${jsonencode(var.oidc_client_id)}" + oidc_config_name = "${jsonencode(var.oidc_config_name)}" + oidc_issuer_url = "${jsonencode(var.oidc_issuer_url)}" + oidc_groups_prefix = "${jsonencode(var.oidc_groups_prefix)}" + oidc_groups_claim = "${jsonencode(var.oidc_groups_claim)}" + oidc_username_prefix = "${jsonencode(var.oidc_username_prefix)}" + oidc_username_claim = "${jsonencode(var.oidc_username_claim)}" + monitoring_enabled = "${jsonencode(var.monitoring_enabled)}" + hibernation_enabled = "${jsonencode(var.hibernation_enabled)}" + keda_version = "${jsonencode(var.keda_version)}" + external_secrets_version = "${jsonencode(var.external_secrets_version)}" + opentelemetry_collector_version = "${jsonencode(var.opentelemetry-collector_version)}" + include_fsx = "${jsonencode(var.include_fsx)}" + include_pgbackup = "${jsonencode(var.include_pgbackup)}" + include_efs = "${jsonencode(var.include_efs)}" + performance_bucket = "${jsonencode(var.performance_bucket)}" + crds_values_yaml_b64 = "${jsonencode(var.crds-values-yaml-b64)}" + pre_reqs_values_yaml_b64 = "${jsonencode(var.pre-reqs-values-yaml-b64)}" + k8s_dashboard_chart_version = "${jsonencode(var.k8s_dashboard_chart_version)}" + enable_k8s_dashboard = "${jsonencode(var.enable_k8s_dashboard)}" + use_acm = "${jsonencode(var.use_acm)}" + enable_waf = "${jsonencode(var.enable_waf)}" + vault_mount_path = "${jsonencode(var.vault_mount_path)}" + terraform_vault_mount_path = "${jsonencode(var.terraform_vault_mount_path)}" + snowflake_enabled = "${jsonencode(var.snowflake_enabled)}" + snowflake_region = "${jsonencode(var.snowflake_region)}" + snowflake_username = "${jsonencode(var.snowflake_username)}" + snowflake_account = "${jsonencode(var.snowflake_account)}" + snowflake_private_key = "${jsonencode(var.snowflake_private_key)}" + snowflake_db_name = "${jsonencode(var.snowflake_db_name)}" + enable_weather_station = "${jsonencode(var.enable_weather_station)}" + aws_primary_dns_role_arn = "${jsonencode(var.aws_primary_dns_role_arn)}" + is_alternate_account_domain = "${jsonencode(var.is_alternate_account_domain)}" + domain_suffix = "${jsonencode(var.domain_suffix)}" + domain_host = "${jsonencode(var.domain_host)}" + alerting_enabled = "${jsonencode(var.alerting_enabled)}" + alerting_slack_enabled = "${jsonencode(var.alerting_slack_enabled)}" + alerting_pagerduty_enabled = "${jsonencode(var.alerting_pagerduty_enabled)}" + alerting_email_enabled = "${jsonencode(var.alerting_email_enabled)}" + alerting_slack_token = "${jsonencode(var.alerting_slack_token)}" + alerting_slack_channel = "${jsonencode(var.alerting_slack_channel)}" + alerting_pagerduty_integration_key = "${jsonencode(var.alerting_pagerduty_integration_key)}" + alerting_email_from = "${jsonencode(var.alerting_email_from)}" + alerting_email_to = "${jsonencode(var.alerting_email_to)}" + alerting_email_host = "${jsonencode(var.alerting_email_host)}" + alerting_email_username = "${jsonencode(var.alerting_email_username)}" + alerting_email_password = "${jsonencode(var.alerting_email_password)}" + eks_addon_version_guardduty = "${jsonencode(var.eks_addon_version_guardduty)}" + use_static_ssl_certificates = "${jsonencode(var.use_static_ssl_certificates)}" + ssl_static_secret_name = "${jsonencode(var.ssl_static_secret_name)}" + local_registry_version = "${jsonencode(var.local_registry_version)}" + local_registry_enabled = "${jsonencode(var.local_registry_enabled)}" + devops_tools_cluster_host = "${jsonencode(var.devops_tools_cluster_host)}" + thanos_grafana_admin_username = "${jsonencode(var.thanos_grafana_admin_username)}" + thanos_cluster_host = "${jsonencode(var.thanos_cluster_host)}" + thanos_cluster_name = "${jsonencode(var.thanos_cluster_name)}" + indico_devops_aws_region = "${jsonencode(var.indico_devops_aws_region)}" + thanos_enabled = "${jsonencode(var.thanos_enabled)}" + keycloak_enabled = "${jsonencode(var.keycloak_enabled)}" + terraform_smoketests_enabled = "${jsonencode(var.terraform_smoketests_enabled)}" + on_prem_test = "${jsonencode(var.on_prem_test)}" + harness_delegate = "${jsonencode(var.harness_delegate)}" + harness_mount_path = "${jsonencode(var.harness_mount_path)}" + + } +} + diff --git a/user_vars.auto.tfvars b/infra/user_vars.auto.tfvars similarity index 100% rename from user_vars.auto.tfvars rename to infra/user_vars.auto.tfvars diff --git a/variables.tf b/infra/variables.tf similarity index 98% rename from variables.tf rename to infra/variables.tf index 9c524a06..0589c12e 100644 --- a/variables.tf +++ b/infra/variables.tf @@ -1,3 +1,9 @@ +# Main flags for resource creation +variable "create_infra" { + type = bool + default = true + description = "Flag to enable infrastructure creation" +} variable "is_azure" { type = bool @@ -538,6 +544,7 @@ variable "performance_bucket" { default = false description = "Add permission to connect to indico-locust-benchmark-test-results" } + variable "crds-values-yaml-b64" { default = "Cg==" } @@ -618,7 +625,7 @@ variable "enable_weather_station" { } variable "aws_primary_dns_role_arn" { - type = string + type = string default = "" description = "The AWS arn for the role needed to manage route53 DNS in a different account." } @@ -828,3 +835,13 @@ variable "harness_mount_path" { type = string default = "harness" } + +variable "infra_crds_version" { + type = string + default = "1.2.2" +} + +variable "infra_pre_reqs_version" { + type = string + default = "2.1.2" +} diff --git a/modules/aws/helm/k8s_dashboard.tf b/modules/aws/helm/k8s_dashboard.tf new file mode 100644 index 00000000..3b18625b --- /dev/null +++ b/modules/aws/helm/k8s_dashboard.tf @@ -0,0 +1,172 @@ +locals { + #TODO: reduce this + ingress_values = var.use_static_ssl_certificates == false ? (< { + userarn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/${user}" + username = user + groups = ["system:masters"] + } + } + + # DNS + the_splits = local.dns_name != "" ? split(".", local.dns_name) : split(".", "test.domain.com") + the_length = length(local.the_splits) + the_tld = local.the_splits[local.the_length - 1] + the_domain = local.the_splits[local.the_length - 2] + alternate_domain_root = join(".", [local.the_domain, local.the_tld]) + + dns_name = var.domain_host == "" ? lower("${var.label}.${var.region}.${var.aws_account}.${var.domain_suffix}") : var.domain_host + #dns_suffix = lower("${var.region}.${var.aws_account}.indico.io") +} diff --git a/modules/aws/infra/main.tf b/modules/aws/infra/main.tf new file mode 100644 index 00000000..56390e84 --- /dev/null +++ b/modules/aws/infra/main.tf @@ -0,0 +1,197 @@ +data "aws_caller_identity" "current" {} + +# Private Key for Cluster Manager (TODO:remove) +resource "tls_private_key" "pk" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "aws_key_pair" "kp" { + key_name = var.name + public_key = tls_private_key.pk.public_key_openssh +} + +# Networking +module "public_networking" { + count = var.direct_connect == true ? 0 : 1 + source = "app.terraform.io/indico/indico-aws-network/mod" + version = "1.2.0" + label = var.label + vpc_cidr = var.vpc_cidr + private_subnet_cidrs = var.private_subnet_cidrs + public_subnet_cidrs = var.public_subnet_cidrs + subnet_az_zones = var.subnet_az_zones +} + +module "private_networking" { + count = var.direct_connect == true ? 1 : 0 + source = "app.terraform.io/indico/indico-aws-dc-network/mod" + version = "1.0.0" + vpc_cidr = var.vpc_cidr + private_subnet_cidrs = var.private_subnet_cidrs + subnet_az_zones = var.subnet_az_zones +} + +module "security-group" { + source = "app.terraform.io/indico/indico-aws-security-group/mod" + version = "1.0.0" + label = var.label + vpc_cidr = var.vpc_cidr + vpc_id = local.network[0].indico_vpc_id +} + +# KMS for Encryption +module "kms_key" { + source = "app.terraform.io/indico/indico-aws-kms/mod" + version = "2.1.0" + label = var.label + additional_tags = var.additional_tags + existing_kms_key = var.existing_kms_key +} + +# SQS/SNS +module "sqs_sns" { + count = var.sqs_sns == true ? 1 : 0 + source = "app.terraform.io/indico/indico-aws-sqs-sns/mod" + version = "1.2.0" + region = var.region + label = var.label + kms_master_key_id = module.kms_key.key.id +} + +# Storage +module "s3-storage" { + source = "app.terraform.io/indico/indico-aws-buckets/mod" + version = "2.0.3" + force_destroy = true # allows terraform to destroy non-empty buckets. + label = var.label + kms_key_arn = module.kms_key.key.arn + submission_expiry = var.submission_expiry + uploads_expiry = var.uploads_expiry + include_rox = var.include_rox +} + + +# This empties the buckets upon delete so terraform doesn't take forever. +resource "null_resource" "s3-delete-data-bucket" { + depends_on = [ + module.s3-storage + ] + + triggers = { + data_bucket_name = module.s3-storage.data_s3_bucket_name + } + + provisioner "local-exec" { + when = destroy + command = "aws s3 rm \"s3://${self.triggers.data_bucket_name}/\" --recursive --only-show-errors || echo \"WARNING: S3 rm ${self.triggers.data_bucket_name} reported errors\" >&2" + } +} + +resource "null_resource" "s3-delete-data-pgbackup-bucket" { + count = var.include_pgbackup == true ? 1 : 0 + + depends_on = [ + module.s3-storage + ] + + triggers = { + pg_backup_bucket_name = module.s3-storage.pgbackup_s3_bucket_name + } + + provisioner "local-exec" { + when = destroy + command = "aws s3 rm \"s3://${self.triggers.pg_backup_bucket_name}/\" --recursive --only-show-errors || echo \"WARNING: S3 rm ${self.triggers.pg_backup_bucket_name} reported errors\" >&2" + } +} + +module "efs-storage" { + count = var.include_efs == true ? 1 : 0 + source = "app.terraform.io/indico/indico-aws-efs/mod" + version = "0.0.1" + label = var.label + additional_tags = merge(var.additional_tags, { "type" = "local-efs-storage" }) + security_groups = [module.security-group.all_subnets_sg_id] + private_subnet_ids = flatten([local.network[0].private_subnet_ids]) + kms_key_arn = module.kms_key.key_arn +} + +module "efs-storage-local-registry" { + count = var.local_registry_enabled == true ? 1 : 0 + source = "app.terraform.io/indico/indico-aws-efs/mod" + version = "0.0.1" + label = "${var.label}-local-registry" + additional_tags = merge(var.additional_tags, { "type" = "local-efs-storage-local-registry" }) + security_groups = [module.security-group.all_subnets_sg_id] + private_subnet_ids = flatten([local.network[0].private_subnet_ids]) + kms_key_arn = module.kms_key.key_arn +} + +module "fsx-storage" { + count = var.include_fsx == true ? 1 : 0 + source = "app.terraform.io/indico/indico-aws-fsx/mod" + version = "1.4.2" + label = var.label + additional_tags = var.additional_tags + region = var.region + storage_capacity = var.storage_capacity + subnet_id = local.network[0].private_subnet_ids[0] + security_group_id = module.security-group.all_subnets_sg_id + data_bucket = module.s3-storage.data_s3_bucket_name + api_models_bucket = module.s3-storage.api_models_s3_bucket_name + kms_key = module.kms_key.key + per_unit_storage_throughput = var.per_unit_storage_throughput + include_rox = var.include_rox +} + +module "cluster" { + cod_snapshots_enabled = true + allow_dns_management = true + aws_account_name = var.aws_account + oidc_enabled = false + source = "app.terraform.io/indico/indico-aws-eks-cluster/mod" + version = "8.1.7" + label = var.label + additional_tags = var.additional_tags + region = var.region + map_users = values(local.eks_users) + vpc_id = local.network[0].indico_vpc_id + security_group_id = module.security-group.all_subnets_sg_id + subnet_ids = flatten([local.network[0].private_subnet_ids]) + node_groups = var.node_groups + cluster_node_policies = var.cluster_node_policies + eks_cluster_iam_role = var.eks_cluster_iam_role + eks_cluster_nodes_iam_role = "${var.label}-${var.region}-node-role" + fsx_arns = [var.include_rox ? module.fsx-storage[0].fsx-rox.arn : "", var.include_fsx == true ? module.fsx-storage[0].fsx-rwx.arn : ""] + kms_key_arn = module.kms_key.key_arn + az_count = var.az_count + key_pair = aws_key_pair.kp.key_name + snapshot_id = var.snapshot_id + default_tags = var.default_tags + s3_buckets = [module.s3-storage.data_s3_bucket_name, var.include_pgbackup ? module.s3-storage.pgbackup_s3_bucket_name : "", var.include_rox ? module.s3-storage.api_models_s3_bucket_name : "", lower("${var.aws_account}-aws-cod-snapshots"), var.performance_bucket ? "indico-locust-benchmark-test-results" : ""] + cluster_version = var.k8s_version + efs_filesystem_id = [var.include_efs == true ? module.efs-storage[0].efs_filesystem_id : ""] + aws_primary_dns_role_arn = var.aws_primary_dns_role_arn +} + +# Alternate Domain name +data "aws_route53_zone" "primary" { + name = var.is_alternate_account_domain == "false" ? lower("${var.aws_account}.${var.domain_suffix}") : lower(local.alternate_domain_root) + provider = aws.dns-control +} + +resource "aws_route53_record" "ipa-app-caa" { + count = var.is_alternate_account_domain == "true" ? 0 : 1 + zone_id = data.aws_route53_zone.primary.zone_id + name = local.dns_name + type = "CAA" + ttl = 300 + records = [ + "0 issue \"sectigo.com\"", + "0 issue \"amazontrust.com\"", + "0 issue \"amazon.com\"", + "0 issue \"amazonaws.com\"", + "0 issue \"awstrust.com\"" + ] + provider = aws.dns-control +} diff --git a/modules/aws/infra/monitoring_routes.tf b/modules/aws/infra/monitoring_routes.tf new file mode 100644 index 00000000..0b655288 --- /dev/null +++ b/modules/aws/infra/monitoring_routes.tf @@ -0,0 +1,39 @@ + +resource "aws_route53_record" "grafana-caa" { + count = var.monitoring_enabled ? 1 : 0 + + zone_id = data.aws_route53_zone.primary.zone_id + name = lower("grafana.${local.dns_name}") + type = "CAA" + ttl = 300 + records = [ + "0 issue \"sectigo.com\"" + ] + provider = aws.dns-control +} + +resource "aws_route53_record" "prometheus-caa" { + count = var.monitoring_enabled ? 1 : 0 + + zone_id = data.aws_route53_zone.primary.zone_id + name = lower("prometheus.${local.dns_name}") + type = "CAA" + ttl = 300 + records = [ + "0 issue \"sectigo.com\"" + ] + provider = aws.dns-control +} + +resource "aws_route53_record" "alertmanager-caa" { + count = var.monitoring_enabled ? 1 : 0 + + zone_id = data.aws_route53_zone.primary.zone_id + name = lower("alertmanager.${local.dns_name}") + type = "CAA" + ttl = 300 + records = [ + "0 issue \"sectigo.com\"" + ] + provider = aws.dns-control +} diff --git a/oidc.tf b/modules/aws/infra/oidc.tf similarity index 100% rename from oidc.tf rename to modules/aws/infra/oidc.tf diff --git a/outputs.tf b/modules/aws/infra/outputs.tf similarity index 66% rename from outputs.tf rename to modules/aws/infra/outputs.tf index bc5bd026..29e8cffe 100644 --- a/outputs.tf +++ b/modules/aws/infra/outputs.tf @@ -1,24 +1,35 @@ - -output "api_models_s3_bucket_name" { - description = "Name of the api-models s3 bucket" - value = module.s3-storage.api_models_s3_bucket_name +# Cluster connection +output "kube_host" { + value = module.cluster.kubernetes_host } -output "data_s3_bucket_name" { - description = "Name of the data s3 bucket" - value = module.s3-storage.data_s3_bucket_name -} +output "kube_ca_certificate" { + value = base64encode(module.cluster.kubernetes_cluster_ca_certificate) -output "s3_role_id" { - description = "ID of the S3 role" - value = module.cluster.s3_role_id +} +output "kube_token" { + sensitive = true + value = module.cluster.kubernetes_token } +# KMS outputs +output "kms_key_arn" { + value = module.kms_key.key_arn + description = "KMS key arn" +} +# EFS outputs output "efs_filesystem_id" { - description = "ID of the EFS filesystem" value = var.include_efs == true ? module.efs-storage[0].efs_filesystem_id : "" + description = "ID of the EFS filesystem" } + +output "local_registry_efs_filesystem_id" { + value = var.local_registry_enabled ? module.efs-storage-local-registry[0].efs_filesystem_id : null + description = "ID of the EFS filesystem for local-registry" +} + +# FSX outputs output "fsx-rwx" { description = "Read write filesystem" value = var.include_fsx == true ? module.fsx-storage[0].fsx-rwx : null @@ -29,12 +40,6 @@ output "fsx-rox" { value = var.include_rox ? module.fsx-storage[0].fsx-rox : "" } -output "key_pem" { - value = tls_private_key.pk.private_key_pem - description = "Generated private key for key pair" - sensitive = true -} - output "fsx_storage_fsx_rwx_dns_name" { value = var.include_fsx == true ? module.fsx-storage[0].fsx-rwx.dns_name : "" } @@ -51,56 +56,44 @@ output "fsx_storage_fsx_rwx_subnet_id" { value = var.include_fsx == true ? module.fsx-storage[0].fsx-rwx.subnet_ids[0] : "" } -output "cluster_name" { - value = var.label -} - -output "cluster_region" { - value = var.region -} - -output "dns_name" { - value = local.dns_name +# S3 outputs +output "api_models_s3_bucket_name" { + description = "Name of the api-models s3 bucket" + value = module.s3-storage.api_models_s3_bucket_name } -# output "kubeconfig" { -# value = module.cluster.kubectl_config -# } - - -output "kube_host" { - value = module.cluster.kubernetes_host +output "data_s3_bucket_name" { + description = "Name of the data s3 bucket" + value = module.s3-storage.data_s3_bucket_name } -output "kube_ca_certificate" { - value = base64encode(module.cluster.kubernetes_cluster_ca_certificate) - -} -output "kube_token" { - sensitive = true - value = module.cluster.kubernetes_token +output "s3_role_id" { + description = "ID of the S3 role" + value = module.cluster.s3_role_id } -output "harness_delegate_name" { - value = var.harness_delegate == true && length(module.harness_delegate) > 0 ? module.harness_delegate[0].delegate_name : "" +output "pgbackup_s3_bucket_name" { + description = "S3 pgbackup bucket name" + value = module.s3-storage.pgbackup_s3_bucket_name } -output "ipa_version" { - value = var.ipa_version +# Misc +output "acm_arn" { + description = "arn of the acm" + value = var.enable_waf == true ? aws_acm_certificate_validation.alb[0].certificate_arn : "" } -output "argo_branch" { - value = var.argo_branch +output "wafv2_arn" { + description = "arn of the wafv2 acl" + value = var.enable_waf == true ? aws_wafv2_web_acl.wafv2-acl[0].arn : "" } -output "argo_path" { - value = var.argo_path +output "key_pem" { + value = tls_private_key.pk.private_key_pem + description = "Generated private key for key pair" + sensitive = true } -output "argo_repo" { - value = var.argo_repo +output "network" { + value = local.network[0] } - -output "monitoring_enabled" { - value = var.monitoring_enabled -} \ No newline at end of file diff --git a/modules/aws/infra/providers.tf b/modules/aws/infra/providers.tf new file mode 100644 index 00000000..656e2050 --- /dev/null +++ b/modules/aws/infra/providers.tf @@ -0,0 +1,11 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + configuration_aliases = [aws.dns-control] + } + kubernetes = { + source = "hashicorp/kubernetes" + } + } +} \ No newline at end of file diff --git a/modules/aws/infra/variables.tf b/modules/aws/infra/variables.tf new file mode 100644 index 00000000..e267d910 --- /dev/null +++ b/modules/aws/infra/variables.tf @@ -0,0 +1,234 @@ +# General +variable "aws_account" { + type = string + description = "The Name of the AWS Acccount this cluster lives in" +} + +variable "region" { + type = string + default = "us-east-1" + description = "The AWS region in which to launch the indico stack" +} + +variable "label" { + type = string + default = "indico" + description = "The unique string to be prepended to resources names" +} + +variable "name" { + type = string + default = "indico" + description = "Name to use in all cluster resources names (TODO: deprecate (redundant))" +} + +variable "additional_tags" { + type = map(string) + default = null + description = "Additonal tags to add to each resource" +} + +variable "default_tags" { + type = map(string) + default = null + description = "Default tags to add to each resource (redundant?)" +} + +variable "dns_name" { + type = string + default = ".indico.io" + description = "DNS name" +} + +variable "domain_suffix" { + type = string + default = "indico.io" + description = "Domain suffix" +} + +variable "domain_host" { + type = string + default = "" + description = "domain host name." +} + +# Network +variable "direct_connect" { + type = bool + default = false + description = "Sets up the direct connect configuration if true; else use public subnets" +} + +variable "vpc_cidr" { + type = string + description = "The VPC for the entire indico stack" +} + +variable "private_subnet_cidrs" { + type = list(string) + description = "CIDR ranges for the private subnets" +} + +variable "public_subnet_cidrs" { + type = list(string) + description = "CIDR ranges for the public subnets" +} + +variable "subnet_az_zones" { + type = list(string) + description = "Availability zones for the subnets" +} + +# KMS +variable "existing_kms_key" { + type = string + default = "" + description = "Name of kms key if it exists in the account (eg. 'alias/')" +} + +# SQS/SNS +variable "sqs_sns" { + type = bool + default = true + description = "Flag for enabling SQS/SNS" +} + +# Blob Storage +variable "submission_expiry" { + type = number + description = "The number of days to retain submissions" + default = 30 +} + +variable "uploads_expiry" { + type = number + description = "The number of days to retain uploads" + default = 30 +} + +variable "include_rox" { + type = bool + default = false + description = "Create a read only FSx file system (TODO: remove (deprecated))" +} + +variable "include_pgbackup" { + type = bool + default = true + description = "Create a read only FSx file system" +} + +# File Storage +variable "include_efs" { + type = bool + default = true + description = "Create efs" +} + +variable "include_fsx" { + type = bool + default = false + description = "Create a fsx file system(s)" +} + +variable "local_registry_enabled" { + type = bool + default = false +} + +variable "storage_capacity" { + type = number + default = 1200 + description = "Storage capacity in GiB for RWX FSx" +} + +variable "per_unit_storage_throughput" { + type = number + default = 100 + description = "Throughput for each 1 TiB or storage (max 200) for RWX FSx" +} + +# Cluster +variable "k8s_version" { + type = string + default = "1.27" + description = "The EKS version to use" +} + +variable "node_groups" { +} + +variable "az_count" { + type = number + default = 2 + description = "Number of availability zones for nodes" + + validation { + condition = var.az_count > 0 && var.az_count <= 3 + error_message = "The az_count must be in the range 1-3" + } +} + +variable "cluster_node_policies" { + type = list(any) + default = ["IAMReadOnlyAccess"] + description = "Additonal IAM policies to add to the cluster IAM role" +} + +variable "eks_cluster_iam_role" { + type = string + default = null + description = "Name of the IAM role to assign to the EKS cluster; will be created if not supplied" +} + +variable "snapshot_id" { + type = string + default = "" + description = "The ebs snapshot of read-only data to use (TODO:?)" +} + +variable "performance_bucket" { + type = bool + default = false + description = "Add permission to connect to indico-locust-benchmark-test-results (TODO:?)" +} + +# OIDC configuration (TODO: add other variables) +variable "oidc_enabled" { + type = bool + default = true + description = "Enable OIDC Auhentication" +} + +# WAF +variable "enable_waf" { + type = bool + default = false + description = "enables aws alb controller for app-edge, also creates waf rules." +} + +# Guardduty +variable "eks_addon_version_guardduty" { + type = bool + default = true + description = "enable guardduty" +} + +# DNS +variable "aws_primary_dns_role_arn" { + type = string + default = "" + description = "The AWS arn for the role needed to manage route53 DNS in a different account." +} + +variable "is_alternate_account_domain" { + type = string + default = "false" + description = "domain name is controlled by a different aws account" +} + +# Set up routes for monitoring +variable "monitoring_enabled" { + type = bool + default = true +} diff --git a/modules/aws/infra/waf.tf b/modules/aws/infra/waf.tf new file mode 100644 index 00000000..8d6f6344 --- /dev/null +++ b/modules/aws/infra/waf.tf @@ -0,0 +1,120 @@ +resource "aws_wafv2_web_acl" "wafv2-acl" { + count = var.enable_waf == true ? 1 : 0 + name = "${var.label}-wafv2" + description = "${var.label}-wafv2 managed rules." + scope = "REGIONAL" + + default_action { + allow {} + } + + rule { + name = "AWS-AWSManagedRulesUnixRuleSet" + priority = 0 + + override_action { + none {} + } + + statement { + managed_rule_group_statement { + name = "AWSManagedRulesUnixRuleSet" + vendor_name = "AWS" + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "AWS-AWSManagedRulesUnixRuleSet" + sampled_requests_enabled = true + } + } + + rule { + name = "AWS-AWSManagedRulesKnownBadInputsRuleSet" + priority = 1 + + override_action { + none {} + } + + statement { + managed_rule_group_statement { + name = "AWSManagedRulesKnownBadInputsRuleSet" + vendor_name = "AWS" + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "AWS-AWSManagedRulesKnownBadInputsRuleSet" + sampled_requests_enabled = true + } + } + + rule { + name = "AWS-AWSManagedRulesAmazonIpReputationList" + priority = 2 + + override_action { + none {} + } + + statement { + managed_rule_group_statement { + name = "AWSManagedRulesAmazonIpReputationList" + vendor_name = "AWS" + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "AWS-AWSManagedRulesAmazonIpReputationList" + sampled_requests_enabled = true + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "${var.label}-wafv2" + sampled_requests_enabled = true + } +} + +resource "aws_acm_certificate" "alb" { + count = var.enable_waf == true ? 1 : 0 + domain_name = local.dns_name + validation_method = "DNS" + depends_on = [ + aws_route53_record.ipa-app-caa + ] +} + + +resource "aws_route53_record" "alb" { + for_each = var.enable_waf ? { + for dvo in aws_acm_certificate.alb[0].domain_validation_options : dvo.domain_name => { + name = dvo.resource_record_name + record = dvo.resource_record_value + type = dvo.resource_record_type + } + } : {} + + allow_overwrite = true + name = each.value.name + records = [each.value.record] + ttl = 60 + type = each.value.type + zone_id = data.aws_route53_zone.primary.zone_id + provider = aws.dns-control +} + + +resource "aws_acm_certificate_validation" "alb" { + count = var.enable_waf == true ? 1 : 0 + certificate_arn = aws_acm_certificate.alb[0].arn + validation_record_fqdns = [for record in aws_route53_record.alb : record.fqdn] + depends_on = [ + aws_acm_certificate.alb[0] + ] +} \ No newline at end of file diff --git a/modules/common/applications/applications.tf b/modules/common/applications/applications.tf new file mode 100644 index 00000000..14116d1d --- /dev/null +++ b/modules/common/applications/applications.tf @@ -0,0 +1,59 @@ +# Create Argo Application YAML for each user supplied application +resource "github_repository_file" "custom-application-yaml" { + for_each = var.argo_enabled == true ? var.applications : {} + + repository = data.github_repository.argo-github-repo[0].name + branch = var.argo_branch + file = "${var.argo_path}/${each.value.name}_application.yaml" + commit_message = var.message + overwrite_on_create = true + + #TODO: + # this allows people to make edits to the file so we don't overwrite it. + #lifecycle { + # ignore_changes = [ + # content + # ] + #} + + content = < 0 && var.az_count <= 3 + error_message = "The az_count must be in the range 1-3" + } +} + +variable "key_arn" { + type = string + description = "" +} + +variable "s3_role_id" { + type = string + description = "" +} + +variable "pgbackup_s3_bucket_name" { + type = string + description = "" +} + +variable "use_acm" { + type = bool + default = false + description = "create cluster that will use acm" +} + +variable "kubernetes_host" { + type = string + description = "" +} + +variable "indico_vpc_id" { + type = string + description = "" +} + +variable "public_subnet_ids" { + description = "Public subnet ids of the cluster" + default = [] +} + +variable "argo_project_name" { + type = string + description = "" +} + +variable "k8s_version" { + type = string + default = "1.27" + description = "The EKS version to use" +} + +variable "local_registry_enabled" { + type = bool + default = false +} + +variable "on_prem_test" { + type = bool + default = false +} + +variable "enable_waf" { + type = bool + default = false + description = "enables aws alb controller for app-edge, also creates waf rules." +} + +variable "waf_arn" { + type = string + description = "waf acl web arn" + default = "" +} + +variable "acm_arn" { + type = string + description = "acm cert validation arn" + default = "" +} + +variable "include_efs" { + type = bool + default = true + description = "Create efs" +} + +variable "efs_filesystem_id" { + type = string + description = "EFS filesystem id" + default = "" +} + +variable "include_fsx" { + type = bool + default = false + description = "Create a fsx file system(s)" +} + +variable "fsx_rwx" { + description = "fsx_rwx object from infra module output" + default = {} +} + +variable "monitoring_password" { + type = string + description = "Generated password for monitoring" + sensitive = true + default = "" +} + +# Smoketest +variable "ipa_smoketest_values" { + type = string + default = "Cg==" # empty newline string +} + +variable "ipa_smoketest_repo" { + type = string + default = "https://harbor.devops.indico.io/chartrepo/indico-charts" +} + +variable "ipa_smoketest_version" { + type = string + default = "0.1.8" +} + +variable "ipa_smoketest_enabled" { + type = bool + default = true +} + +# Snapshot +variable "restore_snapshot_enabled" { + default = false + type = bool + description = "Flag for restoring cluster from snapshot" +} + +variable "restore_snapshot_name" { + type = string + default = "" + description = "Name of snapshot in account's s3 bucket" +} diff --git a/modules/common/local-registry/local-registry.tf b/modules/common/local-registry/local-registry.tf new file mode 100644 index 00000000..111f3726 --- /dev/null +++ b/modules/common/local-registry/local-registry.tf @@ -0,0 +1,196 @@ +data "vault_kv_secret_v2" "account-robot-credentials" { + mount = "customer-${var.aws_account}" + name = "harbor-registry" +} + +resource "aws_efs_access_point" "local-registry" { + root_directory { + path = "/registry" + creation_info { + owner_gid = 1000 + owner_uid = 1000 + permissions = "0777" + } + } + + posix_user { + gid = 1000 + uid = 1000 + } + file_system_id = var.efs_filesystem_id +} + + +# TODO: move all kubernetes resources here to the helm chart +resource "kubernetes_persistent_volume_claim" "local-registry" { + depends_on = [ + kubernetes_namespace.local-registry, + kubernetes_persistent_volume.local-registry + ] + + metadata { + name = "local-registry" + namespace = "local-registry" + } + spec { + access_modes = ["ReadWriteMany"] + storage_class_name = "local-registry" + resources { + requests = { + storage = "100Gi" + } + } + volume_name = "local-registry" + } +} + +resource "kubernetes_persistent_volume" "local-registry" { + metadata { + name = "local-registry" + } + + spec { + capacity = { + storage = "100Gi" + } + + access_modes = ["ReadWriteMany"] + storage_class_name = "local-registry" + + persistent_volume_source { + csi { + driver = "efs.csi.aws.com" + volume_handle = "${var.efs_filesystem_id}::${aws_efs_access_point.local-registry.id}" + } + } + } +} + +resource "kubernetes_namespace" "local-registry" { #TODO: set create_namespace = true for local-registry + metadata { + name = "local-registry" + } +} + +resource "kubernetes_storage_class_v1" "local-registry" { #TODO: add to local-registry helm chart + metadata { + name = "local-registry" + } + + storage_provisioner = "efs.csi.aws.com" + reclaim_policy = "Retain" +} + +resource "helm_release" "local-registry" { + depends_on = [ + kubernetes_namespace.local-registry, + kubernetes_persistent_volume_claim.local-registry + ] + + verify = false + name = "local-registry" + create_namespace = true + namespace = "local-registry" + repository = var.ipa_repo + chart = "local-registry" + version = var.local_registry_version + wait = false + timeout = "1800" # 30 minutes + disable_webhooks = false + + values = [<