Terraform: Steps to create complete AWS S3 module
December 23, 2024

Terraform: Steps to create complete AWS S3 module

Terraform modules are used to simplify the process of organizing and reusing terraform code. Essentially, a module is a container for multiple resources that are logically connected and used together. This blog post explains the steps to create a Terraform module for managing AWS S3 buckets.


When to use mods?

  • You need to reuse infrastructure code in different projects
  • You want to keep your Terraform code organized and maintainable
  • You want to encapsulate the logic of a set of resources or a single resource
  • You want to minimize duplication
  • Your goal is to follow best practices


Step 1: Set up the module structure

I always try to follow this structure, especially if the mod is centered around a core service (like AWS S3 in this example):

module/
  |-- main.tf         # Core resource definitions
  |-- variables.tf    # Input variables
  |-- outputs.tf      # Output values
  |-- providers.tf    # Required providers
Enter full screen mode

Exit full screen mode


Step 2: Define resources in main.tf

  • this main.tf The file defines the core resources of the module. please notes I’ve practiced putting all mod related resources into main.tf files, because I tend to keep mods as short as possible, but I’ve seen a lot of mods that require multiple resources, and then each file is for a specific resource.
  • For our use case, the S3 bucket example, main.tf Will include:
  • Create an S3 bucket with optional lifecycle rules to ignore specific changes.
  • Public access blocks ensure that buckets are protected from public access.
  • Version control for data recovery.
  • Server-side encryption, configure encryption using KMS keys.
  • The bucket policy is optional and only applies under the following circumstances Enable_s3_bucket_policy set to real.

This is the content main.tf:

resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name

  lifecycle {
    ignore_changes = [
      tags_all,
    ]
  }
}

resource "aws_s3_bucket_public_access_block" "this" {
  bucket = aws_s3_bucket.this.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_versioning" "this" {
  bucket = aws_s3_bucket.this.id
  versioning_configuration {
    status = var.bucket_versioning
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
  bucket = aws_s3_bucket.this.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = var.kms_arn
    }
  }
}

resource "aws_s3_bucket_policy" "this" {

  count = var.enable_s3_bucket_policy ? 1 : 0

  bucket = aws_s3_bucket.this.id
  policy = var.s3_bucket_policy
}

Enter full screen mode

Exit full screen mode

It’s worth noting that you should determine which parts of your mod need to be configurable and use Terraform variables for those parts. For properties that are consistent across all module calls, you can define them directly within the module.
For example, the resource used to manage S3 bucket-level public access is set to real Because I know all my buckets need to be blocked from public access otherwise you can set a variable here and set it to real or Wrong Depends on what you need.
Additionally, if you are not 100% sure whether you want to disable S3 versioning for a specific bucket, you can use the defaults: real Define a variable and then override the value using in the module call Wrong.
There are use cases when you need more flexibility in setting up a specific resource, such as aws_s3_bucket_policy in this case. this Count This makes sense since we want the bucket policy to be Elective Based on variable values. Without it, you won’t be able to skip strategies if the resource is already defined in the mod.


Step 3: Enter variables (variables.tf)

this variables.tf File defines the input to the module. This allows users to customize the behavior of the module without modifying its internal logic. For this module, we have defined the following variables:

variable "bucket_name" {
  description = "The name of the bucket"
  type        = string
}

variable "bucket_versioning" {
  description = "Enable or disable bucket versioning"
  type        = bool
  default     = true
}

variable "enable_s3_bucket_policy" {
  description = "Whether to enable the S3 bucket policy"
  type        = bool
  default     = false
}

variable "s3_bucket_policy" {
  description = "value of the s3 bucket policy"
  type        = string
  default     = ""
}

variable "kms_arn" {
  description = "The ARN of the KMS key to use for server-side encryption"
  type        = string
}

Enter full screen mode

Exit full screen mode


Step 4: Required providers (providers.tf)

terraform {
  required_version = "~> 1.9.7"
  required_providers {
    aws = {
      source  = "aws"
      version = ">= 4.37"
    }
  }
}
Enter full screen mode

Exit full screen mode


Step 5: Output values ​​(outputs.tf)

this output.tf File defines the values ​​that the module will expose. This allows users to retrieve important information such as the bucket’s name and ARN.

output "bucket_name" {
  description = "The name of the bucket"
  value       = aws_s3_bucket.this.id
}

output "bucket_arn" {
  description = "The ARN of the bucket"
  value       = aws_s3_bucket.this.arn
}

Enter full screen mode

Exit full screen mode


Step 6: Use the mod

After building the mod, you can use it in the root configuration. Here’s an example of how to call this module:

module "test_bucket" {
  source = "../modules/s3"

  bucket_name = "${local.env}-test-${random_password.random_hash.result}"
  kms_arn     = aws_kms_key.test_key.arn
}

Enter full screen mode

Exit full screen mode

Since we have the option to build a bucket strategy as needed, the module call might look like this:

module "test_bucket" {
  source                  = "../modules/s3"
  bucket_name = "${local.env}-test-${random_password.random_hash.result}"
  kms_arn                 = aws_kms_key.this.arn
  enable_s3_bucket_policy = true
  s3_bucket_policy        = data.aws_iam_policy_document.allow_test_bucket_access.json
}

Enter full screen mode

Exit full screen mode

notes Double check the mod’s source code or the path it is located in, but always try to follow the practice of creating directories module and put all mods there.


in conclusion

Building a Terraform module involves organizing your code into a well-structured format and defining reusable components. In this example, we build a complete module for managing S3 buckets, including advanced features such as version control, encryption, and policies. By following these steps, you can build powerful and reusable modules to meet your infrastructure needs.

2024-12-23 21:30:47

Leave a Reply

Your email address will not be published. Required fields are marked *