> ## Documentation Index
> Fetch the complete documentation index at: https://cdktn.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Functions - CDK Terrain

> Learn when to use built-in Terraform functions to transform or combine values.

Terraform provides a set of built-in functions that transform and combine values within Terraform configurations. The [Terraform function documentation](https://developer.hashicorp.com/terraform/language/functions) contains a complete list. You can also use your editor autocompletion on the `Fn` object to find available options.

Functions can handle normal and [token](/concepts/tokens) values and will return either tokenized values or `IResolvable` values.

## When to Use Terraform Functions

Use Terraform functions when you need to calculate new values based on runtime values that are unknown before Terraform (or OpenTofu) applies a configuration. For example, instance IDs that cloud providers assign on creation.

When inputs are available before [synthesizing your code](/cli-reference/commands#synth) (e.g. local files), we recommend transforming the values with your preferred programming language.

## Usage Example

The following example uses a Data Source from the AWS Provider to fetch the Availability Zones of the given region. As this data is unknown until Terraform applies the configuration, this CDKTN application uses both [Terraform Outputs](/concepts/variables-and-outputs#output-values) and the Terraform [`element`](https://developer.hashicorp.com/terraform/language/functions/element) function.

The `element` function gets the first element from the list of Availability Zone names.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { TerraformStack, TerraformVariable } from "cdktn";
  import { Construct } from "constructs";
  import { AwsProvider } from "@cdktn/provider-aws/lib/aws-provider";
  import { Fn, TerraformOutput } from "cdktn";
  import { DataAwsAvailabilityZones } from "@cdktn/provider-aws/lib/data-aws-availability-zones";
  export class FunctionsStack extends TerraformStack {
    constructor(scope: Construct, id: string) {
      super(scope, id);

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

      const zones = new DataAwsAvailabilityZones(this, "zones", {
        state: "available",
      });
      new TerraformOutput(this, "first-zone", {
        value: Fn.element(zones.names, 0),
      });
    }
  }
  ```

  ```java Java theme={null}
  import io.cdktn.cdktn.Fn;
  import io.cdktn.cdktn.TerraformVariable;
  import io.cdktn.cdktn.TerraformVariableConfig;
  import io.cdktn.cdktn.TerraformOutput;
  import io.cdktn.cdktn.TerraformOutputConfig;
  import imports.aws.data_aws_availability_zones.DataAwsAvailabilityZones;
  import imports.aws.data_aws_availability_zones.DataAwsAvailabilityZonesConfig;

          DataAwsAvailabilityZones zones = new DataAwsAvailabilityZones(this, "zones",
                  DataAwsAvailabilityZonesConfig.builder()
                          .state("available")
                          .build());

          new TerraformOutput(this, "first-zone", TerraformOutputConfig.builder()
                  .value(Fn.element(zones.getNames(), 0))
                  .build());
  ```

  ```python Python theme={null}
  from cdktn import Fn, TerraformOutput
  from imports.aws.provider import AwsProvider
  from imports.aws.data_aws_availability_zones import DataAwsAvailabilityZones

          zones = DataAwsAvailabilityZones(self, 'zones',
                      state="available",
                  )

          TerraformOutput(self, 'first-zone',
              value=Fn.element(zones.names, 0)
          )

  ```

  ```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.DataAwsAvailabilityZones;

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

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

              DataAwsAvailabilityZones zones = new DataAwsAvailabilityZones(this, "zones", new DataAwsAvailabilityZonesConfig
              {
                  State = "available"
              });

              new TerraformOutput(this, "first-zone", new TerraformOutputConfig
              {
                  Value = Fn.Element(zones.Names, 0)
              });
          }
      }
  }
  ```

  ```go Go theme={null}
  import (
  	"github.com/aws/constructs-go/constructs/v10"
  	"github.com/aws/jsii-runtime-go"
  	"github.com/open-constructs/cdk-terrain-go/cdktn"
  	"github.com/open-constructs/cdk-terrain/examples/go/documentation/generated/hashicorp/aws/dataawsavailabilityzones"
  	aws "github.com/open-constructs/cdk-terrain/examples/go/documentation/generated/hashicorp/aws/provider"
  )

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

  	aws.NewAwsProvider(stack, jsii.String("aws"), &aws.AwsProviderConfig{
  		Region: jsii.String("use-west-2"),
  	})

  	zones := dataawsavailabilityzones.NewDataAwsAvailabilityZones(stack, jsii.String("zones"), &dataawsavailabilityzones.DataAwsAvailabilityZonesConfig{
  		State: jsii.String("available"),
  	})

  	cdktn.NewTerraformOutput(stack, jsii.String("first-zone"), &cdktn.TerraformOutputConfig{
  		Value: cdktn.Fn_Element(cdktn.Token_AsAny(zones.Names()), jsii.Number(0)),
  	})

  	return stack
  }

  ```
</CodeGroup>

## Special functions

### Property Access Helpers

To access nested properties from untyped objects or other datasources that return a dynamic datatype, use the Terraform function `lookup` or, for nested access, the function "Fn.lookupNested()" which is a function offered by CDKTN that allows to avoid nesting `Fn.lookup` calls.

```ts theme={null}
const v = new TerraformVariable(this, "complex_object", {
  type: "object({users: list(object({name: string}))})",
});
new TerraformOutput(this, "users", { value: Fn.lookup(v.value, "users") });
new TerraformOutput(this, "first_user_name", {
  value: Fn.lookupNested(v.value, ["users", 0, "name"]),
});
```

```python theme={null}
v = TerraformVariable(self, "complex-object",
    type = 'object({users: list(object({name: string}))})',
)
TerraformOutput(self, 'users',
    value=Fn.lookup(v.string_value, "users")
)
TerraformOutput(self, 'first_user_name',
    value=Fn.lookup_nested(v.string_value, ["users", 0, "name"])
)
```

```java theme={null}
TerraformVariable v = new TerraformVariable(this, "complex_object", TerraformVariableConfig.builder()
        .type("object({users: list(object({name: string}))})")
        .build());
new TerraformOutput(this, "users", TerraformOutputConfig.builder()
        .value(Fn.lookup(v.getValue(), "users"))
        .build());
new TerraformOutput(this, "first-user-name", TerraformOutputConfig.builder()
        .value(Fn.lookupNested(v.getValue(), Arrays.asList("users", "0", "name")))
        .build());
```

```csharp theme={null}
TerraformVariable v = new TerraformVariable(this, "complex_object", new TerraformVariableConfig
{
    Type = "object({users: list(object({name: string}))})",
});
new TerraformOutput(this, "users", new TerraformOutputConfig
{
    Value = Fn.Lookup(v.Value, "users")
});
new TerraformOutput(this, "first-user-name", new TerraformOutputConfig
{
    Value = Fn.LookupNested(v.Value, new[] { "users", "0", "name" })
});
```

```go theme={null}
v := cdktn.NewTerraformVariable(stack, jsii.String("complex-object"), &cdktn.TerraformVariableConfig{
	Type: jsii.String("object({users: list(object({name: string}))})"),
})
cdktn.NewTerraformOutput(stack, jsii.String("users"), &cdktn.TerraformOutputConfig{
	Value: cdktn.Fn_Lookup(v.Value(), jsii.String("users"), nil),
})
cdktn.NewTerraformOutput(stack, jsii.String("first-user-name"), &cdktn.TerraformOutputConfig{
	Value: cdktn.Fn_LookupNested(v.Value(), &[]interface{}{"users", 0, "name"}),
})
```

### Raw string helper

Another helper function offered by CDKTN is `Fn.rawString` which can be used to escape raw strings that contain characters that CDKTN or Terraform would try to interpret otherwise.

```ts theme={null}
new TerraformOutput(this, "quotes", {
  value: Fn.rawString(`"b"`),
});
new TerraformOutput(this, "template", {
  value: Fn.rawString("${TEMPLATE}"),
});
```

```python theme={null}
TerraformOutput(self, 'quotes',
    value=Fn.raw_string('"b"')
)
TerraformOutput(self, 'template',
    value=Fn.raw_string('${TEMPLATE}')
)
```

```java theme={null}
new TerraformOutput(this, "quotes", TerraformOutputConfig.builder()
        .value(Fn.rawString("\"b\""))
        .build());
