Workaround to Create an S3 glacier with CDK + TS
Tags: todayilearned, tutorial, aws, typescript
Published: 2023-04-20 20:47:54

I was working on an archive project, hen a company had been deleted, and we need to move all the objects stored in **S3** to a **Glacier Archive**..

As the world isn't a fairy tale, the **S3 Glacier** is not supported in neither the **CDK** nor **CloudFormation** and doing this manually would not be a good solution in my context, so if you want to do the same as me you will face two possible solutions:

 - [Custom Resource](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html#custom-resources) construct. The Custom Resource's job is to call the SDK APIs to create/update/delete the vault. It will be invoked during the deployment lifecycle by CloudFormation. There are several flavours of Custom Resource. You may be able to use the [custom_resources.AwsCustomResource](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.custom_resources.AwsCustomResource.html) construct, which makes SDK calls easy. If more control is required, you will need to write your own Lambda in conjunction with a [cdk.CustomResource](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CustomResource.html).
 - A workaround using the `transitionDate` with the `new Date()` method on the **lifecycle rule** of the bucket

In my case I chose the **second solution**, so let's figure out how to do this in the code with CDK and Typescript.

## Alright, let's roll up our sleeves and get to work on this code

When I was working on this solution I'd identify some issues:

 - `Found mixed 'Date' and 'Days' based Transition actions in lifecycle rule`, this error occurs because you are mixing `transitionDate` and `transitionAfter` in your lifecycle rule.
 - `'Date' must be at midnight GMT`, this error occurs when you are just instantiating a `new Date()` without setting the hours as midnight

That being said, let's create a function to encapsulate this necessity:
```typescript
  private mountTodayAtMidnight() {
    const today = new Date()
    today.setUTCHours(0, 0, 0, 0)
    
    return today
  }
```

Now we can create our **bucket** with the `transitionDate` setting to today at midnight 

```typescript
import { Duration, RemovalPolicy } from 'aws-cdk-lib'
import { Cors } from 'aws-cdk-lib/aws-apigateway'
import { Bucket, StorageClass, HttpMethods } from 'aws-cdk-lib/aws-s3'
import { Construct } from 'constructs'

export default class ArchiveAssetsBucket extends Construct {
  constructor(scope: Construct, id: string) {
    super(scope, id)

    new Bucket(this, 'archive-assets-bkt', {
      bucketName: 'archive-assets',
      removalPolicy: RemovalPolicy.DESTROY,
      cors: [
        {
          allowedOrigins: Cors.ALL_ORIGINS,
          allowedMethods: [HttpMethods.GET, HttpMethods.PUT, HttpMethods.DELETE],
          allowedHeaders: ['*'],
        },
      ],
      versioned: true,
      lifecycleRules: [
        {
          enabled: true,
          abortIncompleteMultipartUploadAfter: Duration.days(1),
          noncurrentVersionExpiration: Duration.days(1),
          noncurrentVersionsToRetain: 1,
          transitions: [
            {
              storageClass: StorageClass.GLACIER,
              transitionDate: this.mountTodayAtMidnight(), //the magic happens here
            },
          ],
        },
      ]
    })
  }
}
```

I believe this isn't the best solution, but fits to me xD