Skip to content

Instantly share code, notes, and snippets.

@dpetzold
Last active May 11, 2020 05:47
Show Gist options
  • Save dpetzold/c6df0ae042a29130fa58278db196234f to your computer and use it in GitHub Desktop.
Save dpetzold/c6df0ae042a29130fa58278db196234f to your computer and use it in GitHub Desktop.
#!env python3
"""
Given an ACM certificate ARN generate Terraform to create resources for an aws_acm_certificate_validation to pass.
$ generate-certificate-validation.py <certificate arn>
/************************** new.example.com ********************************/
data aws_route53_zone example_com_zone {
name = "example.com."
}
resource aws_route53_record example_com_cert_validation {
name = "_xxxxxxxxxxxxxxxxx.example.com."
type = "CNAME"
zone_id = data.aws_route53_zone.example_com_zone.id
ttl = 600
records = [
"_xxxxxxxxxxxxxxxxxxxx.olprtlswtu.acm-validations.aws."
]
}
resource aws_acm_certificate_validation cert {
certificate_arn = "arn:aws:acm:us-west-2:xxxxxxxxxxxxxx:certificate/xxxxxxxxxxxxxxxxxxxx"
validation_record_fqdns = [
aws_route53_record.new_example_com_cert_validation.fqdn
]
}
"""
import argparse
import dataclasses
import boto3
import sys
from pprint import pprint
@dataclasses.dataclass
class Domain:
name: str
resource_record_name: str
resource_record_type: str
resource_record_value: str
@property
def normalized(self):
return self.name.replace('.', '_')
@property
def tld(self):
parts = self.name.split('.')
if self.name.endswith('.com') and len(parts) > 2:
return '.'.join(parts[1:])
return self.name
def terraform(self):
return f"""\
/************************** {self.name} ********************************/
data aws_route53_zone {self.normalized}_zone {{
name = "{self.tld}."
}}
resource aws_route53_record {self.normalized}_cert_validation {{
name = "{self.resource_record_name}"
type = "{self.resource_record_type}"
zone_id = data.aws_route53_zone.{self.normalized}_zone.id
ttl = 600
records = [
"{self.resource_record_value}"
]
}}
"""
@dataclasses.dataclass
class GenerateCertificateValidation:
profile_name: str
region_name: str
cert_arn: str
debug: bool
@property
def acm_client(self):
return boto3.Session(
profile_name=self.profile_name,
region_name=self.region_name,
).client('acm')
def terraform(self, domain_fqdns):
seperator = ',\n '
return f"""\
resource aws_acm_certificate_validation cert {{
certificate_arn = "{self.cert_arn}"
validation_record_fqdns = [
{seperator.join(domain_fqdns)}
]
}}
"""
def render(self, output: argparse.FileType):
cert = self.acm_client.describe_certificate(
CertificateArn=self.cert_arn,
)['Certificate']
if self.debug:
pprint(cert)
domains = []
for opt in cert['DomainValidationOptions']:
if opt['DomainName'].startswith('*.'):
continue
domains.append(
Domain(
name=opt['DomainName'],
resource_record_name=opt['ResourceRecord']['Name'],
resource_record_type=opt['ResourceRecord']['Type'],
resource_record_value=opt['ResourceRecord']['Value'],
)
)
domain_fqdns = []
for domain in domains:
domain_fqdns.append(f'aws_route53_record.{domain.normalized}_cert_validation.fqdn')
output.write(domain.terraform())
output.write(self.terraform(domain_fqdns))
def main():
parser = argparse.ArgumentParser(description='Generate Terraform certificate validation code.')
parser.add_argument('--profile', default=None)
parser.add_argument('--debug', action='store_true', default=False)
parser.add_argument('--region', default='us-east-1')
parser.add_argument('cert_arn', help='The cerrificate arn')
parser.add_argument('output', nargs='?', default=sys.stdout, type=argparse.FileType('w', encoding='UTF-8'))
args = parser.parse_args()
GenerateCertificateValidation(
args.profile,
args.region,
args.cert_arn,
args.debug,
).render(args.output)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment