Ctrl + k

or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/com.pulumi/aws@7.16.x

docs

guides

data-pipeline.mdsecurity-best-practices.mdweb-application.md
common-patterns.mdgetting-started.mdindex.mdprovider.md
tile.json

tessl/maven-com-pulumi--aws

tessl install tessl/maven-com-pulumi--aws@7.16.0

Pulumi Java SDK for AWS providing strongly-typed Infrastructure-as-Code for 227 AWS service packages including compute, storage, databases, networking, security, analytics, machine learning, and more.

web-application.mddocs/guides/

Deploying a Production Web Application

This guide demonstrates how to deploy a complete, production-ready web application on AWS using Pulumi. The architecture includes high availability, security best practices, and scalability.

Architecture Overview

┌─────────────┐
                                 │  Route 53   │
                                 │     DNS     │
                                 └──────┬──────┘
                                        │
                                 ┌──────▼──────┐
                                 │ CloudFront  │
                                 │     CDN     │
                                 └──────┬──────┘
                                        │
                    ┌───────────────────┼───────────────────┐
                    │                   │                   │
             ┌──────▼──────┐    ┌──────▼──────┐    ┌──────▼──────┐
             │     ALB     │    │     S3      │    │     S3      │
             │ us-west-2a  │    │   Static    │    │    Logs     │
             └──────┬──────┘    │   Assets    │    └─────────────┘
                    │           └─────────────┘
         ┌──────────┴──────────┐
         │     Public Subnet    │
         │   ┌───────────────┐  │
         │   │ NAT Gateway   │  │
         └───┴───────┬───────┴──┘
                     │
         ┌───────────┴──────────┐
         │   Private Subnet     │
         │  ┌─────────────────┐ │
         │  │  ECS Fargate    │ │
         │  │  Application    │ │
         │  └────────┬────────┘ │
         └───────────┼──────────┘
                     │
         ┌───────────▼──────────┐
         │   Private Subnet     │
         │  ┌─────────────────┐ │
         │  │  RDS Database   │ │
         │  │   PostgreSQL    │ │
         │  └─────────────────┘ │
         └──────────────────────┘

Complete Implementation

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// Configuration
const config = new pulumi.Config();
const appName = "webapp";
const environment = pulumi.getStack();
const dbPassword = config.requireSecret("dbPassword");

// Tags for all resources
const tags = {
    Environment: environment,
    Application: appName,
    ManagedBy: "pulumi",
};

// ============================================================================
// 1. NETWORKING - VPC with Public and Private Subnets
// ============================================================================

// Create VPC
const vpc = new aws.ec2.Vpc("vpc", {
    cidrBlock: "10.0.0.0/16",
    enableDnsHostnames: true,
    enableDnsSupport: true,
    tags: { ...tags, Name: `${appName}-vpc` },
});

// Get availability zones
const azs = aws.getAvailabilityZones({
    state: "available",
});

// Create Internet Gateway
const igw = new aws.ec2.InternetGateway("igw", {
    vpcId: vpc.id,
    tags: { ...tags, Name: `${appName}-igw` },
});

// Create public subnets in two AZs for high availability
const publicSubnets = ["10.0.1.0/24", "10.0.2.0/24"].map((cidr, i) => {
    return new aws.ec2.Subnet(`public-subnet-${i}`, {
        vpcId: vpc.id,
        cidrBlock: cidr,
        availabilityZone: azs.then(az => az.names[i]),
        mapPublicIpOnLaunch: true,
        tags: { ...tags, Name: `${appName}-public-${i}`, Tier: "public" },
    });
});

// Create private subnets for application tier
const privateAppSubnets = ["10.0.11.0/24", "10.0.12.0/24"].map((cidr, i) => {
    return new aws.ec2.Subnet(`private-app-subnet-${i}`, {
        vpcId: vpc.id,
        cidrBlock: cidr,
        availabilityZone: azs.then(az => az.names[i]),
        tags: { ...tags, Name: `${appName}-private-app-${i}`, Tier: "application" },
    });
});

// Create private subnets for database tier
const privateDbSubnets = ["10.0.21.0/24", "10.0.22.0/24"].map((cidr, i) => {
    return new aws.ec2.Subnet(`private-db-subnet-${i}`, {
        vpcId: vpc.id,
        cidrBlock: cidr,
        availabilityZone: azs.then(az => az.names[i]),
        tags: { ...tags, Name: `${appName}-private-db-${i}`, Tier: "database" },
    });
});

