I've been dealing with AWS SDK a lot in building https://github.com/dotenx/dotenx, and I've found it challenging quite often, particularly because of poor documentation.
One of the issues I had to resolve was requesting a TLS certificate from AWS ACM and validating it with DNS validation.
If you're in a similar situation, you can use this snippet.
func requestCertificate(domainName, hostedZoneId string) (string, error) {
cfg := &aws.Config{
Region: aws.String(config.Region),
}
svc := acm.New(session.New(), cfg)
input := &acm.RequestCertificateInput{
DomainName: aws.String(domainName),
IdempotencyToken: aws.String(strings.Replace(domainName, ".", "", -1)),
ValidationMethod: aws.String("DNS"),
SubjectAlternativeNames: []*string{
aws.String("*." + domainName),
},
DomainValidationOptions: []*acm.DomainValidationOption{
{
DomainName: aws.String(domainName),
ValidationDomain: aws.String(domainName),
},
},
}
result, err := svc.RequestCertificate(input)
if err != nil {
return "", err
}
time.Sleep(time.Second * 10) // This MUST be long enough, o.w. the validation options won't be available
dcIn := &acm.DescribeCertificateInput{
CertificateArn: result.CertificateArn,
}
c, err := svc.DescribeCertificate(dcIn)
if err != nil {
return "", err
}
if c.Certificate.DomainValidationOptions == nil {
errMsg := "DomainValidationOptions does not exists"
logrus.Error(errMsg)
return "", errors.New(errMsg)
}
fmt.Println("DomainValidationOptions: ", c.Certificate.DomainValidationOptions) // ---> Log at the bottom
for _, dvo := range c.Certificate.DomainValidationOptions {
vRecordName := dvo.ResourceRecord.Name // -----> this is nil and causes panic
vRecordValue := dvo.ResourceRecord.Value
createRoute53Record(*vRecordName, *vRecordValue, hostedZoneId)
}
return *result.CertificateArn, nil
}
func createRoute53Record(domain, value, hostedZoneId string) error {
cfg := &aws.Config{
Region: aws.String(config.Region),
}
if config.Configs.App.RunLocally {
creds := credentials.NewStaticCredentials(config.Configs.Secrets.AwsAccessKeyId, config.Configs.Secrets.AwsSecretAccessKey, "")
cfg = aws.NewConfig().WithRegion(config.Configs.Upload.S3Region).WithCredentials(creds)
}
svc := route53.New(session.New(), cfg)
resourceRecordSet := &route53.ResourceRecordSet{
Name: aws.String(domain + "."),
Type: aws.String("CNAME"),
ResourceRecords: []*route53.ResourceRecord{
{
Value: aws.String(value),
},
},
TTL: aws.Int64(300),
}
upsert := []*route53.Change{{
Action: aws.String("UPSERT"),
ResourceRecordSet: resourceRecordSet,
}}
// Put it into a pretty envelope with a stamp for route53#zoneId and change ticket
params := route53.ChangeResourceRecordSetsInput{
ChangeBatch: &route53.ChangeBatch{
Changes: upsert,
},
HostedZoneId: aws.String(hostedZoneId),
}
// Post it
_, err := svc.ChangeResourceRecordSets(¶ms)
if err != nil {
logrus.Error(err.Error())
}
return err
}
You can find the code here:
https://gist.github.com/mkamrani/edf0134801076352e8c502ff28801e46