Provision an AWS S3 bucket using Terraform

Terraform is a software tool used for infrastructure as codeInfrastructure as Code (IaC) is a technique to provision computing infrastructure using machine-readable code files. Terraform is used for building, editing, and versioning infrastructure safely and efficiently. Terraform efficiently manages the provisioning of infrastructure and does not ask how to provision the infrastructure; it just asks what to do and implement it.

First, we will investigate the code and then provision the infrastructure. Finally, let’s examine how to create a public S3 bucket. Then, we’ll upload an object to the bucket and finally attach a resource policy to publicly access the bucket objects.

Architecture diagram
Architecture diagram

Create an S3 bucket

We need to log into AWS using the provider block. The credentials access_key and the secret_key are used to log in to AWS. These credentials should be passed as environment variables to Terraform, but here, we will pass them in the file for learning purposes. To create an S3 bucket with public contents, we will specify ownership controls and allow public access to the bucket and bucket ACL permissions using resource blocks.

provider "aws"{
region="us-east-1"
# DO not pass access & secret access key in a file, this is for learning purposes only
# pass these values through CMD or in encrypted format.
access_key = "{{access_key_id}}"
secret_key = "{{secret_access_key}}"
}
resource "aws_s3_bucket" "my_bucket"{
bucket="my-bucket-<Unique_Number/Text_Here>"
}
resource "aws_s3_bucket_ownership_controls" "my_bucket" {
bucket = aws_s3_bucket.my_bucket.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
resource "aws_s3_bucket_public_access_block" "my_bucket" {
bucket = aws_s3_bucket.my_bucket.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
resource "aws_s3_bucket_acl" "my_bucket" {
depends_on = [
aws_s3_bucket_ownership_controls.my_bucket,
aws_s3_bucket_public_access_block.my_bucket,
]
bucket = aws_s3_bucket.my_bucket.id
acl = "public-read"
}

Explanation

Let’s take a look at the code above line by line and understand it carefully.

  • Lines 1–7: A provider block is used to specify that the Terraform configuration will be using AWS as the provider. It also sets the AWS region to us-east-1 and provides the AWS access key and secret key for authentication.

  • Lines 8–10: A resource block is used to create and name the S3 bucket.

  • Lines 12–17: This resource block configures ownership controls of the earlier created S3 bucket. It specifies that the object ownership is set to “BucketOwnerPreferred.” This ensures that the bucket owner owns objects uploaded to the bucket.

  • Lines 18–25: This block configures public access settings for the S3 bucket. The bucket attribute is used to reference the ID of the S3 bucket. Different attributes like block_public_acls, block_public_policy, etc., are set to false, to allow public access.

  • Lines 26–34: This block sets the ACL for the S3 bucket to “public-read.” The depends_on attribute ensures that this resource depends on the completion of the ownership controls and public access block configurations.

Upload an object to S3

Now that we have created a public S3 bucket on AWS. We will upload an object index.html to our S3 bucket.

resource "aws_s3_object" "my_object" {
bucket = aws_s3_bucket.my_bucket.bucket
key = "index.html"
source = "index.html"
content_type="text/html"
}

Explanation

Let’s take a look at the above resource block and understand it carefully.

  • Lines 1–6: The block uploads an object “index.html” to the S3 bucket my_bucket with the specified key. The source attribute specifies the local file to upload. The key attribute specifies the key (path) of the S3 object.

Attach a resource policy to the S3 bucket

We have created a public S3 bucket and uploaded index.html to it. To allow public access to the bucket's contents, we would attach a resource policy. We will attach a resource policy to the bucket using a data block.

data "aws_iam_policy_document" "allow_access" {
statement {
principals {
type = "*"
identifiers = ["*"]
}
actions = [
"s3:GetObject"
]
resources = [
aws_s3_bucket.my_bucket.arn,
"${aws_s3_bucket.my_bucket.arn}/*",
]
}
}
resource "aws_s3_bucket_policy" "allow_access" {
bucket = aws_s3_bucket.my_bucket.id
policy = data.aws_iam_policy_document.allow_access.json
}

Explanation

Let’s take a look at the above resource blocks and understand it carefully.

  • Lines 1–17: This block creates an IAM policy document to allow public access to objects in the S3 bucket using a data block. The policy allows the s3:GetObject permission to everyone (*) for the specified S3 bucket and its objects.

  • Lines 18–21: This block attaches the IAM policy document, allow_access, from the data block to the S3 bucket as a bucket policy. The bucket attribute references the S3 bucket created earlier and the policy attribute references the JSON representation of the IAM policy document.

Provision the infrastructure

Now that we have understood the code, let’s provision the infrastructure on AWS.

Note: Append a unqiue number/text at the end of the S3 bucket name on line 5, my-bucket-<Unique_Number/Text_Here>. Ensure that the bucket name is globally unique.

provider "aws"{
    region="us-east-1"
}
resource "aws_s3_bucket" "my_bucket"{
    bucket="my-bucket-4564564"
    }

resource "aws_s3_bucket_ownership_controls" "my_bucket" {
  bucket = aws_s3_bucket.my_bucket.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}
resource "aws_s3_bucket_public_access_block" "my_bucket" {
  bucket = aws_s3_bucket.my_bucket.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}
resource "aws_s3_bucket_acl" "my_bucket" {
  depends_on = [
    aws_s3_bucket_ownership_controls.my_bucket,
    aws_s3_bucket_public_access_block.my_bucket,
  ]

  bucket = aws_s3_bucket.my_bucket.id
  acl    = "public-read"
}
resource "aws_s3_object" "my_object" {
  bucket = aws_s3_bucket.my_bucket.bucket
  key    = "index.html"
  source = "index.html"
  content_type="text/html"
}
data "aws_iam_policy_document" "allow_access" {
  statement {
    principals {
      type        = "*"
      identifiers = ["*"]
    }

    actions = [
      "s3:GetObject"
    ]

    resources = [
      aws_s3_bucket.my_bucket.arn,
      "${aws_s3_bucket.my_bucket.arn}/*",
    ]
  }
}
resource "aws_s3_bucket_policy" "allow_access" {
  bucket = aws_s3_bucket.my_bucket.id
  policy = data.aws_iam_policy_document.allow_access.json
}
Create an S3 bucket using Terraform

Click the “Run” button and type the following command to initialize the terraform directory, terraform init is used to resolve and install dependencies. After initializing the directory, use terraform plan to see the execution plan; it’s an optional step. After this, execute the configuration file using terraform apply, it asks for confirmation, then type yes, and then Terraform will create the requested infrastructure.

terraform init
terraform apply

Putting it all together

We have provisioned the infrastructure; let’s log in to our AWS account to verify the creation of the bucket. We can also check that we have uploaded index.html to our S3 bucket; take a look at the contents of our S3 bucket, to verify that the object is uploaded to the bucket.

  • Search and select S3 in the AWS Management Console.

  • Go to the S3 buckets page and click the bucket name to open the bucket.

We can see the uploaded file index.html in the bucket.

Copyright ©2024 Educative, Inc. All rights reserved