Skip to content

aws.s3-bucket resource

Description

Bucket manages an S3 bucket and its configuration as one resource, the way CloudFormation models AWS::S3::Bucket. The name and whether Object Lock is enabled are fixed at creation, so a change to either replaces the bucket; tags and every configuration block reconcile in place. Each block is its own S3 operation under the hood -- CreateBucket, then PutBucketVersioning, PutPublicAccessBlock, and the rest -- but they are declared together here. A nil block leaves that facet of the bucket untouched. EmptyOnDestroy is a delete-time switch, not a property of the live bucket.

Source: internal/service/s3/bucket_rsrc.go:47

Example usage:

imports: {
  aws: 'github.com/cloudboss/unobin-library-aws'
}

resources: {
  example: aws.s3-bucket {
    # Set input fields here.
  }
}

Inputs

bucket

string

required

object-lock-enabled

optional(boolean)

tags

map(string)

versioning

optional(object)
optional(
  object({
    status: string
    mfa-delete: optional(string)
  })
)

public-access-block

optional(object)
optional(
  object({
    block-public-acls: optional(boolean)
    block-public-policy: optional(boolean)
    ignore-public-acls: optional(boolean)
    restrict-public-buckets: optional(boolean)
  })
)

ownership-controls

optional(object)
optional(
  object({
    object-ownership: string
  })
)

acl

optional(object)
optional(
  object({
    acl: string
  })
)

accelerate

optional(object)
optional(
  object({
    status: string
  })
)

encryption

optional(object)
optional(
  object({
    sse-algorithm: string
    kms-master-key-id: optional(string)
    bucket-key-enabled: optional(boolean)
  })
)

cors

optional(object)
optional(
  object({
    rules: list(
      object({
        id: optional(string)
        allowed-headers: list(string)
        allowed-methods: list(string)
        allowed-origins: list(string)
        expose-headers: list(string)
        max-age-seconds: optional(integer)
      })
    )
  })
)

website

optional(object)
optional(
  object({
    index-document: optional(
      object({
        suffix: string
      })
    )
    error-document: optional(
      object({
        key: string
      })
    )
    redirect-all-requests-to: optional(
      object({
        host-name: string
        protocol: optional(string)
      })
    )
    routing-rules: list(
      object({
        condition: optional(
          object({
            http-error-code-returned-equals: optional(string)
            key-prefix-equals: optional(string)
          })
        )
        redirect: optional(
          object({
            host-name: optional(string)
            http-redirect-code: optional(string)
            protocol: optional(string)
            replace-key-prefix-with: optional(string)
            replace-key-with: optional(string)
          })
        )
      })
    )
  })
)

logging

optional(object)
optional(
  object({
    target-bucket: string
    target-prefix: string
    target-grants: list(
      object({
        grantee: optional(
          object({
            type: string
            email-address: optional(string)
            id: optional(string)
            uri: optional(string)
          })
        )
        permission: string
      })
    )
    target-object-key-format: optional(
      object({
        partitioned-prefix: optional(
          object({
            partition-date-source: string
          })
        )
        simple-prefix: optional(boolean)
      })
    )
  })
)

lifecycle

optional(object)
optional(
  object({
    rules: list(
      object({
        id: string
        status: string
        filter: optional(
          object({
            prefix: optional(string)
            tag: optional(
              object({
                key: string
                value: string
              })
            )
            object-size-greater-than: optional(integer)
            object-size-less-than: optional(integer)
            and: optional(
              object({
                prefix: optional(string)
                tags: map(string)
                object-size-greater-than: optional(integer)
                object-size-less-than: optional(integer)
              })
            )
          })
        )
        expiration: optional(
          object({
            date: optional(string)
            days: optional(integer)
            expired-object-delete-marker: optional(boolean)
          })
        )
        transitions: list(
          object({
            date: optional(string)
            days: optional(integer)
            storage-class: string
          })
        )
        noncurrent-version-expiration: optional(
          object({
            noncurrent-days: optional(integer)
            newer-noncurrent-versions: optional(integer)
          })
        )
        noncurrent-version-transitions: list(
          object({
            noncurrent-days: optional(integer)
            newer-noncurrent-versions: optional(integer)
            storage-class: string
          })
        )
        abort-incomplete-multipart-upload: optional(
          object({
            days-after-initiation: optional(integer)
          })
        )
      })
    )
  })
)

object-lock

optional(object)
optional(
  object({
    rule: object({
      default-retention: object({
        mode: string
        days: optional(integer)
        years: optional(integer)
      })
    })
  })
)

