Skip to main content

AWS Certificate Manager: Create Managed TLS Certificate via AWS CLI, Test Certificate with LoadBalancer in AWS EKS Cluster

823 words·
AWS AWS CLI AWS Certificate Manager (ACM) Route 53
Table of Contents

Create Managed Certificate
#

Create Certificate
#

# Create a New Wildcard ACM Certificate
aws acm request-certificate \
  --domain-name "example-domain.jklug.work" \
  --validation-method DNS \
  --region eu-central-1

# Shell output:
{
    "CertificateArn": "arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac"
}

Verify Certificate
#

# List certificates
aws acm list-certificates --region eu-central-1

# Shell output
{
    "CertificateSummaryList": [
        {
            "CertificateArn": "arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac",
            "DomainName": "example-domain.jklug.work",
            "SubjectAlternativeNameSummaries": [
                "example-domain.jklug.work"
            ],
            "HasAdditionalSubjectAlternativeNames": false,
            "Status": "PENDING_VALIDATION",
            "Type": "AMAZON_ISSUED",
            "KeyAlgorithm": "RSA-2048",
            "KeyUsages": [],
            "ExtendedKeyUsages": [],
            "InUse": false,
            "RenewalEligibility": "INELIGIBLE",
            "CreatedAt": "2025-01-31T11:21:08.387000+00:00"
        }
    ]
}

List Certificate Details
#

# List certificate details: Define certificate ARN
aws acm describe-certificate \
  --certificate-arn arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac \
  --region eu-central-1