// Create Elastic IP for NAT Gateway
const natEip = new aws.ec2.Eip("nat-eip", {
    domain: "vpc",
    tags: { ...tags, Name: `${appName}-nat-eip` },
});

// Create NAT Gateway in first public subnet
const natGw = new aws.ec2.NatGateway("nat-gw", {
    allocationId: natEip.id,
    subnetId: publicSubnets[0].id,
    tags: { ...tags, Name: `${appName}-nat-gw` },
});

// Public route table
const publicRt = new aws.ec2.RouteTable("public-rt", {
    vpcId: vpc.id,
    routes: [{
        cidrBlock: "0.0.0.0/0",
        gatewayId: igw.id,
    }],
    tags: { ...tags, Name: `${appName}-public-rt` },
});

// Associate public subnets with public route table
publicSubnets.forEach((subnet, i) => {
    new aws.ec2.RouteTableAssociation(`public-rta-${i}`, {
        subnetId: subnet.id,
        routeTableId: publicRt.id,
    });
});

// Private route table
const privateRt = new aws.ec2.RouteTable("private-rt", {
    vpcId: vpc.id,
    routes: [{
        cidrBlock: "0.0.0.0/0",
        natGatewayId: natGw.id,
    }],
    tags: { ...tags, Name: `${appName}-private-rt` },
});

// Associate private subnets with private route table
[...privateAppSubnets, ...privateDbSubnets].forEach((subnet, i) => {
    new aws.ec2.RouteTableAssociation(`private-rta-${i}`, {
        subnetId: subnet.id,
        routeTableId: privateRt.id,
    });
});

// ============================================================================
// 2. SECURITY GROUPS
// ============================================================================

// ALB Security Group - allows HTTP/HTTPS from internet
const albSg = new aws.ec2.SecurityGroup("alb-sg", {
    vpcId: vpc.id,
    description: "Security group for Application Load Balancer",
    ingress: [
        {
            protocol: "tcp",
            fromPort: 80,
            toPort: 80,
            cidrBlocks: ["0.0.0.0/0"],
            description: "Allow HTTP from internet",
        },
        {
            protocol: "tcp",
            fromPort: 443,
            toPort: 443,
            cidrBlocks: ["0.0.0.0/0"],
            description: "Allow HTTPS from internet",
        },
    ],
    egress: [{
        protocol: "-1",
        fromPort: 0,
        toPort: 0,
        cidrBlocks: ["0.0.0.0/0"],
        description: "Allow all outbound",
    }],
    tags: { ...tags, Name: `${appName}-alb-sg` },
});

// Application Security Group - allows traffic from ALB
const appSg = new aws.ec2.SecurityGroup("app-sg", {
    vpcId: vpc.id,
    description: "Security group for application containers",
    egress: [{
        protocol: "-1",
        fromPort: 0,
        toPort: 0,
        cidrBlocks: ["0.0.0.0/0"],
        description: "Allow all outbound",
    }],
    tags: { ...tags, Name: `${appName}-app-sg` },
});

// Allow app to receive traffic from ALB
new aws.ec2.SecurityGroupRule("app-from-alb", {
    type: "ingress",
    securityGroupId: appSg.id,
    sourceSecurityGroupId: albSg.id,
    protocol: "tcp",
    fromPort: 8080,
    toPort: 8080,
    description: "Allow traffic from ALB",
});

// Database Security Group - allows traffic from app
const dbSg = new aws.ec2.SecurityGroup("db-sg", {
    vpcId: vpc.id,
    description: "Security group for RDS database",
    egress: [{
        protocol: "-1",
        fromPort: 0,
        toPort: 0,
        cidrBlocks: ["0.0.0.0/0"],
        description: "Allow all outbound",
    }],
    tags: { ...tags, Name: `${appName}-db-sg` },
});

// Allow database to receive traffic from app
new aws.ec2.SecurityGroupRule("db-from-app", {
    type: "ingress",
    securityGroupId: dbSg.id,
    sourceSecurityGroupId: appSg.id,
    protocol: "tcp",
    fromPort: 5432,
    toPort: 5432,
    description: "Allow PostgreSQL from application",
});

