# How to Configure ExternalDNS with Cross-Account Route53

When I’m working with a private EKS cluster, I recently encountered a requirement to configure ExternalDNS to update records in a Route53 hosted zone belonging to a different AWS account. After extensive research on articles, documentation, and forums, I couldn’t find a clear solution or even a solid hint. Eventually, a [GitHub issue](https://github.com/kubernetes-sigs/external-dns/issues/1608) provided the crucial insight leveraging OIDC provider integration in EKS to enable cross-account access.

Using that information, I successfully configured ExternalDNS to update records across AWS accounts. If you're facing a similar challenge, this blog will save you time by providing a direct, step-by-step solution.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747592747153/60b7262b-1556-46b5-8199-186bd910447b.png align="center")

# **Prerequisites: ✅**

Before we dive in, make sure you have the following in place:

* **Two AWS Accounts:**
    
    * **Account A:** This is where your Route 53 hosted zone resides.
        
    * **Account B:** This will host your EKS cluster and where ExternalDNS will run.
        
* **Tools Configured:** Aws cli, Kubectl , lens (optional), Ingress controller (optional to check with ingress)
    
* **Route53 (Account A):** You should have your DNS hosted zone already setup in Account A.
    
* ![example image for Route53](https://cdn.hashnode.com/res/hashnode/image/upload/v1747586636213/ef447a18-99df-408a-a535-15808e65424f.png align="left")
    
* **EKS Cluster (Account B):** You need a running EKS cluster in Account B that ExternalDNS will interact with.
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747593308940/21cd0197-d84d-4cd4-a941-888cf853d850.png align="center")
    

---

# **High-Level Overview: 💡**

Here is a quick rundown of the steps I'll be taking:

1. **Create an IAM Role in the Route 53 Account (Account A):** This role will grant necessary Route 53 permissions and trust the IAM role we will create for ExternalDNS in Account B.
    
2. **Create an IAM Role in the EKS Account (Account B):** This role will have the permission to assume the IAM role we created in Account A. We will configure its trust policy to trust your OIDC provider associated with your EKS cluster.
    
3. **Install ExternalDNS in your EKS Cluster (Account B):** We will use Addons to install ExternalDNS with custom configuration values that leverage the cross-account IAM roles.
    
4. **Verification:** We will verify the setup by deploying application by pointing hostname.
    

---

# Let's get started! 🚀

### **1\. Create the IAM Role in the Route 53 Account (Account A): 1️⃣**

In your **Account A**, navigate to the IAM console and follow these steps:

* Click on **Roles** in the left-hand navigation pane.
    
* Click **Create role**.
    
* For the "Select type of trusted entity," choose **AWS account**.
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747587298563/c6723b8f-745a-4dcd-b5c6-88b8e330c75d.png align="center")
    
* Enter the **Account ID of Account B**.
    
* Click **Next**. (ignore other options)
    
* Now, we need to attach permissions for Route 53. Search for and select the **AmazonRoute53FullAccess** policy.
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747587510238/6d33db6d-8460-46dd-850c-edb39ddbe96d.png align="center")
    
* * **Important Security Consideration:** For production environments, it's highly recommended to scope down these permissions to the minimum required for ExternalDNS to function.
        
* Click **Next**.
    
* Give your role a descriptive name (e.g., `Route53ExternalDNSAccess`).
    
* Review the role details and click **Create role**.
    

> **Take note of the ARN (Amazon Resource Name) of this newly created role.** You'll need it in the next step. It will look something like: `arn:aws:iam::ACCOUNT_A_ID:role/Route53ExternalDNSAccess`.

---

## **2\. Create the IAM Role in the EKS Account (Account B): 2️⃣**

Now, switch to your **Account B** and follow these steps in the IAM console:

* Click on **Roles**.
    
* Click **Create role**.
    
* For the "Select type of trusted entity," choose **Web identity (OIDC)**.
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747589628958/3513b37f-bcfb-4a07-b5da-52690a9776eb.png align="center")
    
* For the "Identity provider," select the OIDC provider associated with your EKS cluster. You can find this information in your EKS cluster details under the "Overview" tab. It will typically look like `oidc.eks.REGION.amazonaws.com/id/YOUR_OIDC_ID`.
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747589683143/2e296178-5d9d-4d34-baaf-5623858512b5.png align="center")
    
* For the "Audience," enter [`sts.amazonaws.com`](http://sts.amazonaws.com).
    
* Then click **Add Condition** to provide service account and external DNS namespace to give access
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747589818431/5595bca9-6b44-4aae-b16d-86baecb6dd3c.png align="center")
    