# Shell output:
{
    "Certificate": {
        "CertificateArn": "arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac",
        "DomainName": "example-domain.jklug.work",
        "SubjectAlternativeNames": [
            "example-domain.jklug.work"
        ],
        "DomainValidationOptions": [
            {
                "DomainName": "example-domain.jklug.work",
                "ValidationDomain": "example-domain.jklug.work",
                "ValidationStatus": "PENDING_VALIDATION",
                "ResourceRecord": {
                    "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                    "Type": "CNAME",
                    "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                },
                "ValidationMethod": "DNS"
            }
        ],
        "Subject": "CN=example-domain.jklug.work",
        "Issuer": "Amazon",
        "CreatedAt": "2025-01-31T11:21:08.387000+00:00",
        "Status": "PENDING_VALIDATION",
        "KeyAlgorithm": "RSA-2048",
        "SignatureAlgorithm": "SHA256WITHRSA",
        "InUseBy": [],
        "Type": "AMAZON_ISSUED",
        "KeyUsages": [],
        "ExtendedKeyUsages": [],
        "RenewalEligibility": "INELIGIBLE",
        "Options": {
            "CertificateTransparencyLoggingPreference": "ENABLED"
        }
    }
}

Take the values of the following section and create a CNAME DNS entry at Route 53:

        "DomainValidationOptions": [
            {
                "DomainName": "example-domain.jklug.work",
                "ValidationDomain": "example-domain.jklug.work",
                "ValidationStatus": "PENDING_VALIDATION",
                "ResourceRecord": {
                    "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                    "Type": "CNAME",
                    "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                },
                "ValidationMethod": "DNS"
            }
        ],

Route 53
#

List Hosted Zone ID
#

# Identify the hosted zone ID
aws route53 list-hosted-zones

# Shell output:
{
    "HostedZones": [
        {
            "Id": "/hostedzone/Z05838622LmyzoneID",
            "Name": "jklug.work.",
            "CallerReference": "9e7f187b-3129-424b-9cf6-c50141743c23",
            "Config": {
                "Comment": "",
                "PrivateZone": false
            },
            "ResourceRecordSetCount": 19
        }

Create DNS Entry / CNAME Record
#

# Create a CNAME DNS entry at Route 53: Define 
aws route53 change-resource-record-sets --hosted-zone-id Z05838622LmyzoneID --change-batch '{
    "Changes": [
        {
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                "Type": "CNAME",
                "TTL": 300,
                "ResourceRecords": [
                    {
                        "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                    }
                ]
            }
        }
    ]
}'

# Shell output:
{
    "ChangeInfo": {
        "Id": "/change/C09097723HVQ9R4V6UQVH",
        "Status": "PENDING",
        "SubmittedAt": "2025-01-31T11:25:08.521000+00:00"
    }
}

Verify CNAME Entry with nslookup
#

# Verify the CNAME Entry
nslookup _aa49443c04657141f09802747021d3a6.example-domain.jklug.work

# Shell output:
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
_aa49443c04657141f09802747021d3a6.example-domain.jklug.work     canonical name = _d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws.

Verify Certificate Status
#

Wait till the “ValidationStatus” changes to “SUCCESS”:

# List certificate details: Define certificate ARN
aws acm describe-certificate \
  --certificate-arn arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac \
  --region eu-central-1

# Shell output:
{
    "Certificate": {
        "CertificateArn": "arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac",
        "DomainName": "example-domain.jklug.work",
        "SubjectAlternativeNames": [
            "example-domain.jklug.work"
        ],
        "DomainValidationOptions": [
            {
                "DomainName": "example-domain.jklug.work",
                "ValidationDomain": "example-domain.jklug.work",
                "ValidationStatus": "SUCCESS",
                "ResourceRecord": {
                    "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                    "Type": "CNAME",
                    "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                },
                "ValidationMethod": "DNS"
            }
        ],
        "Serial": "04:32:8c:2c:46:97:97:48:49:16:99:8f:00:2e:4a:18",
        "Subject": "CN=example-domain.jklug.work",
        "Issuer": "Amazon",
        "CreatedAt": "2025-01-31T11:21:08.387000+00:00",
        "IssuedAt": "2025-01-31T11:25:36.724000+00:00",
        "Status": "ISSUED",
        "NotBefore": "2025-01-31T00:00:00+00:00",
        "NotAfter": "2026-03-01T23:59:59+00:00",
        "KeyAlgorithm": "RSA-2048",
        "SignatureAlgorithm": "SHA256WITHRSA",
        "InUseBy": [],
        "Type": "AMAZON_ISSUED",
        "KeyUsages": [
            {
                "Name": "DIGITAL_SIGNATURE"
            },
            {
                "Name": "KEY_ENCIPHERMENT"
            }
        ],
        "ExtendedKeyUsages": [
            {
                "Name": "TLS_WEB_SERVER_AUTHENTICATION",
                "OID": "1.3.6.1.5.5.7.3.1"
            },
            {
                "Name": "TLS_WEB_CLIENT_AUTHENTICATION",
                "OID": "1.3.6.1.5.5.7.3.2"
            }
        ],
        "RenewalEligibility": "INELIGIBLE",
        "Options": {
            "CertificateTransparencyLoggingPreference": "ENABLED"
        }
    }
}



Test Certificate (EKS Cluster)
#

Example Deployment & LoadBalancer
#

# Create a configuration for a deployment and LoadBalancer
vi example-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-container
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-container
  template:
    metadata:
      labels:
        app: my-container
    spec:
      containers:
      - name: container-2
        image: jueklu/container-2
        ports:
        - containerPort: 8080
---

apiVersion: v1
kind: Service
metadata:
  name: my-container-lb
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
spec:
  selector:
    app: my-container
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8080
  type: LoadBalancer
# Apply the deployment
kubectl apply -f example-deployment.yaml

List LoadBalancer External DNS Name
#

Note: The “EXTERNAL-IP” / DNS name of a load balancer service is listed as “” it’s meaning there is a problem with the configuration. It should only take a view second till the extern IP is shown.

# List load balancer service details: List DNS name
kubectl get svc my-container-lb

# Shell output:
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP                                                                  PORT(S)                      AGE
my-container-lb   LoadBalancer   10.100.27.225   ad852fb1f900047608cbeb2b3ca5e44c-1305577960.eu-central-1.elb.amazonaws.com   80:32003/TCP,443:31468/TCP   27s

Create DNS Entry for LoadBalancer
#

# Create a DNS entry for the LoadBalancer endpoint
aws route53 change-resource-record-sets --hosted-zone-id Z05838622LmyzoneID --change-batch '{
    "Changes": [
        {
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": "example-domain.jklug.work",
                "Type": "CNAME",
                "TTL": 300,
                "ResourceRecords": [
                    {
                        "Value": "ad852fb1f900047608cbeb2b3ca5e44c-1305577960.eu-central-1.elb.amazonaws.com"
                    }
                ]
            }
        }
    ]
}'

# Shell output:
{
    "ChangeInfo": {
        "Id": "/change/C03029662M5AX3SXDU3XU",
        "Status": "PENDING",
        "SubmittedAt": "2025-01-31T11:31:58.098000+00:00"
    }
}

Curl the DNS Name
#

# Curl the DNS name
curl example-domain.jklug.work

# Shell output:
Container runs on: my-container-7df9db746-pmbxq



Cleanup
#

Delete Deployment
#

# Delete the deployment and LoadBalancer
kubectl delete -f example-deployment.yaml

Delete Route53 Entries
#

# Delete the LoadBalancer entry
aws route53 change-resource-record-sets --hosted-zone-id Z05838622LmyzoneID --change-batch '{
    "Changes": [
        {
            "Action": "DELETE",
            "ResourceRecordSet": {
                "Name": "example-domain.jklug.work",
                "Type": "CNAME",
                "TTL": 300,
                "ResourceRecords": [
                    {
                        "Value": "ad852fb1f900047608cbeb2b3ca5e44c-1305577960.eu-central-1.elb.amazonaws.com"
                    }
                ]
            }
        }
    ]
}'
# Delete the certificate entry
aws route53 change-resource-record-sets --hosted-zone-id Z05838622LmyzoneID --change-batch '{
    "Changes": [
        {
            "Action": "DELETE",
            "ResourceRecordSet": {
                "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                "Type": "CNAME",
                "TTL": 300,
                "ResourceRecords": [
                    {
                        "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                    }
                ]
            }
        }
    ]
}'

Delete the Certificate
#

# Delete the certificate
aws acm delete-certificate --certificate-arn arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac --region eu-central-1
# Verify the certificate was deleted
aws acm list-certificates --region eu-central-1

# Shell output:
{
    "CertificateSummaryList": []
}