// ============================================================================
// 3. APPLICATION LOAD BALANCER
// ============================================================================

// Create ALB
const alb = new aws.lb.LoadBalancer("alb", {
    loadBalancerType: "application",
    internal: false,
    subnets: publicSubnets.map(s => s.id),
    securityGroups: [albSg.id],
    enableDeletionProtection: environment === "production",
    tags: { ...tags, Name: `${appName}-alb` },
});

// Create Target Group
const targetGroup = new aws.lb.TargetGroup("app-tg", {
    port: 8080,
    protocol: "HTTP",
    vpcId: vpc.id,
    targetType: "ip",
    healthCheck: {
        enabled: true,
        path: "/health",
        port: "8080",
        protocol: "HTTP",
        healthyThreshold: 2,
        unhealthyThreshold: 3,
        timeout: 5,
        interval: 30,
        matcher: "200",
    },
    deregistrationDelay: 30,
    tags: { ...tags, Name: `${appName}-tg` },
});

// Create HTTP Listener (redirects to HTTPS in production)
const httpListener = new aws.lb.Listener("http-listener", {
    loadBalancerArn: alb.arn,
    port: 80,
    protocol: "HTTP",
    defaultActions: [{
        type: "redirect",
        redirect: {
            port: "443",
            protocol: "HTTPS",
            statusCode: "HTTP_301",
        },
    }],
});

// ============================================================================
// 4. ECS CLUSTER AND SERVICE
// ============================================================================

// Create ECS Cluster
const cluster = new aws.ecs.Cluster("cluster", {
    name: `${appName}-cluster`,
    settings: [{
        name: "containerInsights",
        value: "enabled",
    }],
    tags,
});

// Create IAM Role for ECS Task Execution
const taskExecutionRole = new aws.iam.Role("task-execution-role", {
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: "sts:AssumeRole",
            Effect: "Allow",
            Principal: {
                Service: "ecs-tasks.amazonaws.com",
            },
        }],
    }),
    tags,
});

// Attach AWS managed policy for ECS task execution
new aws.iam.RolePolicyAttachment("task-execution-policy", {
    role: taskExecutionRole.name,
    policyArn: "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
});

// Create IAM Role for ECS Task (application role)
const taskRole = new aws.iam.Role("task-role", {
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: "sts:AssumeRole",
            Effect: "Allow",
            Principal: {
                Service: "ecs-tasks.amazonaws.com",
            },
        }],
    }),
    tags,
});

// Create CloudWatch Log Group for application logs
const logGroup = new aws.cloudwatch.LogGroup("app-logs", {
    name: `/ecs/${appName}`,
    retentionInDays: 30,
    tags,
});

// Create Task Definition
const taskDefinition = new aws.ecs.TaskDefinition("app-task", {
    family: `${appName}-task`,
    networkMode: "awsvpc",
    requiresCompatibilities: ["FARGATE"],
    cpu: "512",
    memory: "1024",
    executionRoleArn: taskExecutionRole.arn,
    taskRoleArn: taskRole.arn,
    containerDefinitions: pulumi.all([logGroup.name, dbPassword]).apply(([logGroupName, password]) =>
        JSON.stringify([{
            name: "app",
            image: "nginx:latest", // Replace with your application image
            essential: true,
            portMappings: [{
                containerPort: 8080,
                protocol: "tcp",
            }],
            environment: [
                {
                    name: "ENVIRONMENT",
                    value: environment,
                },
                {
                    name: "DB_HOST",
                    value: "", // Will be set below
                },
                {
                    name: "DB_NAME",
                    value: "webapp",
                },
            ],
            secrets: [
                {
                    name: "DB_PASSWORD",
                    valueFrom: "", // Will be set from Secrets Manager
                },
            ],
            logConfiguration: {
                logDriver: "awslogs",
                options: {
                    "awslogs-group": logGroupName,
                    "awslogs-region": aws.getRegionOutput().name,
                    "awslogs-stream-prefix": "app",
                },
            },
            healthCheck: {
                command: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
                interval: 30,
                timeout: 5,
                retries: 3,
                startPeriod: 60,
            },
        }])
    ),
    tags,
});

