Skip to content

Instantly share code, notes, and snippets.

@clstokes
Last active October 24, 2021 14:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save clstokes/0a88df1ace4b588df85350881022e0a0 to your computer and use it in GitHub Desktop.
Save clstokes/0a88df1ace4b588df85350881022e0a0 to your computer and use it in GitHub Desktop.
Pulumi Example in Golang to create an AWS ACM Certifcate and Route53 records for validation
module p-go-acm-validation
go 1.14
require (
github.com/pulumi/pulumi-aws/sdk/v3 v3.22.1
github.com/pulumi/pulumi/sdk/v2 v2.16.2
)
package main
import (
"fmt"
"github.com/pulumi/pulumi-aws/sdk/v3/go/aws/acm"
"github.com/pulumi/pulumi-aws/sdk/v3/go/aws/route53"
"github.com/pulumi/pulumi/sdk/v2/go/pulumi"
)
const zoneID = "YOUR_ZONE_ID"
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
domainName := "example.com"
subdomainName := "my-subdomain"
cert, err := acm.NewCertificate(ctx, "cert", &acm.CertificateArgs{
DomainName: pulumi.String(fmt.Sprintf("%v.%v", subdomainName, domainName)),
Tags: pulumi.StringMap{
"Environment": pulumi.String("test"),
},
ValidationMethod: pulumi.String("DNS"),
SubjectAlternativeNames: pulumi.StringArray{
pulumi.String(fmt.Sprintf("1-%v.%v", subdomainName, domainName)),
pulumi.String(fmt.Sprintf("2-%v.%v", subdomainName, domainName)),
pulumi.String(fmt.Sprintf("3-%v.%v", subdomainName, domainName)),
pulumi.String(fmt.Sprintf("4-%v.%v", subdomainName, domainName)),
},
})
if err != nil {
return err
}
// If the number of SubjectAlternativeNames is _KNOWN_ beforehand, can do it this way.
// numberOfDomains := 5 // len(SubjectAlternativeNames) + 1 - or calculate off of an array
// knownNumberOfSANs(ctx, cert, numberOfDomains)
// If the number of SubjectAlternativeNames is _NOT KNOWN_ beforehand, must do it this way.
certArnFromValidation, err := unknownNumberOfSANs(ctx, cert)
if err != nil {
return err
}
ctx.Export("certArnFromValidation", certArnFromValidation)
bucket, err := s3.NewBucket(ctx, "bucket", &s3.BucketArgs{
Acl: pulumi.String("public-read"),
Tags: pulumi.StringMap{
"Name": pulumi.String("cameron-acm-test"),
},
})
if err != nil {
return err
}
s3OriginId := "myS3Origin"
distribution, err := cloudfront.NewDistribution(ctx, "s3Distribution", &cloudfront.DistributionArgs{
Origins: cloudfront.DistributionOriginArray{
&cloudfront.DistributionOriginArgs{
DomainName: bucket.BucketRegionalDomainName,
OriginId: pulumi.String(s3OriginId),
},
},
DefaultCacheBehavior: &cloudfront.DistributionDefaultCacheBehaviorArgs{
AllowedMethods: pulumi.StringArray{
pulumi.String("HEAD"),
pulumi.String("GET"),
},
CachedMethods: pulumi.StringArray{
pulumi.String("HEAD"),
pulumi.String("GET"),
},
ForwardedValues: &cloudfront.DistributionDefaultCacheBehaviorForwardedValuesArgs{
QueryString: pulumi.Bool(false),
Cookies: &cloudfront.DistributionDefaultCacheBehaviorForwardedValuesCookiesArgs{
Forward: pulumi.String("none"),
},
},
TargetOriginId: pulumi.String(s3OriginId),
ViewerProtocolPolicy: pulumi.String("allow-all"),
},
Enabled: pulumi.Bool(false),
ViewerCertificate: &cloudfront.DistributionViewerCertificateArgs{
AcmCertificateArn: certArnFromValidation,
SslSupportMethod: pulumi.String("sni-only"),
},
Restrictions: &cloudfront.DistributionRestrictionsArgs{
GeoRestriction: &cloudfront.DistributionRestrictionsGeoRestrictionArgs{
RestrictionType: pulumi.String("none"),
},
},
})
if err != nil {
return err
}
ctx.Export("distributionArn", distribution.Arn)
return nil
})
}
func knownNumberOfSANs(ctx *pulumi.Context, cert *acm.Certificate, numberOfDomains int) error {
var validationRecordFqdns pulumi.StringArray
for i := 0; i < numberOfDomains; i++ {
rec, err := route53.NewRecord(ctx, fmt.Sprintf("cert-%v", i), &route53.RecordArgs{
ZoneId: pulumi.String(zoneID),
Name: cert.DomainValidationOptions.Index(pulumi.Int(i)).ResourceRecordName().Elem(),
Type: cert.DomainValidationOptions.Index(pulumi.Int(i)).ResourceRecordType().Elem(),
Records: pulumi.StringArray{
cert.DomainValidationOptions.Index(pulumi.Int(i)).ResourceRecordValue().Elem(),
},
Ttl: pulumi.Int(60),
})
if err != nil {
return err
}
validationRecordFqdns = append(validationRecordFqdns, rec.Fqdn)
}
_, err := acm.NewCertificateValidation(ctx, "cert-validation", &acm.CertificateValidationArgs{
CertificateArn: cert.Arn,
ValidationRecordFqdns: validationRecordFqdns,
})
if err != nil {
return err
}
return nil
}
func unknownNumberOfSANs(ctx *pulumi.Context, cert *acm.Certificate) (pulumi.StringOutput, error) {
ctx.Log.Info("Route53 Records and Validation resources will not be shown during the `preview`, but will occur during the `update`.", nil)
// Note: when done this way, the Records created within the 'Apply' will not be shown during preview.
certArn := cert.DomainValidationOptions.ApplyT(func(outputValues []acm.CertificateDomainValidationOption) interface{} {
var validationRecordFqdns pulumi.StringArray
for _, s := range outputValues {
rec, _ := route53.NewRecord(ctx, fmt.Sprintf("cert-%v", *s.ResourceRecordName), &route53.RecordArgs{
ZoneId: pulumi.String(zoneID),
Name: pulumi.String(*s.ResourceRecordName),
Type: pulumi.String(*s.ResourceRecordType),
Records: pulumi.StringArray{
pulumi.String(*s.ResourceRecordValue),
},
Ttl: pulumi.Int(60),
})
validationRecordFqdns = append(validationRecordFqdns, rec.Fqdn)
}
certificationValidation, _ := acm.NewCertificateValidation(ctx, "cert-validation", &acm.CertificateValidationArgs{
CertificateArn: cert.Arn,
ValidationRecordFqdns: validationRecordFqdns,
})
return certificationValidation.CertificateArn
})
return certArn.ApplyT(func(arn interface{}) string { return arn.(string) }).(pulumi.StringOutput), nil
}
name: p-go-acm-validation
runtime: go
description: A minimal AWS Go Pulumi program
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment