AWS EKS ExternalDNS Install with Terraform
Step-01: Introduction¶
- External DNS: Used for Updating Route53 RecordSets from Kubernetes
- We need to create IAM Policy, k8s Service Account & IAM Role and associate them together for external-dns pod to add or remove entries in AWS Route53 Hosted Zones.
- Update External-DNS default manifest to support our needs
- Deploy & Verify logs
Step-02: c1-versions.tf¶
- Create DynamoDB Table
dev-aws-externaldns - Create S3 Bucket Key as
dev/aws-externaldns/terraform.tfstate - Project Folder: 03-externaldns-install-terraform-manifests
# Terraform Settings Block terraform { required_version = ">= 1.0.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 4.12" } helm = { source = "hashicorp/helm" #version = "2.5.1" version = "~> 2.5" } kubernetes = { source = "hashicorp/kubernetes" version = "~> 2.11" } } # Adding Backend as S3 for Remote State Storage backend "s3" { bucket = "terraform-on-aws-eks" key = "dev/aws-externaldns/terraform.tfstate" region = "us-east-1" # For State Locking dynamodb_table = "dev-aws-externaldns" } } # Terraform AWS Provider Block provider "aws" { region = var.aws_region }
Step-03: c2-remote-state-datasource.tf¶
- Project Folder: 03-externaldns-install-terraform-manifests
Step-04: c3-01-generic-variables.tf¶
- Project Folder: 03-externaldns-install-terraform-manifests
# Input Variables - Placeholder file # AWS Region variable "aws_region" { description = "Region in which AWS Resources to be created" type = string default = "us-east-1" } # Environment Variable variable "environment" { description = "Environment Variable used as a prefix" type = string default = "dev" } # Business Division variable "business_divsion" { description = "Business Division in the large organization this Infrastructure belongs" type = string default = "SAP" }
Step-05: c3-02-local-values.tf¶
- Project Folder: 03-externaldns-install-terraform-manifests
# Define Local Values in Terraform locals { owners = var.business_divsion environment = var.environment name = "${var.business_divsion}-${var.environment}" common_tags = { owners = local.owners environment = local.environment } eks_cluster_name = "${data.terraform_remote_state.eks.outputs.cluster_id}" }
Step-06: c4-01-externaldns-iam-policy-and-role.tf¶
- Project Folder: 03-externaldns-install-terraform-manifests
# Resource: Create External DNS IAM Policy resource "aws_iam_policy" "externaldns_iam_policy" { name = "${local.name}-AllowExternalDNSUpdates" path = "/" description = "External DNS IAM Policy" policy = jsonencode({ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "route53:ChangeResourceRecordSets" ], "Resource": [ "arn:aws:route53:::hostedzone/*" ] }, { "Effect": "Allow", "Action": [ "route53:ListHostedZones", "route53:ListResourceRecordSets" ], "Resource": [ "*" ] } ] }) } output "externaldns_iam_policy_arn" { value = aws_iam_policy.externaldns_iam_policy.arn } # Resource: Create IAM Role resource "aws_iam_role" "externaldns_iam_role" { name = "${local.name}-externaldns-iam-role" # Terraform's "jsonencode" function converts a Terraform expression result to valid JSON syntax. assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRoleWithWebIdentity" Effect = "Allow" Sid = "" Principal = { Federated = "${data.terraform_remote_state.eks.outputs.aws_iam_openid_connect_provider_arn}" } Condition = { StringEquals = { "${data.terraform_remote_state.eks.outputs.aws_iam_openid_connect_provider_extract_from_arn}:aud": "sts.amazonaws.com", "${data.terraform_remote_state.eks.outputs.aws_iam_openid_connect_provider_extract_from_arn}:sub": "system:serviceaccount:default:external-dns" } } }, ] }) tags = { tag-key = "AllowExternalDNSUpdates" } } # Associate External DNS IAM Policy to IAM Role resource "aws_iam_role_policy_attachment" "externaldns_iam_role_policy_attach" { policy_arn = aws_iam_policy.externaldns_iam_policy.arn role = aws_iam_role.externaldns_iam_role.name } output "externaldns_iam_role_arn" { description = "External DNS IAM Role ARN" value = aws_iam_role.externaldns_iam_role.arn }
Step-07: c4-02-externaldns-helm-provider.tf¶
- Project Folder: 03-externaldns-install-terraform-manifests
# Datasource: EKS Cluster Auth data "aws_eks_cluster_auth" "cluster" { name = data.terraform_remote_state.eks.outputs.cluster_id } # HELM Provider provider "helm" { kubernetes { host = data.terraform_remote_state.eks.outputs.cluster_endpoint cluster_ca_certificate = base64decode(data.terraform_remote_state.eks.outputs.cluster_certificate_authority_data) token = data.aws_eks_cluster_auth.cluster.token } }
Step-08: c4-03-externaldns-install.tf¶
- Project Folder: 03-externaldns-install-terraform-manifests
# Resource: Helm Release resource "helm_release" "external_dns" { depends_on = [aws_iam_role.externaldns_iam_role] name = "external-dns" repository = "https://kubernetes-sigs.github.io/external-dns/" chart = "external-dns" namespace = "default" set { name = "image.repository" value = "k8s.gcr.io/external-dns/external-dns" } set { name = "serviceAccount.create" value = "true" } set { name = "serviceAccount.name" value = "external-dns" } set { name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn" value = "${aws_iam_role.externaldns_iam_role.arn}" } set { name = "provider" # Default is aws (https://github.com/kubernetes-sigs/external-dns/tree/master/charts/external-dns) value = "aws" } set { name = "policy" # Default is "upsert-only" which means DNS records will not get deleted even equivalent Ingress resources are deleted (https://github.com/kubernetes-sigs/external-dns/tree/master/charts/external-dns) value = "sync" # "sync" will ensure that when ingress resource is deleted, equivalent DNS record in Route53 will get deleted } }
Step-09: c4-04-externaldns-outputs.tf¶
- Project Folder: 03-externaldns-install-terraform-manifests
Step-10: terraform.tfvars¶
- Project Folder: 03-externaldns-install-terraform-manifests
Step-11: Execute Terraform Commands¶
# Change Directory
cd 03-externaldns-install-terraform-manifests
# Terraform Initialize
terraform init
# Terraform validate
terraform validate
# Terraform plan
terraform plan
# Terraform Apply
terraform apply -auto-approve
Step-12: Verify External DNS Installation on EKS Cluster¶
# List All resources from default Namespace
kubectl get all
# List pods (external-dns pod should be in running state)
kubectl get pods
# Verify Deployment by checking logs
kubectl logs -f $(kubectl get po | egrep -o 'external-dns[A-Za-z0-9-]+')
Step-13: Don't Clean-Up LBC Controller, EKS Cluster & ExternalDNS¶
- Dont destroy the Terraform Projects in below three folders
- Terraform Project Folder: 01-ekscluster-terraform-manifests
- Terraform Project Folder: 02-lbc-install-terraform-manifests
- Terraform Project Folder: 03-externaldns-install-terraform-manifests
- We are going to use them for all upcoming Usecases.
- Destroy Resorces Order
- 03-externaldns-install-terraform-manifests
- 02-lbc-install-terraform-manifests
- 01-ekscluster-terraform-manifests
############################################################## ## Destroy External DNS # Change Directroy cd 03-externaldns-install-terraform-manifests # Terraform Destroy terraform init terraform apply -destroy -auto-approve ############################################################## ## Destroy LBC # Change Directroy cd 02-lbc-install-terraform-manifests # Terraform Destroy terraform init terraform apply -destroy -auto-approve ############################################################## ## Destroy EKS Cluster # Change Directroy cd 01-ekscluster-terraform-manifests # Terraform Destroy terraform init terraform apply -destroy -auto-approve ##############################################################
References¶
- External DNS Helm Chart
- External DNS Helm Chart Configuration
- External DNS + Ingress
- External DNS on AWS
🎉 New Course
Ultimate DevOps Real-World Project Implementation on AWS
$15.99
$84.99
81% OFF
DEVOPS2026FEB
Enroll Now on Udemy →
🎉 Offer