// Create ECS Service
const service = new aws.ecs.Service("app-service", {
    name: `${appName}-service`,
    cluster: cluster.id,
    taskDefinition: taskDefinition.arn,
    desiredCount: 2,
    launchType: "FARGATE",
    platformVersion: "LATEST",
    networkConfiguration: {
        assignPublicIp: false,
        subnets: privateAppSubnets.map(s => s.id),
        securityGroups: [appSg.id],
    },
    loadBalancers: [{
        targetGroupArn: targetGroup.arn,
        containerName: "app",
        containerPort: 8080,
    }],
    healthCheckGracePeriodSeconds: 60,
    deploymentConfiguration: {
        maximumPercent: 200,
        minimumHealthyPercent: 100,
    },
    tags,
}, { dependsOn: [httpListener] });

// Configure Auto Scaling for ECS Service
const scalingTarget = new aws.appautoscaling.Target("app-scaling-target", {
    serviceNamespace: "ecs",
    resourceId: pulumi.interpolate`service/${cluster.name}/${service.name}`,
    scalableDimension: "ecs:service:DesiredCount",
    minCapacity: 2,
    maxCapacity: 10,
});

// CPU-based auto scaling
new aws.appautoscaling.Policy("cpu-scaling-policy", {
    name: `${appName}-cpu-scaling`,
    serviceNamespace: scalingTarget.serviceNamespace,
    resourceId: scalingTarget.resourceId,
    scalableDimension: scalingTarget.scalableDimension,
    policyType: "TargetTrackingScaling",
    targetTrackingScalingPolicyConfiguration: {
        targetValue: 70.0,
        predefinedMetricSpecification: {
            predefinedMetricType: "ECSServiceAverageCPUUtilization",
        },
        scaleInCooldown: 300,
        scaleOutCooldown: 60,
    },
});

// ============================================================================
// 5. RDS DATABASE
// ============================================================================

// Create DB Subnet Group
const dbSubnetGroup = new aws.rds.SubnetGroup("db-subnet-group", {
    subnetIds: privateDbSubnets.map(s => s.id),
    tags: { ...tags, Name: `${appName}-db-subnet-group` },
});

// Create RDS Instance
const db = new aws.rds.Instance("db", {
    identifier: `${appName}-db`,
    engine: "postgres",
    engineVersion: "15.5",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    maxAllocatedStorage: 100, // Enable storage autoscaling
    storageType: "gp3",
    storageEncrypted: true,
    dbName: "webapp",
    username: "dbadmin",
    password: dbPassword,
    dbSubnetGroupName: dbSubnetGroup.name,
    vpcSecurityGroupIds: [dbSg.id],
    publiclyAccessible: false,
    multiAz: environment === "production",
    backupRetentionPeriod: 7,
    backupWindow: "03:00-04:00",
    maintenanceWindow: "mon:04:00-mon:05:00",
    enabledCloudwatchLogsExports: ["postgresql", "upgrade"],
    performanceInsightsEnabled: true,
    performanceInsightsRetentionPeriod: 7,
    deletionProtection: environment === "production",
    skipFinalSnapshot: environment !== "production",
    finalSnapshotIdentifier: environment === "production" ? `${appName}-final-snapshot` : undefined,
    tags,
});

// ============================================================================
// 6. S3 BUCKET FOR STATIC ASSETS
// ============================================================================

// Create S3 bucket for static assets
const assetsBucket = new aws.s3.BucketV2("assets", {
    bucket: `${appName}-assets-${aws.getCallerIdentityOutput().accountId}`,
    tags,
});

// Enable versioning
new aws.s3.BucketVersioningV2("assets-versioning", {
    bucket: assetsBucket.id,
    versioningConfiguration: {
        status: "Enabled",
    },
});

// Block public access
new aws.s3.BucketPublicAccessBlock("assets-public-access-block", {
    bucket: assetsBucket.id,
    blockPublicAcls: true,
    blockPublicPolicy: true,
    ignorePublicAcls: true,
    restrictPublicBuckets: true,
});

// Enable encryption
new aws.s3.BucketServerSideEncryptionConfigurationV2("assets-encryption", {
    bucket: assetsBucket.id,
    rules: [{
        applyServerSideEncryptionByDefault: {
            sseAlgorithm: "AES256",
        },
        bucketKeyEnabled: true,
    }],
});

// Create Origin Access Identity for CloudFront
const oai = new aws.cloudfront.OriginAccessIdentity("oai", {
    comment: `OAI for ${appName}`,
});

