diff --git a/contexts/aws-example/blueprint.yaml b/contexts/aws-example/blueprint.yaml index 512d1079..77c6bae7 100644 --- a/contexts/aws-example/blueprint.yaml +++ b/contexts/aws-example/blueprint.yaml @@ -16,7 +16,10 @@ sources: terraform: - path: network/aws-vpc - path: cluster/aws-eks +- path: cluster/aws-eks/additions + destroy: false - path: gitops/flux + destroy: false kustomize: - name: telemetry-base path: telemetry/base @@ -53,7 +56,6 @@ kustomize: source: core dependsOn: - pki-resources - force: true components: - nginx - nginx/flux-webhook @@ -66,7 +68,6 @@ kustomize: source: core dependsOn: - policy-resources - force: true components: - cert-manager - trust-manager @@ -75,18 +76,15 @@ kustomize: source: core dependsOn: - pki-base - force: true components: - private-issuer/ca - public-issuer/selfsigned -- name: gitops - path: gitops/flux +- name: dns + path: dns source: core - dependsOn: - - ingress-base - force: true components: - - webhook + - external-dns + - external-dns/route53 - name: observability path: observability source: core diff --git a/contexts/aws-example/terraform/cluster/aws-eks/additions.tfvars b/contexts/aws-example/terraform/cluster/aws-eks/additions.tfvars new file mode 100644 index 00000000..443c73b4 --- /dev/null +++ b/contexts/aws-example/terraform/cluster/aws-eks/additions.tfvars @@ -0,0 +1,10 @@ +# Managed by Windsor CLI: This file is partially managed by the windsor CLI. Your changes will not be overwritten. + +# ARN of the IAM role for external-dns. If not provided, will be looked up from the cluster. +# external_dns_role_arn = null + +# AWS region where the Route53 hosted zone is located. If not provided, will use the cluster's region. +# route53_region = null + +# Name of the EKS cluster. +# cluster_name = "" diff --git a/kustomize/dns/external-dns/route53/README.md b/kustomize/dns/external-dns/route53/README.md new file mode 100644 index 00000000..533af0bc --- /dev/null +++ b/kustomize/dns/external-dns/route53/README.md @@ -0,0 +1,43 @@ +# External DNS Route53 Configuration + +This component configures external-dns to use AWS Route53 for DNS management in EKS clusters. + +## Dependencies + +This component requires the `aws-eks/additions` Terraform module to be applied first, as it creates a ConfigMap with required configuration values: + +```hcl +resource "kubernetes_config_map" "external_dns" { + metadata { + name = "external-dns" + namespace = "system-dns" + } + + data = { + aws_role_arn = "arn:aws:iam::${account_id}:role/${cluster_name}-external-dns" + aws_region = "us-west-2" # or cluster's region + txt_owner_id = "${cluster_name}-${context_id}" + } +} +``` + +## Configuration Values + +The HelmRelease uses the following values from the ConfigMap: + +| ConfigMap Key | Helm Value Path | Description | +|---------------|----------------|-------------| +| aws_role_arn | aws.role_arn | IAM role ARN for external-dns | +| aws_region | aws.region | AWS region for Route53 operations | +| txt_owner_id | txtOwnerId | Unique identifier for TXT records | + +## Usage + +1. Apply the `aws-eks/additions` Terraform module to create the ConfigMap +2. Apply this kustomization to deploy external-dns with Route53 configuration + +## Notes + +- The ConfigMap must exist in the `system-dns` namespace +- The IAM role referenced by `aws_role_arn` must have appropriate Route53 permissions +- The `txt_owner_id` is constructed as `${cluster_name}-${context_id}` to ensure uniqueness across clusters diff --git a/kustomize/dns/external-dns/route53/kustomization.yaml b/kustomize/dns/external-dns/route53/kustomization.yaml new file mode 100644 index 00000000..47249ed4 --- /dev/null +++ b/kustomize/dns/external-dns/route53/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component +patches: + - target: + group: helm.toolkit.fluxcd.io + version: v2 + kind: HelmRelease + name: external-dns + namespace: system-dns + path: patches/helm-release.yaml diff --git a/kustomize/dns/external-dns/route53/patches/helm-release.yaml b/kustomize/dns/external-dns/route53/patches/helm-release.yaml new file mode 100644 index 00000000..aebe112a --- /dev/null +++ b/kustomize/dns/external-dns/route53/patches/helm-release.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: external-dns + namespace: system-dns +spec: + valuesFrom: + - kind: ConfigMap + name: external-dns + valuesKey: aws_region + targetPath: aws.region + - kind: ConfigMap + name: external-dns + valuesKey: txt_owner_id + targetPath: txtOwnerId + values: + provider: + aws: + usePodIdentity: true + sources: + - ingress + - service + policy: sync + registry: txt + domainFilters: + - ${DOMAIN} + serviceAccount: + create: true + name: external-dns + annotations: {} diff --git a/terraform/cluster/aws-eks/README.md b/terraform/cluster/aws-eks/README.md index e1f5b784..ce8f28e6 100644 --- a/terraform/cluster/aws-eks/README.md +++ b/terraform/cluster/aws-eks/README.md @@ -67,6 +67,7 @@ No modules. | [cluster\_name](#input\_cluster\_name) | The name of the EKS cluster. | `string` | `""` | no | | [context\_id](#input\_context\_id) | The windsor context id for this deployment | `string` | `""` | no | | [context\_path](#input\_context\_path) | The path to the context folder, where kubeconfig is stored | `string` | `""` | no | +| [endpoint\_private\_access](#input\_endpoint\_private\_access) | Whether to enable private access to the EKS cluster. | `bool` | `false` | no | | [endpoint\_public\_access](#input\_endpoint\_public\_access) | Whether to enable public access to the EKS cluster. | `bool` | `true` | no | | [fargate\_profiles](#input\_fargate\_profiles) | Map of EKS Fargate profile definitions to create. |
map(object({
selectors = list(object({
namespace = string
labels = optional(map(string), {})
}))
tags = optional(map(string), {})
})) | `{}` | no |
| [kubernetes\_version](#input\_kubernetes\_version) | The kubernetes version to deploy. | `string` | `"1.32"` | no |
diff --git a/terraform/cluster/aws-eks/_templates/kubeconfig.tpl b/terraform/cluster/aws-eks/_templates/kubeconfig.tpl
new file mode 100644
index 00000000..9354987d
--- /dev/null
+++ b/terraform/cluster/aws-eks/_templates/kubeconfig.tpl
@@ -0,0 +1,26 @@
+apiVersion: v1
+kind: Config
+clusters:
+- name: ${cluster_name}
+ cluster:
+ server: ${cluster_endpoint}
+ certificate-authority-data: ${cluster_ca}
+contexts:
+- name: ${cluster_name}
+ context:
+ cluster: ${cluster_name}
+ user: ${cluster_name}
+current-context: ${cluster_name}
+users:
+- name: ${cluster_name}
+ user:
+ exec:
+ apiVersion: client.authentication.k8s.io/v1beta1
+ command: aws
+ args:
+ - eks
+ - get-token
+ - --cluster-name
+ - ${cluster_name}
+ - --region
+ - ${region}
diff --git a/terraform/cluster/aws-eks/additions/.terraform.lock.hcl b/terraform/cluster/aws-eks/additions/.terraform.lock.hcl
new file mode 100644
index 00000000..6456939e
--- /dev/null
+++ b/terraform/cluster/aws-eks/additions/.terraform.lock.hcl
@@ -0,0 +1,45 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "5.97.0"
+ constraints = "5.97.0"
+ hashes = [
+ "h1:rUDE0OgA+6IiEA+w0cPp3/QQNH4SpjFjYcQ6p7byKS4=",
+ "zh:02790ad98b767d8f24d28e8be623f348bcb45590205708334d52de2fb14f5a95",
+ "zh:088b4398a161e45762dc28784fcc41c4fa95bd6549cb708b82de577f2d39ffc7",
+ "zh:0c381a457b7af391c43fc0167919443f6105ad2702bde4d02ddea9fd7c9d3539",
+ "zh:1a4b57a5043dcca64d8b8bae8b30ef4f6b98ed2144f792f39c4e816d3f1e2c56",
+ "zh:1bf00a67f39e67664337bde065180d41d952242801ebcd1c777061d4ffaa1cc1",
+ "zh:24c549f53d6bd022af31426d3e78f21264d8a72409821669e7fd41966ae68b2b",
+ "zh:3abda50bbddb35d86081fe39522e995280aea7f004582c4af22112c03ac8b375",
+ "zh:7388ed7f21ce2eb46bd9066626ce5f3e2a5705f67f643acce8ae71972f66eaf6",
+ "zh:96740f2ff94e5df2b2d29a5035a1a1026fe821f61712b2099b224fb2c2277663",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:9f399f8e8683a3a3a6d63a41c7c3a5a5f266eedef40ea69eba75bacf03699879",
+ "zh:bcf2b288d4706ebd198f75d2159663d657535483331107f2cdef381f10688baf",
+ "zh:cc76c8a9fc3bad05a8779c1f80fe8c388734f1ec1dd0affa863343490527b466",
+ "zh:de4359cf1b057bfe7a563be93829ec64bf72e7a2b85a72d075238081ef5eb1db",
+ "zh:e208fa77051a1f9fa1eff6c5c58aabdcab0de1695b97cdea7b8dd81df3e0ed73",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/kubernetes" {
+ version = "2.27.0"
+ constraints = "2.27.0"
+ hashes = [
+ "h1:GzU0FzYAT/+IgAhnSBcFH3bT+4I5N6oSga6iZgNJAus=",
+ "zh:3bdba30ae67c55dc7e9a317ac0da3b208ea7926fe9c2f0ae6587ee88dcc58d1f",
+ "zh:3f35138a831c00b188d2ffee27111dd0cf59afad2dd5653ed9e67d59646de12c",
+ "zh:64066d18f6ae9a316c2bc840ef3e641d7ab94e1ea3a41d12523e77345ad442ef",
+ "zh:653063d44b44881af3a480f7f8eaa94fa300e0229df2072d30f606bddcc9f025",
+ "zh:87f306e37efb61d13efa6da53a1e45e97e5996ebc0568b1caf8c3c5e54c05809",
+ "zh:8c428b9708f9634391e52300218771eab3fe942bb1295d8c0ad50ca4b33db3d9",
+ "zh:a44e87119a0337ded15479851786a13f412b413d9a463ba550d1210249206b0f",
+ "zh:aa2c4d110b0de6ef997c0d45f3f23f8a98f5530753095d6eff439a6d91a8ea31",
+ "zh:eb15ed8781ac6a0dec2f7d03cf090e23cfa05e3225806c6231ff2c574662fd63",
+ "zh:eb81c563f93bd3303f9620d11cd49f21f3f89ac3475c6d3e821b239feb9c217d",
+ "zh:f1a344a7f16131123577e4ec994d04a34ea458ec16c1ccac53fe7946bd817b18",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ ]
+}
diff --git a/terraform/cluster/aws-eks/additions/README.md b/terraform/cluster/aws-eks/additions/README.md
new file mode 100644
index 00000000..d974fa7b
--- /dev/null
+++ b/terraform/cluster/aws-eks/additions/README.md
@@ -0,0 +1,43 @@
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >=1.8 |
+| [aws](#requirement\_aws) | 5.97.0 |
+| [kubernetes](#requirement\_kubernetes) | 2.27.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | 5.97.0 |
+| [kubernetes](#provider\_kubernetes) | 2.27.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [kubernetes_config_map.external_dns](https://registry.terraform.io/providers/hashicorp/kubernetes/2.27.0/docs/resources/config_map) | resource |
+| [kubernetes_namespace.system_dns](https://registry.terraform.io/providers/hashicorp/kubernetes/2.27.0/docs/resources/namespace) | resource |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/5.97.0/docs/data-sources/caller_identity) | data source |
+| [aws_eks_cluster.current](https://registry.terraform.io/providers/hashicorp/aws/5.97.0/docs/data-sources/eks_cluster) | data source |
+| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/5.97.0/docs/data-sources/region) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [cluster\_name](#input\_cluster\_name) | Name of the EKS cluster. | `string` | `""` | no |
+| [context\_id](#input\_context\_id) | The windsor context id for this deployment | `string` | `""` | no |
+| [external\_dns\_role\_arn](#input\_external\_dns\_role\_arn) | ARN of the IAM role for external-dns. If not provided, will be looked up from the cluster. | `string` | `null` | no |
+| [route53\_region](#input\_route53\_region) | AWS region where the Route53 hosted zone is located. If not provided, will use the cluster's region. | `string` | `null` | no |
+
+## Outputs
+
+No outputs.
+
diff --git a/terraform/cluster/aws-eks/additions/main.tf b/terraform/cluster/aws-eks/additions/main.tf
new file mode 100644
index 00000000..1c4cf4f9
--- /dev/null
+++ b/terraform/cluster/aws-eks/additions/main.tf
@@ -0,0 +1,71 @@
+# The AWS EKS Config module manages Kubernetes resources for AWS EKS clusters
+# It provides configuration for system components like external-dns
+# This module bridges Terraform and Kubernetes resources
+# Key features: namespace management, configmap creation, auto-import of existing resources
+
+terraform {
+ required_version = ">=1.8"
+ required_providers {
+ kubernetes = {
+ source = "hashicorp/kubernetes"
+ version = "2.27.0"
+ }
+ aws = {
+ source = "hashicorp/aws"
+ version = "5.97.0"
+ }
+ }
+}
+
+locals {
+ cluster_name = var.cluster_name != "" ? var.cluster_name : "cluster-${var.context_id}"
+}
+
+data "aws_region" "current" {}
+
+data "aws_caller_identity" "current" {}
+
+data "aws_eks_cluster" "current" {
+ name = local.cluster_name
+}
+
+# =============================================================================
+# Namespace Resources
+# =============================================================================
+
+# The system-dns namespace hosts DNS-related components
+# It provides isolation and security context for DNS services
+resource "kubernetes_namespace" "system_dns" {
+ metadata {
+ name = "system-dns"
+ labels = {
+ "pod-security.kubernetes.io/enforce" = "baseline"
+ "pod-security.kubernetes.io/audit" = "baseline"
+ "pod-security.kubernetes.io/warn" = "baseline"
+ }
+ }
+
+ lifecycle {
+ ignore_changes = [
+ metadata[0].labels
+ ]
+ }
+}
+
+# =============================================================================
+# ConfigMap Resources
+# =============================================================================
+
+# The external-dns configmap provides configuration for the external-dns service
+# It contains AWS-specific settings and credentials
+resource "kubernetes_config_map" "external_dns" {
+ metadata {
+ name = "external-dns"
+ namespace = kubernetes_namespace.system_dns.metadata[0].name
+ }
+
+ data = {
+ aws_region = var.route53_region != null ? var.route53_region : data.aws_region.current.name
+ txt_owner_id = local.cluster_name
+ }
+}
diff --git a/terraform/cluster/aws-eks/additions/test.tftest.hcl b/terraform/cluster/aws-eks/additions/test.tftest.hcl
new file mode 100644
index 00000000..fc1e8bcd
--- /dev/null
+++ b/terraform/cluster/aws-eks/additions/test.tftest.hcl
@@ -0,0 +1,76 @@
+mock_provider "aws" {
+ mock_data "aws_caller_identity" {
+ defaults = {
+ account_id = "123456789012"
+ }
+ }
+ mock_data "aws_region" {
+ defaults = {
+ name = "us-west-2"
+ }
+ }
+ mock_data "aws_eks_cluster" {
+ defaults = {
+ name = "test-cluster"
+ arn = "arn:aws:eks:us-west-2:123456789012:cluster/test-cluster"
+ }
+ }
+}
+
+mock_provider "kubernetes" {}
+
+# Verifies that the module creates resources with minimal configuration,
+# ensuring that all default values are correctly applied and only required variables are set.
+run "minimal_configuration" {
+ command = plan
+
+ variables {
+ context_id = "test"
+ }
+
+ assert {
+ condition = kubernetes_namespace.system_dns.metadata[0].name == "system-dns"
+ error_message = "Namespace should be created with default name 'system-dns'"
+ }
+
+ assert {
+ condition = kubernetes_config_map.external_dns.metadata[0].name == "external-dns"
+ error_message = "ConfigMap should be created with name 'external-dns'"
+ }
+
+ assert {
+ condition = kubernetes_config_map.external_dns.data.aws_region == "us-west-2"
+ error_message = "ConfigMap should have correct AWS region"
+ }
+
+ assert {
+ condition = kubernetes_config_map.external_dns.data.txt_owner_id == "cluster-test"
+ error_message = "ConfigMap should have correct txt owner ID"
+ }
+}
+
+# Verifies that the module handles all optional variables correctly
+run "full_configuration" {
+ command = plan
+
+ variables {
+ context_id = "test"
+ cluster_name = "custom-cluster"
+ route53_region = "us-east-1"
+ }
+
+ assert {
+ condition = kubernetes_config_map.external_dns.metadata[0].name == "external-dns"
+ error_message = "ConfigMap should be created with name 'external-dns'"
+ }
+
+ assert {
+ condition = kubernetes_config_map.external_dns.data.aws_region == "us-east-1"
+ error_message = "ConfigMap should use provided AWS region"
+ }
+
+ assert {
+ condition = kubernetes_config_map.external_dns.data.txt_owner_id == "custom-cluster"
+ error_message = "ConfigMap should have correct txt owner ID"
+ }
+}
diff --git a/terraform/cluster/aws-eks/additions/variables.tf b/terraform/cluster/aws-eks/additions/variables.tf
new file mode 100644
index 00000000..faa5958a
--- /dev/null
+++ b/terraform/cluster/aws-eks/additions/variables.tf
@@ -0,0 +1,27 @@
+# =============================================================================
+# Input Variables
+# =============================================================================
+
+variable "external_dns_role_arn" {
+ description = "ARN of the IAM role for external-dns. If not provided, will be looked up from the cluster."
+ type = string
+ default = null
+}
+
+variable "route53_region" {
+ description = "AWS region where the Route53 hosted zone is located. If not provided, will use the cluster's region."
+ type = string
+ default = null
+}
+
+variable "cluster_name" {
+ description = "Name of the EKS cluster."
+ type = string
+ default = ""
+}
+
+variable "context_id" {
+ description = "The windsor context id for this deployment"
+ type = string
+ default = ""
+}
diff --git a/terraform/cluster/aws-eks/variables.tf b/terraform/cluster/aws-eks/variables.tf
index 624da090..9f2b16e3 100644
--- a/terraform/cluster/aws-eks/variables.tf
+++ b/terraform/cluster/aws-eks/variables.tf
@@ -39,6 +39,12 @@ variable "endpoint_public_access" {
default = true
}
+variable "endpoint_private_access" {
+ description = "Whether to enable private access to the EKS cluster."
+ type = bool
+ default = false
+}
+
variable "cluster_api_access_cidr_block" {
description = "The CIDR block for the cluster API access."
type = string