DEV Community

Cover image for Upload Multiple Files to S3 using Terraform
Ashraf Minhaj
Ashraf Minhaj

Posted on

Upload Multiple Files to S3 using Terraform

Introduction

Whether to make a static website or just backing up files, we often need sending files to s3 buckets. The simplest way is to run s3 cp or s3 sync commands, but integrating these commands in CI/CD causes a problem, they will always run and even if there is no change, the files will be updated which can take a lot of time depending on file size.
As terraform is stateful, we can just use terraform aws resources to provision files to s3 bucket. I will show you three ways you can achieve this, and also tell which works better for which situation.
Let's get started.

Method1: Provision Files using s3 cp / s3 sync

We can use a terraform null resource to run s3 cp or s3 sync command like this -

resource "null_resource" "update_source_files" {
    provisioner "local-exec" {
        command     = "aws s3 sync app/ s3://my-s3-bucket" 
    }
}
Enter fullscreen mode Exit fullscreen mode

Bu there is a problem, this alone can't detect if a file content is changed. Thus it will not update files after the first apply.

Method2: Provision file using template_files module

Alternatively you can use this module to provision files -

module "template_files" {
    source                  = "hashicorp/dir/template"
    base_dir                = "app"
}

resource "aws_s3_object" "objects" {
    for_each                = "${module.template_files.files}"
    bucket                  = "s3://my-s3-bucket"
    key                     = each.key
    content_type            = each.value.content_type
    source                  = each.value.source_path
    content                 = each.value.content
    etag                    = each.value.digests.md5
}
Enter fullscreen mode Exit fullscreen mode

This works like a charm. But there's a problem. It takes a lot of times to plan and apply. The module traverses each files. For one of our frontends, we had around 1500 files and it took 20 minutes to plan apply in CICD.
If time is not a deal breaker to you then you can use this.

Method3: Provision files using s3_bucket_object

Finally, this is another way -

resource "aws_s3_bucket_object" "provision_source_files" {
    bucket  = "s3://my-s3-bucket"
    for_each = fileset("app/", "**/*.*")

    key    = each.value
    source = "app/${each.value}"
}
Enter fullscreen mode Exit fullscreen mode

fileset("app/", "*/.*") means to take every files in the app directory.

This works just like method2. But it's way faster. from 19 minutes it took only 6 minutes in whole.

Conclusion: Best method?

If you are using your cli, you can use the first method. I often use this as an on the go solution. But with null resource it becomes useless unless somehow file change is tracked. The second method is time consuming. So I would suggest the 3rd method.
But hey, no solution is perfect for everyone. That's why we have multiple solutions for a problem (every problems). So just try and find what suits better for your problem.

Best wishes.

Top comments (0)