// Bucket policy to allow CloudFront access
const assetsBucketPolicy = new aws.s3.BucketPolicy("assets-policy", {
    bucket: assetsBucket.id,
    policy: pulumi.all([assetsBucket.arn, oai.iamArn]).apply(([bucketArn, oaiArn]) =>
        JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Principal: {
                    AWS: oaiArn,
                },
                Action: "s3:GetObject",
                Resource: `${bucketArn}/*`,
            }],
        })
    ),
});

// ============================================================================
// 7. CLOUDFRONT CDN
// ============================================================================

// Create CloudFront distribution
const cdn = new aws.cloudfront.Distribution("cdn", {
    enabled: true,
    comment: `CDN for ${appName}`,
    origins: [
        {
            originId: "alb",
            domainName: alb.dnsName,
            customOriginConfig: {
                httpPort: 80,
                httpsPort: 443,
                originProtocolPolicy: "http-only",
                originSslProtocols: ["TLSv1.2"],
            },
        },
        {
            originId: "s3",
            domainName: assetsBucket.bucketRegionalDomainName,
            s3OriginConfig: {
                originAccessIdentity: oai.cloudfrontAccessIdentityPath,
            },
        },
    ],
    defaultCacheBehavior: {
        targetOriginId: "alb",
        viewerProtocolPolicy: "redirect-to-https",
        allowedMethods: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"],
        cachedMethods: ["GET", "HEAD"],
        compress: true,
        defaultTtl: 0,
        maxTtl: 0,
        minTtl: 0,
        forwardedValues: {
            queryString: true,
            cookies: { forward: "all" },
            headers: ["Host", "Origin", "Authorization"],
        },
    },
    orderedCacheBehaviors: [{
        pathPattern: "/static/*",
        targetOriginId: "s3",
        viewerProtocolPolicy: "redirect-to-https",
        allowedMethods: ["GET", "HEAD", "OPTIONS"],
        cachedMethods: ["GET", "HEAD"],
        compress: true,
        defaultTtl: 86400,
        maxTtl: 31536000,
        minTtl: 0,
        forwardedValues: {
            queryString: false,
            cookies: { forward: "none" },
        },
    }],
    priceClass: "PriceClass_100",
    restrictions: {
        geoRestriction: {
            restrictionType: "none",
        },
    },
    viewerCertificate: {
        cloudfrontDefaultCertificate: true,
        // For custom domain, use:
        // acmCertificateArn: certificate.arn,
        // sslSupportMethod: "sni-only",
        // minimumProtocolVersion: "TLSv1.2_2021",
    },
    tags,
});

// ============================================================================
// 8. ROUTE 53 DNS (Optional - for custom domain)
// ============================================================================

// Uncomment to use custom domain:
/*
const zone = aws.route53.getZone({
    name: "example.com",
});

const certificate = new aws.acm.Certificate("cert", {
    domainName: "app.example.com",
    validationMethod: "DNS",
    tags,
}, { provider: aws.usEast1Provider }); // ACM cert must be in us-east-1 for CloudFront

const certValidationRecord = new aws.route53.Record("cert-validation", {
    name: certificate.domainValidationOptions[0].resourceRecordName,
    type: certificate.domainValidationOptions[0].resourceRecordType,
    zoneId: zone.then(z => z.zoneId),
    records: [certificate.domainValidationOptions[0].resourceRecordValue],
    ttl: 60,
});

const certValidation = new aws.acm.CertificateValidation("cert-validation", {
    certificateArn: certificate.arn,
    validationRecordFqdns: [certValidationRecord.fqdn],
}, { provider: aws.usEast1Provider });

const dnsRecord = new aws.route53.Record("dns", {
    name: "app",
    type: "A",
    zoneId: zone.then(z => z.zoneId),
    aliases: [{
        name: cdn.domainName,
        zoneId: cdn.hostedZoneId,
        evaluateTargetHealth: false,
    }],
});
*/

// ============================================================================
// 9. CLOUDWATCH ALARMS AND MONITORING
// ============================================================================

// ALB Unhealthy Target alarm
new aws.cloudwatch.MetricAlarm("alb-unhealthy-targets", {
    name: `${appName}-unhealthy-targets`,
    comparisonOperator: "GreaterThanThreshold",
    evaluationPeriods: 2,
    metricName: "UnHealthyHostCount",
    namespace: "AWS/ApplicationELB",
    period: 300,
    statistic: "Average",
    threshold: 0,
    dimensions: {
        LoadBalancer: alb.arnSuffix,
        TargetGroup: targetGroup.arnSuffix,
    },
    alarmDescription: "Alert when targets are unhealthy",
    tags,
});

