amazon-web-servicesamazon-s3terraformterraform-provider-awsamazon-s3-access-points

Terraform: How to check if s3 access point exists before creating it?


I have a resource that creates multiple s3 access points depending on the input provided. The input is a map with s3 uri as the key and parsed bucket name as the value.

Example:

{
"s3://my_bucket/model1.tar.gz" -> "my_bucket",
"s3://my_bucket_2/model2.tar.gz" -> "my_bucket_2",
"s3://my_bucket/model3.tar.gz" -> "my_bucket"
}

I then use for_each to iterate through each element in the map to create s3 access points. Unfortunately, there are 2 "my_bucket" values in the map, which means it will attempt to create s3 access points for that designated bucket twice, and thus will error out with message:

AccessPointAlreadyOwnedByYou: Your previous request to create the named accesspoint succeeded and you already own it.

How can I check that the access point exists first before creating the resource?

Code Example:

resource "aws_s3_access_point" "s3_access_point" {
  for_each = var.create ? local.uri_bucket_map : {}

  bucket = each.value
  name   = format("%s-%s", each.value, "access-point")
}

output "s3_access_point_arn" {
  description = "The arn of the access point"
  value       = { for uri, ap in aws_s3_access_point.s3_access_point : uri => ap.arn }
}

Desired Output:

{
"s3://my_bucket/model1.tar.gz" -> <access point uri>,
"s3://my_bucket_2/model2.tar.gz" -> <access point uri>,
"s3://my_bucket/model3.tar.gz" -> <access point uri>
}

Solution

  • I would invert your uri_bucket_map:

    locals {
      uri_bucket_map_inverse = {
        for k,v in local.uri_bucket_map: v => k...
      }
    }
    

    giving:

    {
      "my_bucket" = [
        "s3://my_bucket/model1.tar.gz",
        "s3://my_bucket/model3.tar.gz",
      ]
      "my_bucket_2" = [
        "s3://my_bucket_2/model2.tar.gz",
      ]
    }
    

    then just create access points as:

    resource "aws_s3_access_point" "s3_access_point" {
      for_each = var.create ? local.uri_bucket_map_inverse : {}
    
      bucket = each.key
      name   = format("%s-%s", each.key, "access-point")
    }
    

    and the output would use both the APs and the inverted list map:

    output "s3_access_point_arn" {
      description = "The arn of the access point"
      value       = merge([for bucket_name, ap in aws_s3_access_point.s3_access_point:
                              { for uri in local.uri_bucket_map_inverse[bucket_name]: 
                                    uri => ap.arn
                              }  
                         ]...)
    }