empty-on-destroy

optional(boolean)

Input Constraints

Accelerate rules

accelerate status must be Enabled or Suspended.

Rule logic
When
input.accelerate.status != null
Require
input.accelerate.status == 'Enabled'
|| input.accelerate.status == 'Suspended'

Versioning rules

versioning status must be Enabled or Suspended.

Rule logic
When
input.versioning.status != null
Require
input.versioning.status == 'Enabled'
|| input.versioning.status == 'Suspended'

versioning mfa-delete must be Enabled or Disabled.

Rule logic
When
input.versioning.mfa-delete != null
Require
input.versioning.mfa-delete == 'Enabled'
|| input.versioning.mfa-delete == 'Disabled'

Acl rules

acl must be one of the S3 canned bucket ACLs.

Rule logic
When
input.acl.acl != null
Require
input.acl.acl == 'private'
|| input.acl.acl == 'public-read'
|| input.acl.acl == 'public-read-write'
|| input.acl.acl == 'authenticated-read'
|| input.acl.acl == 'aws-exec-read'
|| input.acl.acl == 'bucket-owner-read'
|| input.acl.acl == 'bucket-owner-full-control'
|| input.acl.acl == 'log-delivery-write'

Ownership controls rules

object-ownership must be BucketOwnerPreferred, ObjectWriter, or BucketOwnerEnforced.

Rule logic
When
input.ownership-controls.object-ownership != null
Require
input.ownership-controls.object-ownership == 'BucketOwnerPreferred'
|| input.ownership-controls.object-ownership == 'ObjectWriter'
|| input.ownership-controls.object-ownership == 'BucketOwnerEnforced'

Encryption rules

sse-algorithm must be AES256, aws:kms, or aws:kms:dsse.

Rule logic
When
input.encryption.sse-algorithm != null
Require
input.encryption.sse-algorithm == 'AES256'
|| input.encryption.sse-algorithm == 'aws:kms'
|| input.encryption.sse-algorithm == 'aws:kms:dsse'

kms-master-key-id requires a KMS sse-algorithm.

Rule logic
When
input.encryption.kms-master-key-id != null
Require
input.encryption.sse-algorithm == 'aws:kms'
|| input.encryption.sse-algorithm == 'aws:kms:dsse'

Object lock rules

object-lock requires object-lock-enabled to be true.

Rule logic
When
input.object-lock != null
Require
input.object-lock-enabled == true

object-lock mode must be GOVERNANCE or COMPLIANCE.

Rule logic
When
input.object-lock.rule.default-retention.mode != null
Require
input.object-lock.rule.default-retention.mode == 'GOVERNANCE'
|| input.object-lock.rule.default-retention.mode == 'COMPLIANCE'

At most one of object-lock.rule.default-retention.days or object-lock.rule.default-retention.years.

object-lock retention requires days or years.

Rule logic
When
input.object-lock != null
Require
input.object-lock.rule.default-retention.days != null
|| input.object-lock.rule.default-retention.years != null

Website rules

Forbidden together: website.redirect-all-requests-to, website.index-document, website.error-document, and website.routing-rules.

website requires index-document or redirect-all-requests-to.

Rule logic
When
input.website != null
Require
input.website.index-document != null
|| input.website.redirect-all-requests-to != null

redirect-all-requests-to protocol must be http or https.

Rule logic
When
input.website.redirect-all-requests-to.protocol != null
Require
input.website.redirect-all-requests-to.protocol == 'http'
|| input.website.redirect-all-requests-to.protocol == 'https'

a routing rule requires a redirect.

Rule logic
For each
input.website.routing-rules
Require
@each.value.redirect != null

a routing rule redirect protocol must be http or https.

Rule logic
For each
input.website.routing-rules
When
@each.value.redirect.protocol != null
Require
@each.value.redirect.protocol == 'http'
|| @each.value.redirect.protocol == 'https'

At most one of website.routing-rules[*].redirect.replace-key-prefix-with or website.routing-rules[*].redirect.replace-key-with.

Cors rules

cors holds at most 100 rules.

Rule logic
Require
input.cors.rules == null
|| @core.length(input.cors.rules) <= 100

a cors rule requires allowed-methods and allowed-origins.

Rule logic
For each
input.cors.rules
Require
((@each.value.allowed-methods != null) && (@core.length(@each.value.allowed-methods) >= 1))
&& ((@each.value.allowed-origins != null) && (@core.length(@each.value.allowed-origins) >= 1))