new TerraformOutput(this, "template", TerraformOutputConfig.builder()
        .value(Fn.rawString("${TEMPLATE}"))
        .build());
```

```csharp theme={null}
new TerraformOutput(this, "quotes", new TerraformOutputConfig
{
    Value = Fn.RawString("\"b\"")
});
new TerraformOutput(this, "template", new TerraformOutputConfig
{
    Value = Fn.RawString("${TEMPLATE}")
});
```

```go theme={null}
cdktn.NewTerraformOutput(stack, jsii.String("quotes"), &cdktn.TerraformOutputConfig{
	Value: cdktn.Fn_RawString(jsii.String("\"b\"")),
})
cdktn.NewTerraformOutput(stack, jsii.String("template"), &cdktn.TerraformOutputConfig{
	Value: cdktn.Fn_RawString(jsii.String("${TEMPLATE}")),
})
```

## Operators

Use the `Op` object to include operators like `!`, `+`, and `-`.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Fn, TerraformOutput } from "cdktn";
  import { Op } from "cdktn";

  const zones = new DataAwsAvailabilityZones(this, "zones", {
    state: "available",
  });

  // ...

  new TerraformOutput(this, "half-of-the-zone", {
    value: Op.div(Fn.lengthOf(zones.names), 2),
  });
  ```

  ```csharp C# theme={null}
              DataAwsAvailabilityZones zones = new DataAwsAvailabilityZones(this, "zones", new DataAwsAvailabilityZonesConfig {
                  State = "available"
              });

              new TerraformOutput(this, "half-of-the-zone", new TerraformOutputConfig {
                  Value = Op.Div(Fn.LengthOf(Token.AsList(zones.Names)), 2)
              });
  ```

  ```go Go theme={null}
  import (
  	"fmt"

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

  	zones := dataawsavailabilityzones.NewDataAwsAvailabilityZones(stack, jsii.String("zones"),
  		&dataawsavailabilityzones.DataAwsAvailabilityZonesConfig{
  			State: jsii.String("available"),
  		},
  	)

  	cdktn.NewTerraformOutput(stack, jsii.String("half-of-the-zone"), &cdktn.TerraformOutputConfig{
  		Value: cdktn.Op_Div(
  			cdktn.Fn_LengthOf(cdktn.Token_AsAny(zones.Names())),
  			jsii.Number(2),
  		),
  	})
  ```
</CodeGroup>

# Using Terraform built-in functions directly within strings

