Creating S3 Buckets using CloudFormation via AWS CLI
Tags: cloudformation, s3
Published: 2024-03-01 23:21:21

Whether you're setting up a new stack using CloudFormation or trying to decipher an existing stack, understanding CloudFormation syntax is crucial for building, updating, and maintaining AWS resources effectively. In this tutorial, we'll learn by doing. We're going to create two S3 buckets using CloudFormation through the AWS-CLI.




## Prerequisites
Before diving into this tutorial, ensure you have the following prerequisites in place:

- AWS CLI installed on your machine.
- AWS credentials configured with the necessary permissions.


## Understanding the Process Flow

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1cogbz1jwctfnig9iocc.png)


The flow chart above outlines our process:

1. We start by creating an S3 bucket to store our CloudFormation templates.
2. Next, we create an aggregated CloudFormation template.
3. Finally, we deploy a stack with two nested stacks, each creating an S3 bucket.

---

### Step 1: Create an S3 Bucket and Prepare the Template

First, we'll create an S3 bucket that will store our CloudFormation templates. Execute the following command, replacing <your-bucket-name> and <your-region> with your unique bucket name and desired AWS region, respectively. Remember, S3 bucket names must be globally unique.


```bash
aws s3 mb s3://<your-bucket-name> --region <your-region>
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5nyx9wx67sby11esk6it.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/037xg1exj2r4b3ay9yrz.png)


After creating the S3 bucket, set up your project directory with `main.json` and `S3Template.json` files.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6iasjg31q5t7in23key.png)


### S3Template.json (Nested Stack Template)
```json
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "S3 bucket template", // Description of what this template does
    "Parameters": {
        "Environment": { "Type": "String" }, // Parameter for environment (e.g., dev, prod)
        "ProjectName": { "Type": "String" }, // Parameter for project name
        "Application": { "Type": "String" }, // Parameter for application name
        "ExpirationInDays": { "Type": "Number" } // Parameter for setting lifecycle expiration in days
    },
    "Conditions": {
        "LifeCycleCondition": { // Condition to check if lifecycle rule is needed
            "Fn::Not": [
                { "Fn::Equals": [{ "Ref": "ExpirationInDays" }, 0] }
            ]
        }
    },
    "Resources": {
        "CFS3Bucket": { // Defines an S3 bucket resource
            "Type": "AWS::S3::Bucket",
            "Properties": {
                "BucketName": { // Bucket name created from a combination of parameters
                    "Fn::Join": ["-", [{ "Ref": "ProjectName" }, { "Ref": "Environment" }, { "Ref": "Application" }]]
                },
                "LifecycleConfiguration": { // Sets lifecycle configuration based on the condition
                    "Fn::If": [
                        "LifeCycleCondition",
                        { "Rules": [{ "ExpirationInDays": { "Ref": "ExpirationInDays" }, "Status": "Enabled" }] },
                        { "Ref": "AWS::NoValue" }
                    ]
                },
                "PublicAccessBlockConfiguration": { // Blocks public access to the bucket
                    "BlockPublicAcls": "true",
                    "BlockPublicPolicy": "true",
                    "IgnorePublicAcls": "true",
                    "RestrictPublicBuckets": "true"
                }
            }
        }
    },
    "Outputs": {
        "CFS3Bucket": { "Value": { "Ref": "CFS3Bucket" } }, // Outputs the name of the created bucket
        "CFS3BucketArn": { "Value": { "Fn::GetAtt": ["CFS3Bucket", "Arn"] } } // Outputs the ARN of the created bucket
    }
}


```


### main.json (Main Stack Template)
```json
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "CF template to demonstrate nested stack creation", // Description of what this template does
    "Parameters": {
        "Environment": { "Type": "String", "Default": "dev" }, // Default parameter for environment
        "ProjectName": { "Type": "String", "Default": "gritcoding" } // Default parameter for project name
    },
    "Resources": {
        "S3CatStack": { // Defines a nested stack for 'cat' application
            "Type": "AWS::CloudFormation::Stack",
            "Properties": {
                "Parameters": { // Parameters passed to the nested stack
                    "ProjectName": { "Ref": "ProjectName" },
                    "Environment": { "Ref": "Environment" },
                    "Application": "cat",
                    "ExpirationInDays": 0
                },
                "TemplateURL": "S3Template.json" // URL of the template for the nested stack. Initially, this points to the local S3Template.json file.
            }
        },
        "S3DogStack": { // Defines a nested stack for 'dog' application
            "Type": "AWS::CloudFormation::Stack",
            "Properties": {
                "Parameters": { // Parameters passed to the nested stack
                    "ProjectName": { "Ref": "ProjectName" },
                    "Environment": { "Ref": "Environment" },
                    "Application": "dog",
                    "ExpirationInDays": 0
                },
                "TemplateURL": "S3Template.json" // URL of the template for the nested stack. Initially, this points to the local S3Template.json file.
            }
        }
    }
}

```

#### Important Note on Parameters
When using nested stacks in CloudFormation, it's crucial to ensure that all required parameters in the nested stack (in this case, S3Template.json) are provided by the main stack (main.json). If the main stack doesn't pass these parameters, the CloudFormation deployment will fail. 

---

### Step 2: Package the CloudFormation Template

In your project's root directory, package the `main.json` and `S3Template.json` files into a single template using the following command:

```bash
aws --region <your-region> cloudformation package --s3-bucket <your-bucket-name> --template-file ./main.json --output-template-file ./packaged-template.json --use-json
```

Check your directory for the newly created `packaged-template.json` file.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8a5putn60qlq2mbaqzom.png)

---

### Step 3: Deploy the Packaged Template
Now, deploy the stack to AWS CloudFormation using the command below:

```bash
aws cloudformation deploy --template-file ./packaged-template.json --stack-name <your-stack-name>
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6utenf711vtxbsmifwg7.png)

Visit your AWS console, check the CloudFormation and S3 sections, and you should see the newly created stacks and S3 buckets.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kw1mhmsxlvb66zajkhe5.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/llny2gb4qtdgomu8fk8x.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0uc9d2h9g3y2set27pii.png)


To delete the stack, use:

```
aws cloudformation delete-stack --stack-name <your stack name>
```
Confirm the stack's removal in the AWS console.

---

## Learn More

This tutorial aimed to shed light on CloudFormation syntax. For those who prefer hands-on learning, you can fork and explore the source code from this repository: [GitHub](https://github.com/grit-coding/DevToCodeSnippets/tree/main/tech-tutorials/infrastructure/cloudformation/create-two-nested-buckets). And if you found this guide or the repository useful, a star or a reaction would be much appreciated—it's a simple way to show support and keeps me inspired to share more content like this.😄

For further reading and official AWS documentation, refer to:

- [AWS CloudFormation User Guide](https://docs.aws.amazon.com/cloudformation/index.html)
- [AWS CloudFormation Template Reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-reference.html)
- [AWS S3 Bucket - AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html)
- [AWS CloudFormation Stack - AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html)
- [Intrinsic Function Reference - AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html)
- [AWS CLI Command Reference](https://docs.aws.amazon.com/cli/latest/index.html)