* Before creating role, we need to add permissions that allow this role to assume the role we created in Account A.
    
* So, for that open policy console in new tab and click **Create policy**.
    
* In the JSON tab of the "Create policy" page, paste the following policy, **replacing**  
    `arn:aws:iam::<ACCOUNT_A_ID>:role/Route53ExternalDNSAccess` with the actual ARN of the role you created in Account A:
    
* ```json
        {
            "Statement": [
                {
                    "Action": [
                        "sts:AssumeRole"
                    ],
                    "Effect": "Allow",
                    "Resource": [
                        "arn:aws:iam::<ACCOUNT_A_ID>:role/Route53ExternalDNSAccess" #arn of route53 role which we created above.
                    ],
                    "Sid": "Statement1"
                }
            ],
            "Version": "2012-10-17"
        }
    ```
    
* Click **Next**
    
* Give your policy a descriptive name (e.g., `ExternalDNSAssumeRoute53RolePolicy`).
    
* Click **Create policy**.
    
* Go back to the "Create role" page (where you selected the OIDC trust). Click **Next: Permissions**.
    
* Search for and select the policy you just created (`ExternalDNSAssumeRoute53RolePolicy`).
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747590632201/bc24c293-efbd-4c98-85a9-400387902e7d.png align="center")
    
* Click **Next: Review**.
    
* Give your role a descriptive name (e.g., `ExternalDNSIRSAccess`).
    
* Review the role details and click **Create role**.
    

> **Take note of the ARN of this newly created role.** You'll need this for the `Route53ExternalDNSAccess` Role (Account A) to assume this. It will look something like: `arn:aws:iam::ACCOUNT_B_ID:role/ExternalDNSIRSAccess`.

---

## **3\. Install ExternalDNS with Configuration Values 🔻**

**(Account B):**

* Now, in your **Account B**, use Addons to install ExternalDNS.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747590922705/68832dd0-33b8-4462-8aa5-5494e2b10d54.png align="center")

* Click **Next**
    
* Now add the role which we have created under IRSA option
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747591078819/f5ab0dc6-bda0-42ba-8f23-7a4068945439.png align="center")

* To pass the below configuration to the external DNS, expand the `Optional configuration settings` option.
    
* ```json
        domainFilters:
          - nginx.local
        txtOwnerId: Z03011653ICHPFH5D6TUJ
        extraArgs:
          - --aws-zone-type=private
          - --aws-assume-role=arn:aws:iam::<ACCOUNT_A_ID>:role/Route53ExternalDNSAccess
    ```
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747591325610/0d662019-cf6b-4526-8f2c-673b19af3169.png align="center")
    

Once you have configured the values, you can install ExternalDNS using Addons

---

## **4.Verification: 🍾**

After the ExternalDNS is deployed, you can check the ExternalDNS pod logs in your EKS cluster (Account B) using `kubectl logs -n kube-system -l app=external-dns`. Look for any errors related to IAM permissions or Route 53 connectivity.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747591945860/49874fb1-61f1-48f5-8adb-b27f64dbd0da.png align="center")

To verify that ExternalDNS is correctly creating DNS records, you can deploy a sample application and create an Ingress resource with a hostname. Here are example Kubernetes manifests:

**Deployment (deploy.yaml):**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 256Mi
```

**Service (service.yaml):**

```yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  type: ClusterIP 
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
```

**Ingress (ingress.yaml):**

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx 
  rules:
    - host: test.nginx.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-service
                port:
                  number: 80
```

* Replace `test.nginx.local` with a hostname that matches the `domainFilters` you configured for ExternalDNS (e.g., if your `domainFilters` is `nginx.local`).
    

Apply these manifests to your EKS cluster:

```json
kubectl apply -f nginx.yaml
kubectl apply -f nginx-service.yaml
kubectl apply -f nginx-ingress.yaml
```

After the Ingress is created, ExternalDNS should automatically create your records in your Route 53 hosted zone (in Account A) pointing `test.nginx.local` to the load balancer associated with your Ingress.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747593118855/a23eed65-feff-4e6c-a3ae-5c510519a6c6.png align="center")

This is how it looks.

---

# **Conclusion: ⭐**

Setting up ExternalDNS with cross-account Route 53 felt daunting at first for me, but now you can easily setup by following these precise steps, you can save yourself a significant amount of time and get your DNS records managed seamlessly across your AWS accounts. Remember to always adhere to the principle of least privilege when granting IAM permissions in production environments.

I hope this guide has been helpful! ❤️Let me know in the comments if you have any questions or run into any issues. Happy Automating! 🥳