It is also possible to use all built-in Terraform functions without using CDKTN's `Fn.*` functions described above. To write Terraform built-in functions the same as you would in HCL, simply wrap the desired string within the HCL `${` and `}` syntax. **Note:** CDKTN doesn't do any further processing within the escaped syntax (`${` and `}`), and thus is unable to handle nested escape syntaxes yet.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Fn, TerraformOutput } from "cdktn";
  import { Op } from "cdktn";
  import { DataAwsAvailabilityZones } from "@cdktn/provider-aws/lib/data-aws-availability-zones";

  const zones = new DataAwsAvailabilityZones(this, "zones", {
    state: "available",
  });

  // ...

  new TerraformOutput(this, "half-of-the-zone-raw", {
    value: `\${length(${zones.fqn}.names) / 2}`,
  });
  ```

  ```csharp C# theme={null}
              DataAwsAvailabilityZones zones = new DataAwsAvailabilityZones(this, "zones", new DataAwsAvailabilityZonesConfig
              {
                  State = "available"
              });

              new TerraformOutput(this, "half-of-the-zone", new TerraformOutputConfig
              {
                  Value = $"${{length({zones.Fqn}.names) / 2}}"
              });
  ```

  ```go Go theme={null}
  import (
  	"fmt"

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

  	zones := dataawsavailabilityzones.NewDataAwsAvailabilityZones(stack, jsii.String("zones"),
  		&dataawsavailabilityzones.DataAwsAvailabilityZonesConfig{
  			State: jsii.String("available"),
  		},
  	)

  	cdktn.NewTerraformOutput(stack, jsii.String("half-of-the-zone-raw"), &cdktn.TerraformOutputConfig{
  		Value: jsii.String(
  			fmt.Sprintf("${length(%s.names) / 2}",
  				*cdktn.Token_AsString(zones.Names(), &cdktn.EncodingOptions{}),
  			),
  		),
  	})
  ```
</CodeGroup>

## Function Availability Across Terraform and OpenTofu

CDK Terrain works with both Terraform and OpenTofu, but the two products do not
support the exact same set of built-in functions. The `Fn` class covers the
union of both: most functions are available everywhere, while a few exist in
only one product or were introduced in a later release.

The bindings are generated from the function metadata of every stable
Terraform release since 1.5.7 and every stable OpenTofu release since 1.6.0.
Functions that are not universally available are listed below.

| Function          | Terraform | OpenTofu  |
| ----------------- | --------- | --------- |
| `issensitive`     | >= 1.8.0  | >= 1.7.0  |
| `templatestring`  | >= 1.9.0  | >= 1.7.0  |
| `ephemeralasnull` | >= 1.10.0 | >= 1.11.0 |
| `convert`         | >= 1.15.0 | —         |
| `base64gunzip`    | —         | >= 1.7.0  |
| `cidrcontains`    | —         | >= 1.7.0  |
| `urldecode`       | —         | >= 1.7.0  |

All other functions exposed on `Fn` are available in every supported release
of both products.

### Validating Function Usage at Synth Time

With the `validateFunctionVersions` [feature flag](/release#feature-flags)
enabled, CDK Terrain checks every function you use through `Fn` against the
project's [declared `targetVersions`](/create-and-deploy/configuration-file#declare-target-runtimes)
— the Terraform/OpenTofu version ranges your project intends to support.
Projects created with `cdktn init` have the flag enabled by default; existing
projects can opt in via `cdktf.json`:

```json theme={null}
{
  "targetVersions": {
    "terraform": ">=1.5.7",
    "opentofu": ">=1.6.0"
  },
  "context": {
    "validateFunctionVersions": "true"
  }
}
```

A function must be available across the *entire* declared range of every
targeted product. If it is not, synthesis fails with an actionable error
instead of letting `terraform plan` fail later:

```
Validation failed with the following errors:
  [MyStack/resource] Terraform function "templatestring" requires terraform >=1.9.0, but the project targets terraform >=1.5.7. It is available in terraform >=1.9.0 and opentofu >=1.7.0.
```

Raise the declared floor (e.g. `"terraform": ">=1.9.0"`) — or drop the
product you don't support — to use the function.

<Note>
  The validation is purely declarative: it never executes a Terraform or
  OpenTofu binary, so synth behaves identically in CI, package builds, and
  environments without either installed. To explicitly verify the binary
  installed on a machine against the declared targets, opt in with
  `"validateInstalledBinary": true`, which applies to commands that execute
  the CLI (`diff`, `deploy`, `destroy`).
</Note>

To disable all synth-time validations, pass `skipValidation: true` to your
`App` configuration.

### Limitations

* Functions written inside raw expression strings (escape hatches such as
  overrides or hand-built `${...}` interpolations) are not tracked and
  therefore not validated.
* [Provider-defined functions](https://developer.hashicorp.com/terraform/plugin/framework/functions)
  (`provider::<name>::<function>`) are out of scope; availability depends on
  the provider, not the CLI version.
