# terraform-s3

## Overview

This module provides functionality to create Amazon S3 buckets with server-side encryption enabled. Consult the `modules/test` directory for working examples.

### Variables

There are two variables that must be provided for the module to function properly:
```
* env
* bucket_name
```

### Regions

As of version 3.0 of the terraform aws provider, the region variable is no longer supported. To create a bucket in a particular region, pass an AWS provider configuration into the module block as follows:
```
# Used for other services
provider "aws" {
  region = "us-east-1"
}

# Used specifically for S3 bucket in another region
provider "aws" {
  alias  = "aws_us_west_2"
  region = "us-west-2"
}

module "s3_bucket" {
    source = "git@github.com:theorchard/terraform-s3.git//modules/s3_bucket?ref=X.X.X"
    providers = {
      aws = aws.aws_us_west_2
    }

    env                 = "dev"
    bucket_name         = "my-bucket"
    s3_read_only_policy = true # Default is false

    apply_server_side_encryption_by_default = {

      sse_algorithm = "AES256"
    }
}
```

### Server-side Encryption

Server-side encryption should now be used by default. There are additional requirements needed if you're configuring server-side encryption with the follow type:

#### AES256

If you're planning to use AES256 as your encryption type, then you'll need to add a map called `apply_server_side_encryption_by_default` in your module block, and set `sse_algorithm` to `AES256`:
```
module "s3_bucket" {
    source = "../../modules/s3_bucket"
    
    env         = "dev"
    bucket_name = "my-bucket"

    apply_server_side_encryption_by_default = {

      sse_algorithm = "AES256"
    }
}
```

#### AWS-KMS

If you're planning to use AWS-KMS as your encryption type, there are two variables that are required for the module to setup KMS properly:
```
* kms_key_enabled = true | false
* kms_key_name    = string (should be similar to the bucket name)
```

You will also need to add a map called `apply_server_side_encryption_by_default` in your module block and set `sse_algorithm` to `aws:kms`:
```
module "s3_bucket" {
    source = "../../modules/s3_bucket"
    
    env          = "dev"
    bucket_name  = "my-bucket"
    
    kms_key_enabled = true
    kms_key_name = "test_key"

    apply_server_side_encryption_by_default = {
      
      kms_master_key_id = module.s3_bucket.s3_kms_encryption_key_id_output
      sse_algorithm     = "aws:kms"

    }
}
```

`kms_master_key_id` should always be set to `module.s3_bucket.s3_kms_encryption_key_id_output`, as this will automatically populate with the KMS key id created from the module.

### Lifecycle polices
Add this block if you want to enable lifecycle polices on the bucket. Each lifecycle block is optional.
Notes on versioning.
* With Versioning, version ID forms a key element to define uniqueness of an object within an bucket along with the bucket name and object key.
* If an object with the same key is uploaded the new uploaded object becomes the Current version and the previous object becomes the Non current version.
* If you overwrite an object, it results in a new object version in the bucket. You can always restore the previous version.
```
# Usage is in test/main.tf
  # This rule will move old versions of the objects to user's defined storage class. 
  lifecycle_rules_options_noncurrent_version_transition = [
    {
      prefix        = "folder_name1" # Using "" will lifecyle the whole bucket.
      enabled       = true
      days          = 30
      storage_class = "GLACIER" # Move non current objects to GLACIER in 30 days.
    },
    {
      prefix        = "folder_name2"
      enabled       = true
      days          = 90
      storage_class = "DEEP_ARCHIVE" # Move non transitions object to DEEP_ARCHIVE in 90 days.
    },
  ]
  
  # This rule will move current version of the objects to user's defined storage class. Non recommended if you are serving static web pages from s3.
  lifecycle_rules_options_current_version_transition = [
    {
      prefix        = "folder_name1" # Using "" will lifecyle the whole bucket.
      enabled       = true
      days          = 40
      storage_class = "GLACIER" # Move current objects to GLACIER in 40 days.
    },
    {
      prefix        = "folder_name2"
      enabled       = true
      days          = 100
      storage_class = "DEEP_ARCHIVE" # Move current objects to DEEP_ARCHIVE in 100 days.
    },
  ]

  # This rule will delete the old versions of the objects. Can be used if there is no need to keep the old versions of the object.
  lifecycle_rules_options_noncurrent_version_expiration = [
    {
      prefix  = "folder_name1" # Using "" will lifecyle the whole bucket.
      enabled = true
      days    = 50
    },
    {
      prefix  = "folder_name2"
      enabled = true
      days    = 200
    },
  ]
 
  # This rule will delete the current objects from the bucket. Use with CAUTION unless you have specific use case.
  lifecycle_rules_options_current_version_expiration = [
    {
      prefix  = "folder_name1" # Using "" will lifecyle the whole bucket.
      enabled = true
      days    = 50
    },
    {
      prefix  = "folder_name2"
      enabled = true
      days    = 300
    },
  ]
  
  #  If you initiate a multipart upload but never finish it, the in-progress upload occupies some storage space and will incur storage charges. However, these uploads are not visible when you list the contents of a bucket and have to be explicitly removed.
  lifecycle_rules_abort_incomplete_multipart_upload_days = [
    {
      prefix  = "folder_name1" # Using "" will lifecyle the whole bucket.
      enabled = true
      days    = 7
    },
    {
      prefix  = "folder_name2"
      enabled = true
      days    = 7
    },
    ]
    
```