an allowed method must be GET, PUT, POST, DELETE, or HEAD.

Rule logic
For each
@rule in input.cors.rules
@m in @rule.value.allowed-methods
Require
@m.value == 'GET'
|| @m.value == 'PUT'
|| @m.value == 'POST'
|| @m.value == 'DELETE'
|| @m.value == 'HEAD'

Lifecycle rules

a lifecycle rule status must be Enabled or Disabled.

Rule logic
For each
input.lifecycle.rules
Require
@each.value.status == 'Enabled'
|| @each.value.status == 'Disabled'

a lifecycle rule needs at least one action.

Rule logic
For each
input.lifecycle.rules
Require
@each.value.expiration != null
|| @each.value.transitions != null
|| @each.value.noncurrent-version-expiration != null
|| @each.value.noncurrent-version-transitions != null
|| @each.value.abort-incomplete-multipart-upload != null

At most one of lifecycle.rules[*].filter.prefix, lifecycle.rules[*].filter.tag, lifecycle.rules[*].filter.object-size-greater-than, lifecycle.rules[*].filter.object-size-less-than, or lifecycle.rules[*].filter.and.

At most one of lifecycle.rules[*].expiration.date, lifecycle.rules[*].expiration.days, or lifecycle.rules[*].expiration.expired-object-delete-marker.

an expiration needs date, days, or expired-object-delete-marker.

Rule logic
For each
input.lifecycle.rules
When
@each.value.expiration != null
Require
@each.value.expiration.date != null
|| @each.value.expiration.days != null
|| @each.value.expiration.expired-object-delete-marker != null

a transition storage-class must be GLACIER, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, DEEP_ARCHIVE, or GLACIER_IR.

Rule logic
For each
@rule in input.lifecycle.rules
@tr in @rule.value.transitions
Require
@tr.value.storage-class == 'GLACIER'
|| @tr.value.storage-class == 'STANDARD_IA'
|| @tr.value.storage-class == 'ONEZONE_IA'
|| @tr.value.storage-class == 'INTELLIGENT_TIERING'
|| @tr.value.storage-class == 'DEEP_ARCHIVE'
|| @tr.value.storage-class == 'GLACIER_IR'

At most one of lifecycle.rules[*].transitions[*].date or lifecycle.rules[*].transitions[*].days.

a transition needs date or days.

Rule logic
For each
@rule in input.lifecycle.rules
@tr in @rule.value.transitions
Require
@tr.value.date != null
|| @tr.value.days != null

a transition storage-class must be GLACIER, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, DEEP_ARCHIVE, or GLACIER_IR.

Rule logic
For each
@rule in input.lifecycle.rules
@tr in @rule.value.noncurrent-version-transitions
Require
@tr.value.storage-class == 'GLACIER'
|| @tr.value.storage-class == 'STANDARD_IA'
|| @tr.value.storage-class == 'ONEZONE_IA'
|| @tr.value.storage-class == 'INTELLIGENT_TIERING'
|| @tr.value.storage-class == 'DEEP_ARCHIVE'
|| @tr.value.storage-class == 'GLACIER_IR'

Logging rules

a target grant permission must be FULL_CONTROL, READ, or WRITE.

Rule logic
For each
input.logging.target-grants
Require
@each.value.permission == 'FULL_CONTROL'
|| @each.value.permission == 'READ'
|| @each.value.permission == 'WRITE'

a grantee type must be CanonicalUser, AmazonCustomerByEmail, or Group.

Rule logic
For each
input.logging.target-grants
When
@each.value.grantee.type != null
Require
@each.value.grantee.type == 'CanonicalUser'
|| @each.value.grantee.type == 'AmazonCustomerByEmail'
|| @each.value.grantee.type == 'Group'

At most one of logging.target-object-key-format.partitioned-prefix or logging.target-object-key-format.simple-prefix.

target-object-key-format requires partitioned-prefix or simple-prefix.

Rule logic
When
input.logging.target-object-key-format != null
Require
input.logging.target-object-key-format.partitioned-prefix != null
|| input.logging.target-object-key-format.simple-prefix != null

partition-date-source must be EventTime or DeliveryTime.

Rule logic
When
input.logging.target-object-key-format.partitioned-prefix.partition-date-source != null
Require
input.logging.target-object-key-format.partitioned-prefix.partition-date-source == 'EventTime'
|| input.logging.target-object-key-format.partitioned-prefix.partition-date-source == 'DeliveryTime'

Outputs

arn

string

bucket-region

string

bucket-domain-name

string

bucket-regional-domain-name

string