// ECS Service CPU alarm
new aws.cloudwatch.MetricAlarm("ecs-cpu-high", {
    name: `${appName}-ecs-cpu-high`,
    comparisonOperator: "GreaterThanThreshold",
    evaluationPeriods: 2,
    metricName: "CPUUtilization",
    namespace: "AWS/ECS",
    period: 300,
    statistic: "Average",
    threshold: 80,
    dimensions: {
        ClusterName: cluster.name,
        ServiceName: service.name,
    },
    alarmDescription: "Alert when CPU is high",
    tags,
});

// RDS CPU alarm
new aws.cloudwatch.MetricAlarm("rds-cpu-high", {
    name: `${appName}-rds-cpu-high`,
    comparisonOperator: "GreaterThanThreshold",
    evaluationPeriods: 2,
    metricName: "CPUUtilization",
    namespace: "AWS/RDS",
    period: 300,
    statistic: "Average",
    threshold: 80,
    dimensions: {
        DBInstanceIdentifier: db.identifier,
    },
    alarmDescription: "Alert when database CPU is high",
    tags,
});

// ============================================================================
// OUTPUTS
// ============================================================================

export const vpcId = vpc.id;
export const albDns = alb.dnsName;
export const cdnDomain = cdn.domainName;
export const cdnUrl = pulumi.interpolate`https://${cdn.domainName}`;
export const dbEndpoint = db.endpoint;
export const dbAddress = db.address;
export const clusterName = cluster.name;
export const serviceName = service.name;
export const assetsBucketName = assetsBucket.bucket;

Key Features

High Availability

  • Multi-AZ deployment across 2 availability zones
  • Application Load Balancer distributes traffic
  • Auto-scaling based on CPU utilization (2-10 instances)
  • Multi-AZ RDS for database redundancy (in production)

Security

  • Private subnets for application and database tiers
  • Security groups with least-privilege access
  • RDS encryption at rest
  • S3 encryption at rest
  • CloudFront with HTTPS enforcement
  • No public access to application or database

Performance

  • CloudFront CDN for global content delivery
  • S3 for static assets with caching
  • ECS Fargate for serverless compute
  • RDS Performance Insights enabled
  • Container Insights enabled

Monitoring

  • CloudWatch alarms for unhealthy targets
  • CloudWatch alarms for high CPU
  • Application logs in CloudWatch
  • RDS logs exported to CloudWatch
  • Container Insights for ECS

Scalability

  • Auto-scaling for ECS service
  • RDS storage auto-scaling
  • CloudFront global distribution
  • Stateless application design

Configuration

Create a Pulumi.dev.yaml configuration file:

config:
  aws:region: us-west-2
  webapp:dbPassword:
    secure: your-encrypted-password

Set the database password:

pulumi config set --secret dbPassword YourSecurePassword123!

Deployment

# Install dependencies
npm install

# Preview changes
pulumi preview

# Deploy infrastructure
pulumi up

# Get outputs
pulumi stack output cdnUrl
pulumi stack output albDns

Post-Deployment Steps

  1. Deploy your application: Update the task definition with your actual container image
  2. Configure DNS: Point your domain to the CloudFront distribution
  3. Set up SSL certificate: Create and validate an ACM certificate for your domain
  4. Upload static assets: Upload files to the S3 assets bucket
  5. Configure database: Run database migrations against the RDS endpoint
  6. Set up monitoring: Configure CloudWatch dashboards and SNS notifications

Cost Optimization

  • Use Fargate Spot for development environments
  • Enable S3 Intelligent-Tiering for static assets
  • Use RDS Reserved Instances for production
  • Configure S3 lifecycle policies to delete old versions
  • Use CloudFront with appropriate caching strategies

Cleanup

# Destroy all resources
pulumi destroy

# Remove stack
pulumi stack rm dev

Next Steps

  • Add WAF rules for CloudFront
  • Implement CI/CD pipeline
  • Add Redis/ElastiCache for caching
  • Set up backup automation
  • Configure CloudWatch dashboards
  • Add SNS notifications for alarms