### CORS rules
```

module "s3_bucket" {
    source = "git@github.com:theorchard/terraform-s3.git//modules/s3_bucket?ref=X.X.X"
    providers = {
      aws = aws.aws_us_west_2
    }

    env                 = "dev"
    bucket_name         = "my-bucket"
    s3_read_only_policy = true # Default is false

    apply_server_side_encryption_by_default = {

      sse_algorithm = "AES256"
    }
  
  # CORS Rules   
  bucket_cors_rule = [
    {
      allowed_headers = ["*"]
      allowed_methods = ["PUT"]
      allowed_origins = [
        "https://oa.devorch.com"
      ]
      expose_headers  = ["ETag"]
      max_age_seconds = 3000
    },
    {
      allowed_headers = ["*"]
      allowed_methods = ["PUT", "POST"]
      allowed_origins = [
        "https://phys.devorch.com"
      ]
      expose_headers  = ["ETag"]
      max_age_seconds = 2000
    }
  ]
}
```

### ACL (Access control list)
    If you're planning to use Access control list (acl), you need to add a map called `acl_policy_grants` in your module block. one thing to mention that, you need to add a map for each `permission`, because value of `permission` supports only string.
```
acl_policy_grants = [
    {
      id         = data.aws_canonical_user_id.current.id
      type       = "CanonicalUser"
      permission = "READ_ACP"
    },
    {
      id         = data.aws_canonical_user_id.current.id
      type       = "CanonicalUser"
      permission = "READ"
    },
    {
      id         = data.aws_canonical_user_id.current.id
      type       = "CanonicalUser"
      permission = "WRITE"
    },
    {
      id         = data.aws_canonical_user_id.current.id
      type       = "CanonicalUser"
      permission = "WRITE_ACP"
    }
  ]
}
```

### Replication configuration

#### When the bucket should be the source of replication
  If you're planning to use `replication_configuration` with hashicorp/aws > 4, you don't create replication role, it will be created automatically from `terraform-s3` module, you need to add a map called `apply_replication_configuration` in your module block.
```
  apply_replication_configuration = [{
    prefix             = ""
    destination_bucket = module.s3_destination_bucket.s3_bucket_name_output
    storage_class      = "STANDARD"
    enabled            = true
    delete_marker_replication_enabled = true
  }]
```

#### When the bucket should be the destination of replication
  This can only be done after the source bucket has been configured because the role needs to exist first.

  Then add a map called `apply_replication_destination_configuration` in your module block.
```
apply_replication_destination_configuration = [{
  replication_source_iam_role_arn = "arn:aws:iam::<AWS ACCOUNT ID>:role/source-bucket-role"
}]

### Resources

#### The module manages the following: 

* S3 buckets
* KMS keys that are created specifically for S3 buckets
* Bucket versioning
* IAM read-only policy for the bucket
* Bucket policy for PutObjects for both server-side encryption types
* Bucket lifecycle policies
* CORS rule
* Replication configuration 
* ACL (Access control list)
```

### Storage Lens Dashboard

The module supports optionally creating a Storage Lens dashboard for the bucket. This also enables additional S3 metrics in datadog if Cloudwatch metrics export is enabled.

To enable the dashboard with the default settings:

```
storage_lens_configuration = {
  enabled = true
}
```

See the [variables.tf](./modules/s3_bucket/variables.tf) file for all available options.
