Files
thamanyah/infrastructure/main.go
T
2026-08-12 19:13:38 +03:00

298 lines
9.5 KiB
Go

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 deployers 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
})
}