In this post I shall how to use the examples provided by Pulumi to setup a static website using AWS S3.

Pre requisities

  1. These steps assume you have already cloned https://github.com/pulumi/examples.git 2. Python version >=3 3. AWS account

Setup user in IAM

  • Create a user in AWS which has access to create S3 resources * Export the credentials.csv * You will need the AWS_ACCESS_KEY_ID * You will need the AWS_SECRET_ACCESS_KEY * Note down the AWS region you want to deploy into

Deployment configuration

  • Export the following environment variables

    export AWS_ACCESS_KEY_ID= export AWS_SECRET_ACCESS_KEY=

Initialize the stack

Make sure you are in the top level directory where you cloned the examples git repository

    cd aws-py-s3-folder
    pulumi stack init s3-website
    pulumi config set aws:region <aws_region>

It is important to note that if you run ‘pulumi stack init s3-website’ inside a directory without a project file i.e. the** Pulumi.yaml file**. You will get the following error message.

error: no Pulumi project found in the current working directory

Pulumi core files

  • Pulumi.yaml » main project file * main.py » entry point file * www » directory holding the static content which will be served in the S3 bucket * requirements.txt » a list of required Python modules

Once you have initialized the Pulumi stack you will also find an extra file called ‘Pulumi.S3-website.yaml’ which contains the AWS region you configured Pulumi to use.

You will notice the requirements.txt file containing the required Python modules.

    pulumi>=2.0.0,<3.0.0
    pulumi-aws>=2.0.0,<3.0.0

main.py

No HCL here except only Python for defining the deployment. As below you’ll notice the modules from pulumi are imported.

Definition of the S3 read policy, along with what files will be served.

The export of stack properties are defined at the bottom, these can later be inspected.

    import json
    import mimetypes
    import os
    
    from pulumi import export, FileAsset
    from pulumi_aws import s3
    
    web_bucket = s3.Bucket('s3-website-bucket', website={
        "index_document": "index.html"
    })
    
    content_dir = "www"
    for file in os.listdir(content_dir):
        filepath = os.path.join(content_dir, file)
        mime_type, _ = mimetypes.guess_type(filepath)
        obj = s3.BucketObject(file,
            bucket=web_bucket.id,
            source=FileAsset(filepath),
            content_type=mime_type)
    
    def public_read_policy_for_bucket(bucket_name):
        return json.dumps({
            "Version": "2012-10-17",
            "Statement": [{
                "Effect": "Allow",
                "Principal": "*",
                "Action": [
                    "s3:GetObject"
                ],
                "Resource": [
                    f"arn:aws:s3:::{bucket_name}/*",
                ]
            }]
        })
    
    bucket_name = web_bucket.id
    bucket_policy = s3.BucketPolicy("bucket-policy",
        bucket=bucket_name,
        policy=bucket_name.apply(public_read_policy_for_bucket))
    
    # Export the name of the bucket
    export('bucket_name',  web_bucket.id)
    export('website_url', web_bucket.website_endpoint)
    

Deployment

Python virtual env

This virtual environment is required to install dependency packages necessary for the deployment.

    python -m venv venv
    source venv/bin/activate
    pip install -r requirements.txt

Deploy

    pulumi up
    Previewing update (s3-website):
         Type                    Name                         Plan       
     +   pulumi:pulumi:Stack     aws-py-s3-folder-s3-website  create     
     +   ├─ aws:s3:Bucket        s3-website-bucket            create     
     +   ├─ aws:s3:BucketPolicy  bucket-policy                create     
     +   ├─ aws:s3:BucketObject  index.html                   create     
     +   ├─ aws:s3:BucketObject  favicon.png                  create     
     +   └─ aws:s3:BucketObject  python.png                   create     
     
    Resources:
        + 6 to create
    
    Do you want to perform this update?  [Use arrows to move, enter to select, type to filter]
      yes
    > no
      details
    Do you want to perform this update?  [Use arrows to move, enter to select, type to filter]
    > yes
      no
      details
    Do you want to perform this update? yes
    Updating (s3-website):
         Type                    Name                         Status      
     +   pulumi:pulumi:Stack     aws-py-s3-folder-s3-website  created     
     +   ├─ aws:s3:Bucket        s3-website-bucket            created     
     +   ├─ aws:s3:BucketObject  index.html                   created     
     +   ├─ aws:s3:BucketObject  python.png                   created     
     +   ├─ aws:s3:BucketObject  favicon.png                  created     
     +   └─ aws:s3:BucketPolicy  bucket-policy                created     
     
    Outputs:
        bucket_name: "s3-website-bucket-#####"
        website_url: "s3-website-bucket-#####".s3-website-#####".amazonaws.com"                                                                                                                                          
    
    Resources:
        + 6 created
    
    Duration: 12s
    
    Permalink: https://app.pulumi.com/####/aws-py-s3-folder/S3-website/updates/1
    

Inspect stack output

    pulumi stack output
    Current stack outputs (2):
        OUTPUT       VALUE
        bucket_name  s3-website-bucket-####
        website_url  s3-website-bucket-####.amazonaws.com

Open the website_url in your browser, and you will see the static contents served in S3.

Cleanup

    pulumi destroy -y
    Previewing destroy (s3-website):
         Type                    Name                         Plan       
     -   pulumi:pulumi:Stack     aws-py-s3-folder-s3-website  delete     
     -   ├─ aws:s3:BucketPolicy  bucket-policy                delete     
     -   ├─ aws:s3:BucketObject  python.png                   delete     
     -   ├─ aws:s3:BucketObject  favicon.png                  delete     
     -   ├─ aws:s3:BucketObject  index.html                   delete     
     -   └─ aws:s3:Bucket        s3-website-bucket            delete     
     
    Outputs:
      - bucket_name: "s3-website-bucket-#####""
      - website_url: "s3-website-bucket-#####".s3-website-#####".amazonaws.com"                                                                                                                                          
    
    Resources:
        - 6 to delete
    
    Destroying (s3-website):
         Type                    Name                         Status      
     -   pulumi:pulumi:Stack     aws-py-s3-folder-s3-website  deleted     
     -   ├─ aws:s3:BucketPolicy  bucket-policy                deleted     
     -   ├─ aws:s3:BucketObject  index.html                   deleted     
     -   ├─ aws:s3:BucketObject  python.png                   deleted     
     -   ├─ aws:s3:BucketObject  favicon.png                  deleted     
     -   └─ aws:s3:Bucket        s3-website-bucket            deleted     
     
    Outputs:
      - bucket_name: "s3-website-bucket-#####"
      - website_url: "s3-website-bucket-#####".s3-website-#####".amazonaws.com"                                                                                                                                          
    
    Resources:
        - 6 deleted
    
    Duration: 8s
    

Delete the stack

    pulumi stack rm s3-website -y
    Stack 's3-website' has been removed!

Also remove the IAM user you created for this demo.

Overall time: <1min to setup a basic S3 bucket and serve static content in AWS.