> ## Documentation Index
> Fetch the complete documentation index at: https://cdkterrain-docs-canonical-asset-hashes.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Assets - CDK Terrain

> Asset constructs manage assets for resources that need them, such as template_file, S3 bucket objects, or Lambda function archive files.

In CDK Terrain (CDKTN) v0.4+, asset constructs can manage assets for resources that need them, such as template\_file, S3 bucket objects, or Lambda function archive files. You can use Terraform assets to move existing files or directories into your CDKTN application so that you can use them in resource definitions.

Assets are especially useful for:

* Copying over previously generated zip files with Lambda functions.
* Deploying static local files to S3.

## Usage Example

<Tip>Try the [Deploy Multiple Lambda Functions with TypeScript](/tutorials/lambda-functions) tutorial. This tutorial guides you through using a `TerraformAsset` to archive a Lambda function, uploading the archive to an S3 bucket, then deploying the Lambda function.</Tip>

The following example uses `TerraformAsset` to upload the contents of the specified directory into an S3 Bucket. The `TerraformAsset` is responsible for making sure the directory ends up in the correct output folder as a zip file that the `S3BucketObject` can reference.

The stack output directory in `cdktf.out` contains all of the assets that `TerraformAsset` needs. This is important for workflows where you use synthesized configurations with Terraform directly. For example, you would only need to upload the contents of the stack output folder to HCP Terraform or Terraform Enterprise.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Construct } from "constructs";
  import { TerraformStack } from "cdktn";
  import { TerraformAsset, AssetType } from "cdktn";
  import { AwsProvider } from "./.gen/providers/aws/provider";
  import { S3Bucket } from "./.gen/providers/aws/s3-bucket";
  import { S3BucketObject } from "./.gen/providers/aws/s3-bucket-object";
  import * as path from "path";

  export class AssetsStack extends TerraformStack {
    constructor(scope: Construct, name: string) {
      super(scope, name);

      new AwsProvider(this, "aws", {
        region: "us-west-2",
      });

      const bucket = new S3Bucket(this, "bucket", {
        bucket: "demo",
      });

      const asset = new TerraformAsset(this, "lambda-asset", {
        path: path.resolve(__dirname, "lambda"),
        type: AssetType.ARCHIVE, // if left empty it infers directory and file
      });

      new S3BucketObject(this, "lambda-archive", {
        bucket: bucket.bucket,
        key: asset.fileName,
        source: asset.path, // returns a posix path
      });
    }
  }
  ```

  ```python Python theme={null}
  from constructs import Construct
  from cdktn import TerraformAsset, TerraformStack, AssetType
  from imports.aws.provider import AwsProvider
  from imports.aws.s3_bucket import S3Bucket
  from imports.aws.s3_bucket_object import S3BucketObject
  import os


  class MyAssetStack(TerraformStack):
      def __init__(self, scope: Construct, name: str):
          super().__init__(scope, name)

          AwsProvider(self, 'aws', region='eu-central-1')

          bucket = S3Bucket(self, "bucket", bucket="demo")

          asset = TerraformAsset(self, "lambda-asset",
                      path=os.path.join(os.path.dirname(__file__), 'lambda'),
                      type=AssetType.ARCHIVE
                  )

          S3BucketObject(self, "lambda-archive",
              bucket=bucket.bucket,
              key=asset.file_name,
              source=asset.path
          )

  app = cdktn.App()
  MyAssetStack(app, "demo")
  app.synth()
  ```

  ```java Java theme={null}
  import software.constructs.Construct;
  import io.cdktn.cdktn.TerraformStack;
  import io.cdktn.cdktn.TerraformAsset;
  import io.cdktn.cdktn.TerraformAssetConfig;
  import io.cdktn.cdktn.AssetType;
  import imports.aws.provider.AwsProvider;
  import imports.aws.provider.AwsProviderConfig;
  import imports.aws.s3_bucket.S3Bucket;
  import imports.aws.s3_bucket.S3BucketConfig;
  import imports.aws.s3_bucket_object.S3BucketObject;
  import imports.aws.s3_bucket_object.S3BucketObjectConfig;

  import java.nio.file.Paths;

  public class MyAssetStack extends TerraformStack {
      public MyAssetStack(Construct scope, String name){
          super(scope, name);

          new AwsProvider(this,"aws", AwsProviderConfig.builder()
                  .region("eu-central-1")
                  .build()
          );

          S3Bucket bucket = new S3Bucket(this, "bucket", S3BucketConfig.builder()
                  .bucket("demo")
                  .build()
          );

          TerraformAsset asset = new TerraformAsset(this, "lambda-asset", TerraformAssetConfig.builder()
                  .path(Paths.get(System.getProperty("user.dir"), "lambda").toString())
                  .type(AssetType.ARCHIVE)
                  .build()
          );

          new S3BucketObject(this, "lambda-archive", S3BucketObjectConfig.builder()
                  .bucket(bucket.getBucket())
                  .key(asset.getFileName())
                  .source(asset.getPath())
                  .build()
          );

      }

      public static void main(String[] args) {
          final App app = new App();
          new MyAssetStack(app, "demo");
          app.synth();
      }
  }
  ```

  ```csharp C# theme={null}
  using System;
  using System.IO;
  using System.Collections.Generic;
  using System.Linq;
  using Constructs;
  using Io.Cdktn;
  using aws.Provider;
  using aws.S3Bucket;
  using aws.S3BucketObject;

  namespace Examples
  {
      class MyAssetStack : TerraformStack
      {
          public MyAssetStack(Construct scope, string name) : base(scope, name)
          {

              new AwsProvider(this, "aws", new AwsProviderConfig
              {
                  Region = "eu-central-1"
              });

              S3Bucket bucket = new S3Bucket(this, "bucket", new S3BucketConfig
              {
                  Bucket = "demo"
              });

              TerraformAsset asset = new TerraformAsset(this, "lambda-asset", new TerraformAssetConfig
              {
                  Path = Path.Join(Environment.CurrentDirectory, "lambda"),
                  Type = AssetType.ARCHIVE
              });

              new S3BucketObject(this, "lambda-archive", new S3BucketObjectConfig
              {
                  Bucket = bucket.Bucket,
                  Key = asset.FileName,
                  Source = asset.Path
              });
          }
      }
  }
  ```

  ```go Go theme={null}
  package main

  import (
  	"os"
  	"path"

  	"github.com/aws/constructs-go/constructs/v10"
  	"github.com/aws/jsii-runtime-go"
  	"github.com/open-constructs/cdk-terrain-go/cdktn"
  	aws "github.com/open-constructs/cdk-terrain/examples/go/documentation/generated/hashicorp/aws/provider"
  	"github.com/open-constructs/cdk-terrain/examples/go/documentation/generated/hashicorp/aws/s3bucket"
  	"github.com/open-constructs/cdk-terrain/examples/go/documentation/generated/hashicorp/aws/s3bucketobject"
  )

  func NewAssetsStack(scope constructs.Construct, name string) cdktn.TerraformStack {
  	stack := cdktn.NewTerraformStack(scope, &name)

  	cwd, _ := os.Getwd()

  	aws.NewAwsProvider(stack, jsii.String("aws"), &aws.AwsProviderConfig{
  		Region: jsii.String("us-east-1"),
  	})

  	bucket := s3bucket.NewS3Bucket(stack, jsii.String("bucket"), &s3bucket.S3BucketConfig{
  		Bucket: jsii.String("demo"),
  	})

  	asset := cdktn.NewTerraformAsset(stack, jsii.String("lambda-asset"), &cdktn.TerraformAssetConfig{
  		Path: jsii.String(path.Join(cwd, "lambda")),
  		Type: cdktn.AssetType_ARCHIVE,
  	})

  	s3bucketobject.NewS3BucketObject(stack, jsii.String("lambda-archive"), &s3bucketobject.S3BucketObjectConfig{
  		Bucket: bucket.Bucket(),
  		Key:    asset.FileName(),
  		Source: asset.Path(),
  	})

  	return stack
  }

  func main() {
  	app := cdktn.NewApp(nil)

  	NewAssetsStack(app, "assets")

  	app.Synth()
  }

  ```
</CodeGroup>

## Paths

Assets support both absolute and relative paths. Relative paths are always considered to be relative to your project's `cdktf.json` file.

## Asset Hashing

Each asset exposes an `assetHash`, and the hash is part of the synthesized `path`. When the asset's contents change, the hash — and therefore the path — changes, so resources referencing the asset pick up the new version. You can pass a fixed `assetHash` in the `TerraformAsset` configuration to control this yourself, for example to avoid updates from files that change on every build.

With the `canonicalAssetHashes` feature flag enabled, the hash covers everything that affects the emitted asset: file contents, names, permission bits, symlink targets, and — for directory assets — empty directories. Renaming a file, making a script executable, or retargeting a symlink each produce a new hash and asset path. For `ARCHIVE` assets the hash corresponds exactly to the emitted zip file.

Projects created with `cdktn init` have the flag enabled. Existing projects can opt in via `cdktf.json`:

```json theme={null}
{
  "context": {
    "canonicalAssetHashes": "true"
  }
}
```

<Note>
  Enabling the flag on an existing project changes all asset hashes and paths once, so referencing resources see a one-time update. See [Canonical Asset Hashes](/release/canonical-asset-hashes) for details.
</Note>
