package main import ( "encoding/json" "fmt" "io" "net/http" "strings" "github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudfront" "github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2" "github.com/pulumi/pulumi-aws/sdk/v7/go/aws/rds" "github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3" "github.com/pulumi/pulumi-postgresql/sdk/v3/go/postgresql" "github.com/pulumi/pulumi-random/sdk/v4/go/random" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) // currentPublicIP returns the caller's public IP, used to scope the RDS // security group to the machine that will actually run `pulumi up`. func currentPublicIP() (string, error) { resp, err := http.Get("https://checkip.amazonaws.com") if err != nil { return "", err } defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(resp.Body) if err != nil { return "", err } return strings.TrimSpace(string(body)), nil } const originId = "encoded-bucket-origin" // AWS managed "CachingOptimized" cache policy (same ID in every account/region). const cachingOptimizedPolicyId = "658327ea-f89d-4fab-a63d-7e88639e58f6" // newServiceDatabase creates a login role and a same-named database owned by // that role, so the service only ever touches its own database. func newServiceDatabase(ctx *pulumi.Context, provider *postgresql.Provider, service string) (*random.RandomPassword, error) { password, err := random.NewRandomPassword(ctx, service+"-db-password", &random.RandomPasswordArgs{ Length: pulumi.Int(24), Special: pulumi.Bool(true), MinUpper: pulumi.Int(1), MinLower: pulumi.Int(1), MinNumeric: pulumi.Int(1), MinSpecial: pulumi.Int(1), }) if err != nil { return nil, err } role, err := postgresql.NewRole(ctx, service+"-db-role", &postgresql.RoleArgs{ Name: pulumi.String(service), Login: pulumi.Bool(true), Password: password.Result, }, pulumi.Provider(provider)) if err != nil { return nil, err } _, err = postgresql.NewDatabase(ctx, service+"-database", &postgresql.DatabaseArgs{ Name: pulumi.String(service), Owner: role.Name, }, pulumi.Provider(provider)) if err != nil { return nil, err } return password, nil } func main() { pulumi.Run(func(ctx *pulumi.Context) error { // Create an AWS resource (S3 Bucket) that holds encoded assets bucket, err := s3.NewBucket(ctx, "encoded-bucket", nil) if err != nil { return err } // Keep the bucket private; CloudFront reaches it via Origin Access Control. _, err = s3.NewBucketPublicAccessBlock(ctx, "encoded-bucket-public-access-block", &s3.BucketPublicAccessBlockArgs{ Bucket: bucket.ID(), BlockPublicAcls: pulumi.Bool(true), BlockPublicPolicy: pulumi.Bool(true), IgnorePublicAcls: pulumi.Bool(true), RestrictPublicBuckets: pulumi.Bool(true), }) if err != nil { return err } oac, err := cloudfront.NewOriginAccessControl(ctx, "encoded-bucket-oac", &cloudfront.OriginAccessControlArgs{ Description: pulumi.String("OAC for encoded-bucket assets"), OriginAccessControlOriginType: pulumi.String("s3"), SigningBehavior: pulumi.String("always"), SigningProtocol: pulumi.String("sigv4"), }) if err != nil { return err } distribution, err := cloudfront.NewDistribution(ctx, "encoded-bucket-cdn", &cloudfront.DistributionArgs{ Enabled: pulumi.Bool(true), Comment: pulumi.String("Edge caching for encoded-bucket assets"), Origins: cloudfront.DistributionOriginArray{ &cloudfront.DistributionOriginArgs{ DomainName: bucket.BucketRegionalDomainName, OriginId: pulumi.String(originId), OriginAccessControlId: oac.ID(), S3OriginConfig: &cloudfront.DistributionOriginS3OriginConfigArgs{ OriginAccessIdentity: pulumi.String(""), }, }, }, DefaultCacheBehavior: &cloudfront.DistributionDefaultCacheBehaviorArgs{ TargetOriginId: pulumi.String(originId), ViewerProtocolPolicy: pulumi.String("redirect-to-https"), AllowedMethods: pulumi.ToStringArray([]string{"GET", "HEAD"}), CachedMethods: pulumi.ToStringArray([]string{"GET", "HEAD"}), Compress: pulumi.Bool(true), CachePolicyId: pulumi.String(cachingOptimizedPolicyId), }, Restrictions: &cloudfront.DistributionRestrictionsArgs{ GeoRestriction: &cloudfront.DistributionRestrictionsGeoRestrictionArgs{ RestrictionType: pulumi.String("none"), }, }, ViewerCertificate: &cloudfront.DistributionViewerCertificateArgs{ CloudfrontDefaultCertificate: pulumi.Bool(true), }, }) if err != nil { return err } // Allow only this distribution to read objects from the bucket. policy := pulumi.All(bucket.Arn, distribution.Arn).ApplyT(func(args []interface{}) (string, error) { bucketArn := args[0].(string) distributionArn := args[1].(string) doc := map[string]interface{}{ "Version": "2012-10-17", "Statement": []map[string]interface{}{ { "Sid": "AllowCloudFrontServicePrincipal", "Effect": "Allow", "Principal": map[string]string{"Service": "cloudfront.amazonaws.com"}, "Action": "s3:GetObject", "Resource": bucketArn + "/*", "Condition": map[string]interface{}{ "StringEquals": map[string]string{"AWS:SourceArn": distributionArn}, }, }, }, } b, err := json.Marshal(doc) return string(b), err }).(pulumi.StringOutput) _, err = s3.NewBucketPolicy(ctx, "encoded-bucket-policy", &s3.BucketPolicyArgs{ Bucket: bucket.ID(), Policy: policy, }) if err != nil { return err } // Small, single-AZ Postgres instance for the assignment: default VPC, // no public access, no Multi-AZ, no backups, destroyable without a snapshot. vpc, err := ec2.LookupVpc(ctx, &ec2.LookupVpcArgs{Default: pulumi.BoolRef(true)}) if err != nil { return err } subnets, err := ec2.GetSubnets(ctx, &ec2.GetSubnetsArgs{ Filters: []ec2.GetSubnetsFilter{ {Name: "vpc-id", Values: []string{vpc.Id}}, }, }) if err != nil { return err } dbSubnetGroup, err := rds.NewSubnetGroup(ctx, "postgres-subnet-group", &rds.SubnetGroupArgs{ SubnetIds: pulumi.ToStringArray(subnets.Ids), }) if err != nil { return err } myIP, err := currentPublicIP() if err != nil { return err } myIPCidr := fmt.Sprintf("%s/32", myIP) dbSecurityGroup, err := ec2.NewSecurityGroup(ctx, "postgres-sg", &ec2.SecurityGroupArgs{ Description: pulumi.String("Allow Postgres access from within the VPC and the deployer's IP"), VpcId: pulumi.String(vpc.Id), Ingress: ec2.SecurityGroupIngressArray{ &ec2.SecurityGroupIngressArgs{ Protocol: pulumi.String("tcp"), FromPort: pulumi.Int(5432), ToPort: pulumi.Int(5432), CidrBlocks: pulumi.ToStringArray([]string{vpc.CidrBlock}), }, &ec2.SecurityGroupIngressArgs{ Protocol: pulumi.String("tcp"), FromPort: pulumi.Int(5432), ToPort: pulumi.Int(5432), CidrBlocks: pulumi.ToStringArray([]string{myIPCidr}), Description: pulumi.String("Deployer IP, for the postgresql provider to create databases"), }, }, Egress: ec2.SecurityGroupEgressArray{ &ec2.SecurityGroupEgressArgs{ Protocol: pulumi.String("-1"), FromPort: pulumi.Int(0), ToPort: pulumi.Int(0), CidrBlocks: pulumi.ToStringArray([]string{"0.0.0.0/0"}), }, }, }) if err != nil { return err } // Generated once and kept in the stack state, so it stays stable across deploys // unless explicitly replaced. RDS master passwords can't contain '/', '@', '"', or spaces. dbPassword, err := random.NewRandomPassword(ctx, "postgres-db-password", &random.RandomPasswordArgs{ Length: pulumi.Int(24), Special: pulumi.Bool(true), OverrideSpecial: pulumi.String("!#$%&*()-_=+[]{}<>:?"), MinUpper: pulumi.Int(1), MinLower: pulumi.Int(1), MinNumeric: pulumi.Int(1), MinSpecial: pulumi.Int(1), }) if err != nil { return err } db, err := rds.NewInstance(ctx, "postgres-db", &rds.InstanceArgs{ Engine: pulumi.String("postgres"), EngineVersion: pulumi.String("16"), InstanceClass: pulumi.String("db.t3.micro"), AllocatedStorage: pulumi.Int(20), StorageType: pulumi.String("gp3"), StorageEncrypted: pulumi.Bool(true), DbName: pulumi.String("appdb"), Username: pulumi.String("postgres"), Password: dbPassword.Result, DbSubnetGroupName: dbSubnetGroup.Name, VpcSecurityGroupIds: pulumi.StringArray{dbSecurityGroup.ID()}, PubliclyAccessible: pulumi.Bool(true), MultiAz: pulumi.Bool(false), BackupRetentionPeriod: pulumi.Int(0), DeletionProtection: pulumi.Bool(false), SkipFinalSnapshot: pulumi.Bool(true), ApplyImmediately: pulumi.Bool(true), }) if err != nil { return err } pgProvider, err := postgresql.NewProvider(ctx, "postgres-provider", &postgresql.ProviderArgs{ Host: db.Address, Port: db.Port, Username: pulumi.String("postgres"), Password: dbPassword.Result, Sslmode: pulumi.String("require"), Superuser: pulumi.Bool(false), }) if err != nil { return err } _, err = newServiceDatabase(ctx, pgProvider, "cms") if err != nil { return err } _, err = newServiceDatabase(ctx, pgProvider, "discovery") if err != nil { return err } // Export the name of the bucket ctx.Export("bucketName", bucket.ID()) ctx.Export("cdnDomainName", distribution.DomainName) ctx.Export("dbEndpoint", db.Endpoint) ctx.Export("dbPassword", dbPassword.Result) return nil }) }