---
category: infrastructure
category_name: Infrastructure
release_count: 20
has_more: true
canonical: https://releases.sh/categories/infrastructure
---

<Release date="August 5, 2026" published="2026-08-05T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-05-access-user-id-metadata/" org="cloudflare" source="cloudflare-developer-platform">
## AI Gateway, Access - Identity-aware controls are now available in AI Gateway

AI Gateway now integrates with Cloudflare Access, giving you two new capabilities:

- **Protect your gateway endpoint.** Put your AI Gateway behind Access so you can set policies that control who is allowed to call a specific gateway's endpoint.
- **Identity-aware controls.** When traffic reaches AI Gateway through an Access-protected custom domain, AI Gateway can use the authenticated user's Access identity in logs, analytics, routing, and spend controls.

With identity-aware controls, you can set spend limits by authenticated user, control which gateways different users can access, filter logs by user, and build policies without passing user IDs from the client application. AI Gateway adds the verified Access user ID to request metadata as `cf.user_id`.

For setup instructions, refer to [Cloudflare Access](https://developers.cloudflare.com/ai-gateway/configuration/cloudflare-access/).
</Release>

<Release date="August 5, 2026" published="2026-08-05T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-05-access-user-id-metadata/" org="cloudflare" source="cloudflare-cloudflare-one">
## AI Gateway, Access - Identity-aware controls are now available in AI Gateway

AI Gateway now integrates with Cloudflare Access, giving you two new capabilities:

- **Protect your gateway endpoint.** Put your AI Gateway behind Access so you can set policies that control who is allowed to call a specific gateway's endpoint.
- **Identity-aware controls.** When traffic reaches AI Gateway through an Access-protected custom domain, AI Gateway can use the authenticated user's Access identity in logs, analytics, routing, and spend controls.

With identity-aware controls, you can set spend limits by authenticated user, control which gateways different users can access, filter logs by user, and build policies without passing user IDs from the client application. AI Gateway adds the verified Access user ID to request metadata as `cf.user_id`.

For setup instructions, refer to [Cloudflare Access](https://developers.cloudflare.com/ai-gateway/configuration/cloudflare-access/).
</Release>

<Release version="v3.256.0" date="August 4, 2026" published="2026-08-04T19:34:54.000Z" url="https://github.com/pulumi/pulumi/releases/tag/v3.256.0" org="pulumi" source="pulumi">
## 3.256.0 (2026-08-04)

### Features

- [cli] Add `--ignore-protect` flag to `pulumi up`, `pulumi preview` and `pulumi destroy` to allow deleting protected resources without unprotecting them in the state first [#24053](https://github.com/pulumi/pulumi/pull/24053)
- [cli/env] Add an optional `--export-env-vars` flag to the `env provider {aws,azure,gcp}-login` commands to also set the standard SDK environment variables referencing the login outputs [#24055](https://github.com/pulumi/pulumi/pull/24055)
- [cli] Add a `--server` flag to `pulumi package add`, `publish`, `get-schema`, `get-mapping`, `gen-sdk`, `info` and `pulumi schema check` that skips package resolution and uses the given URL as the plugin download URL [#24107](https://github.com/pulumi/pulumi/pull/24107)

### Bug Fixes

- [backend/service] Fix `pulumi login --insecure` not being reflected in the stack's service secrets manager state, which caused TLS verification failures against self-hosted backends using self-signed certificates [#24134](https://github.com/pulumi/pulumi/pull/24134)
- [cli] Scope current stack selection to the active backend so switching backends no longer surfaces stale stack errors [#23974](https://github.com/pulumi/pulumi/pull/23974)
- [programgen/go] Parent an invoke written inside a component to that component, lower a component's outputs, and only import `fmt` when a component needs it [#24019](https://github.com/pulumi/pulumi/pull/24019)
- [programgen/python] Parent an invoke written inside a component to that component, so it resolves the component's providers [#24018](https://github.com/pulumi/pulumi/pull/24018)
- [sdk/nodejs] Defer output-form invokes that depend on a remote component whose resources are pending creation, by declaring invoke dependencies to the engine [#24042](https://github.com/pulumi/pulumi/pull/24042)
- [engine] Gate invokes on the created-ness of their declared dependencies, including the children of remote components, resolving them as unknown during previews that still have to create them [#24040](https://github.com/pulumi/pulumi/pull/24040)
- [sdk/go] Defer output-form invokes that depend on a remote component whose resources are pending creation, by declaring invoke dependencies to the engine [#24044](https://github.com/pulumi/pulumi/pull/24044)
- [pcl] Declare invoke dependencies to the engine so invokes that depend on pending resources, including remote components, resolve as unknown during previews [#24041](https://github.com/pulumi/pulumi/pull/24041)
- [sdk/python] Defer output-form invokes that depend on a remote component whose resources are pending creation, by declaring invoke dependencies to the engine [#24043](https://github.com/pulumi/pulumi/pull/24043)
- [cli/env] `env provider` no longer writes a new environment revision when the resulting definition is unchanged [#24055](https://github.com/pulumi/pulumi/pull/24055)
- [sdk/go] Output-form invokes now infer their resource dependencies from their arguments, so they are skipped during preview while a dependent resource is pending creation and their results carry those dependencies [#24054](https://github.com/pulumi/pulumi/pull/24054)
- [sdkgen/go] Fixes nested optional output conversions [#24096](https://github.com/pulumi/pulumi/pull/24096)
- [engine] Fix a plugin process leak in `NewPolicyAnalyzer` when `ConfigureStack` fails after the plugin has booted [#24106](https://github.com/pulumi/pulumi/pull/24106)
- [programgen/go] Avoid redundant applies when projecting properties from generated Go object outputs [#24112](https://github.com/pulumi/pulumi/pull/24112)
- [backend/diy] Fix 403 errors writing to third-party S3-compatible backends (e.g. IBM COS, MinIO) by defaulting request_checksum_calculation to when_required when the s3:// backend URL sets a custom endpoint [#24109](https://github.com/pulumi/pulumi/pull/24109)
- [programgen/nodejs] Avoid redundant applies when projecting properties from Node.js outputs [#24119](https://github.com/pulumi/pulumi/pull/24119)
- [programgen/python] Avoid redundant applies when projecting properties from Python outputs [#24120](https://github.com/pulumi/pulumi/pull/24120)
- [sdk] Fix apply erroring for skipped resources [#24108](https://github.com/pulumi/pulumi/pull/24108)
- [cli] Retry rate-limited (HTTP 429) API requests when they are safe to retry, honoring the server's Retry-After header [#24131](https://github.com/pulumi/pulumi/pull/24131)
- [cli] Exit non-zero from remote operations (`pulumi up --remote`, `pulumi deployment run`) when the deployment fails [#24155](https://github.com/pulumi/pulumi/pull/24155)
- [cli/new] Resolve and install packages required by the program during `pulumi new`, as `pulumi install` does [#24126](https://github.com/pulumi/pulumi/pull/24126)
- [cli] Make --remote not require a Pulumi.yaml file to be present [#24128](https://github.com/pulumi/pulumi/pull/24128)
- [cli/import] Generate explicit providers in the import file `resources` [#24135](https://github.com/pulumi/pulumi/pull/24135)
- [sdk/nodejs] Fix trustedDependencies parsing for bun [#24145](https://github.com/pulumi/pulumi/pull/24145)
- [auto/go] ImportResources no longer leaks `--stack` into the converter's arguments when converter args are passed [#24146](https://github.com/pulumi/pulumi/pull/24146)
- [auto/go] Fix `ImportResources` when `GenerateCode(false)` is set [#24147](https://github.com/pulumi/pulumi/pull/24147)
- [sdk/go] Fix hooks and transforms causing panics with mocks [#24161](https://github.com/pulumi/pulumi/pull/24161)
- [programgen/go] Fix plain invokes emitting nonexistent `...ArgsArgs` argument types [#24172](https://github.com/pulumi/pulumi/pull/24172)

### Improvements

- [cli/import] Error when running `pulumi import --from terraform` in a Pulumi HCL project [#23744](https://github.com/pulumi/pulumi/pull/23744)
- [programgen] Add `ID` type to PCL [#22702](https://github.com/pulumi/pulumi/pull/22702)
- [engine] Give the resource monitor's `Invoke` its own response message, separating it from the one a provider returns [#24100](https://github.com/pulumi/pulumi/pull/24100)
- [cli/do] Allow stateful resources to register their own provider resources based on provider inputs on the command line [#24098](https://github.com/pulumi/pulumi/pull/24098)
- [cli] Refresh the first-login welcome message to link your Pulumi Cloud console and the Pulumi changelog [#24122](https://github.com/pulumi/pulumi/pull/24122)
- [cli/do] Add support for the `--provider` argument for stateful operations [#24132](https://github.com/pulumi/pulumi/pull/24132)
- [programgen/go] Better typing for maps, using known types rather than `map[string]interface{}` [#24142](https://github.com/pulumi/pulumi/pull/24142)
- [cli/import] Serve the package-resolver service to state converters via `resolver_target` on `ConvertStateRequest`, so converters can resolve package specifications the same way the CLI does [#24174](https://github.com/pulumi/pulumi/pull/24174)

### Miscellaneous

- [cli] Retire the Pulumi AI mode of `pulumi new` (interactive choice and `--ai`/`--language` flags). The backing service has been shut down; use `pulumi neo` instead. [#24116](https://github.com/pulumi/pulumi/pull/24116)
- [sdk/dotnet] Upgrade dotnet to v3.110.0 [#24175](https://github.com/pulumi/pulumi/pull/24175)
- [yaml] Upgrade yaml to v1.38.1 [#24175](https://github.com/pulumi/pulumi/pull/24175)

</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-04-index-capacity-20-million/" org="cloudflare" source="cloudflare-developer-platform">
## Vectorize - Vectorize indexes now support up to 20 million vectors

You can now store up to 20 million vectors in a single Vectorize index, doubling the previous limit of 10 million vectors. This enables larger-scale semantic search, recommendation systems, and retrieval-augmented generation (RAG) applications without splitting data across multiple indexes.

Vectorize continues to support indexes with up to 1,536 dimensions per vector at 32-bit precision. Refer to the [Vectorize limits documentation](https://developers.cloudflare.com/vectorize/platform/limits/) for complete details.
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://www.pulumi.com/blog/terraform-to-pulumi-cloud-hands-on/" org="pulumi" source="pulumi-blog">
## A guided tour of Terraform state, hosted modules, and HCL in Pulumi

![](https://www.pulumi.com/images/generated/blog/terraform-to-pulumi-cloud-hands-on/index.png)

[Today’s big release](https://www.pulumi.com/releases/terraform-state-backend-modules-hcl/) contains a whole new set of features designed for seamless interoperability with the Terraform and OpenTofu ecosystems, and there’s a lot there — so much that it can be tough to get your head around all of it. But it generally falls into three major categories:

- **Support for Pulumi Cloud as a Terraform state backend**, including remote execution with human approvals
- **A Terraform module registry** in Pulumi Cloud that lets you publish, document, and share your modules even across language boundaries
- **First-class support for HCL** as an authoring language in the Pulumi engine

To make this release a little easier to appreciate holistically, I’ve put together a quick end-to-end walkthrough that doesn’t quite cover *everything*, but does cover the big stuff, and should give you a sense of how it all comes together. We’ll start with a simple Terraform project that you’ll deploy to AWS, and then one step at a time, bring it into Pulumi Cloud and kick the tires on each of these new features as we go. It’ll take a bit, but all you’ll need are a free Pulumi account and the ability to deploy an S3 bucket to AWS.

We’ve got a bunch to cover, so let’s jump right in.

## Start with a Terraform project

Our tour begins with a tiny Terraform project that provisions a single Amazon S3 bucket using a locally defined module that we’ll publish later. The project is [available on GitHub](https://github.com/cnunciato/simple-tf-template) as a template, and the easiest way to use it is with the GitHub CLI:

```bash
<span class="line"><span class="cl">$ gh repo create my-tf-project <span class="se">\
</span></span></span><span class="line"><span class="cl"> --template cnunciato/simple-tf-template <span class="se">\
</span></span></span><span class="line"><span class="cl"> --public <span class="se">\
</span></span></span><span class="line"><span class="cl"> --clone <span class="o">&&</span> <span class="nb">cd</span> my-tf-project
</span></span>
```

We’ll use the local Terraform backend to start. Set your AWS credentials (preferably with [environment variables](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html)), then deploy the project with Terraform or OpenTofu. (This walkthrough uses the `terraform` CLI, but you can swap in `tofu` if that’s your preference.)

```bash
<span class="line"><span class="cl">$ terraform init <span class="o">&&</span> terraform apply
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Apply complete! Resources: <span class="m">2</span> added, <span class="m">0</span> changed, <span class="m">0</span> destroyed.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Outputs:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">bucket_arn</span> <span class="o">=</span> <span class="s2">"arn:aws:s3:::my-tf-project-bucket-14d19ece"</span>
</span></span><span class="line"><span class="cl"><span class="nv">bucket_name</span> <span class="o">=</span> <span class="s2">"my-tf-project-bucket-14d19ece"</span>
</span></span>
```

Now let’s see how to move this project into Pulumi Cloud.

## Migrate the state to Pulumi Cloud

First, [create a Pulumi Cloud account](https://app.pulumi.com/signup) if you don’t already have one (it’s free for individuals) and sign in to the Pulumi console. Then, add a `backend` block to `main.tf`, swapping `<your-org>` for your own Pulumi Cloud account or organization name:

```hcl
<span class="line"><span class="cl"><span class="k">terraform</span> {<span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1"> # ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">backend</span> <span class="s2">"remote"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> hostname</span> <span class="o">=</span> <span class="s2">"tf.pulumi.com"</span>
</span></span><span class="line"><span class="cl"><span class="n"> organization</span> <span class="o">=</span> <span class="s2">"<your-org>"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">workspaces</span> {
</span></span><span class="line"><span class="cl"><span class="n"> name</span> <span class="o">=</span> <span class="s2">"my-tf-project_dev"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span>
```

The workspace `name` is an underscore-delimited string that expresses the name of the [project](https://www.pulumi.com/docs/iac/concepts/projects/) you’d like to use (here, `my-tf-project`) and the [stack](https://www.pulumi.com/docs/iac/concepts/stacks/) (`dev`). A single Pulumi project can have as many stacks as you like.

Next, sign in to Pulumi Cloud with the Terraform CLI:

```bash
<span class="line"><span class="cl">$ terraform login tf.pulumi.com
</span></span>
```

Choose `yes` when prompted, and you’ll be taken to Pulumi Cloud to create a [personal access token](https://www.pulumi.com/docs/administration/access-identity/access-tokens/#personal-access-tokens), which you can paste into the prompt to authenticate:

![Creating a personal access token in the Pulumi Cloud console](./token.png)

```
Success! Logged in to Terraform Enterprise (tf.pulumi.com)
```

With the `backend` block in place and your `terraform` CLI signed in to Pulumi Cloud, you’re ready to complete the migration:

```bash
<span class="line"><span class="cl">$ terraform init -migrate-state
</span></span>
```

Terraform should detect the `backend` change and offer to copy your existing state:

```
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "local" backend
to the newly configured "remote" backend. [...] Enter "yes" to copy and
"no" to start with an empty state.
Enter a value: yes
```

Choose `yes`, and you’re done:

```
Successfully configured the backend "remote"! Terraform will automatically
use this backend unless the backend configuration changes.
```

Note that nothing about your deployed infrastructure has changed here; all we did was migrate your local state to Pulumi Cloud, and the process is identical whether you’re moving from S3, Azure, Google Cloud, or HCP Terraform or Terraform Enterprise. See [Store Terraform state in Pulumi Cloud](https://www.pulumi.com/docs/iac/get-started/terraform/terraform-state-backend/) for details.

Now hop over to the Pulumi Cloud console, choose **Stacks**, and you’ll see your new stack in the list, along with its first update:

![The migrated Terraform stack and its first update in the Pulumi Cloud console](./new-tf-stack.png)

### Runs happen remotely by default

Another thing to note is that Terraform stacks backed by Pulumi Cloud run *remotely* by default, just as they do in HCP Terraform and Terraform Enterprise. Plans and applies are executed on a Pulumi Cloud-hosted runner, and output is streamed simultaneously to your terminal and to the browser. Try making a small change to `main.tf` — adding a tag, say — and run `terraform apply`:

```diff
<span class="line"><span class="cl"> tags = {
</span></span><span class="line"><span class="cl"> Environment = "dev"
</span></span><span class="line"><span class="cl"><span class="gi">+ Owner = "me"
</span></span></span><span class="line"><span class="cl"> }
</span></span>
```

```bash
<span class="line"><span class="cl">$ terraform apply
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Running apply in the remote backend. Output will stream here.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">To view this run in a browser, visit:
</span></span><span class="line"><span class="cl">https://tf.pulumi.com/app/cnunciato/my-tf-project_dev/runs/run-8a635542-334f-4367-b188-8b4b442b2550
</span></span><span class="line"><span class="cl">...
</span></span>
```

Confirm with another `yes`, and you’ll notice the `apply` *fails* — which makes sense, considering the runner doesn’t have your AWS credentials yet. That’s where [Pulumi ESC](https://www.pulumi.com/docs/esc/) comes in.

### Configure cloud credentials with ESC

When you create a new Terraform stack, Pulumi Cloud automatically creates a linked [ESC environment](https://www.pulumi.com/docs/esc/concepts/environments/) for you with the same name. You can use this environment to configure settings of all kinds, including cloud credentials and other encrypted secrets, and make those settings available to the stack at runtime.

In the stack’s **Overview** tab, click the linked `my-tf-project/dev` environment to open the environment editor, where you can either set your AWS credentials directly or wire up a new [connection](https://www.pulumi.com/docs/esc/guides/configuring-oidc/) by choosing **Add new** → **Login provider/configuration** and following the steps for AWS.

Since I’ve already configured an environment for sharing my AWS credentials across all of my stacks (which I’ve named `default/personal`), I can just [import that environment](https://www.pulumi.com/docs/esc/concepts/imports/) into this one to use it:

```yaml
<span class="line"><span class="cl"><span class="nt">imports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="l">default/personal</span><span class="w">
</span></span></span>
```

Try this yourself, then run the `apply` again, and see that it succeeds:

```bash
<span class="line"><span class="cl">$ terraform apply -auto-approve
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">OpenTofu will perform the following actions:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># module.s3-bucket.aws_s3_bucket.this will be updated in-place</span>
</span></span><span class="line"><span class="cl"> ~ resource <span class="s2">"aws_s3_bucket"</span> <span class="s2">"this"</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">id</span> <span class="o">=</span> <span class="s2">"my-tf-project-bucket-b64b8e37"</span>
</span></span><span class="line"><span class="cl"> ~ <span class="nv">tags</span> <span class="o">=</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"Environment"</span> <span class="o">=</span> <span class="s2">"dev"</span>
</span></span><span class="line"><span class="cl"> + <span class="s2">"Owner"</span> <span class="o">=</span> <span class="s2">"me"</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> ~ <span class="nv">tags_all</span> <span class="o">=</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> + <span class="s2">"Owner"</span> <span class="o">=</span> <span class="s2">"me"</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Plan: <span class="m">0</span> to add, <span class="m">1</span> to change, <span class="m">0</span> to destroy.
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Apply complete! Resources: <span class="m">0</span> added, <span class="m">1</span> changed, <span class="m">0</span> destroyed.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Outputs:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">bucket_arn</span> <span class="o">=</span> <span class="s2">"arn:aws:s3:::my-tf-project-bucket-b64b8e37"</span>
</span></span><span class="line"><span class="cl"><span class="nv">bucket_name</span> <span class="o">=</span> <span class="s2">"my-tf-project-bucket-b64b8e37"</span>
</span></span>
```

Remote execution is powered by [Pulumi Deployments](https://www.pulumi.com/docs/deployments/), which means your Terraform stacks can also be triggered by VCS events — pull requests and pushes to GitHub, GitLab, and others — as well as manual human approvals. Wiring that up is pretty simple as well:

1. Under **Management → Version Control** in the Pulumi console, configure your VCS provider and add the `my-tf-project` repository.
2. In your stack’s **Settings** tab, choose **Deploy**, configure the repository and the base branch to build from (e.g., `main`), and save.
3. Push a commit to that branch.

When you do that, you’ll see a new plan in the **Deployments** tab, and once that finishes, you’ll be prompted to approve or decline the run:

![A deployment run awaiting confirmation in the Pulumi Cloud console](./confirm.png)

Click **Confirm**, and you’re off and running.

And that’s it! Your Terraform stacks are now first-class citizens in Pulumi Cloud, with [access control](https://www.pulumi.com/docs/administration/access-identity/rbac/), [Neo code reviews](https://www.pulumi.com/docs/ai/neo/code-reviews/), and [Pulumi Policies](https://www.pulumi.com/docs/insights/policy/) all available to them. [Audit policies](https://www.pulumi.com/docs/iac/get-started/terraform/terraform-state-backend/#audit-policies) are runnable on any Terraform stack, and preventative policies can be used to block non-compliant changes before they happen.

Next up: modules.

## Publish a Terraform module

If your team’s been using Terraform for a while, chances are you’ve also written some Terraform modules, so you’ll need somewhere to keep them as you move to Pulumi Cloud. In addition to its role as a Terraform state backend, Pulumi Cloud now includes a [private registry](https://www.pulumi.com/docs/idp/concepts/terraform-modules/) that can host your Terraform modules and make them available across your organization.

Module publishing requires an [Enterprise or Business Critical](https://www.pulumi.com/pricing/) plan, so you’ll need an organization with one of those for this part — but you can easily [create one](https://app.pulumi.com) with a free trial if you don’t have one already. In the Pulumi console, open the organization menu, choose **Create organization**, give it a name, and you’re good to go.

Pulumi Cloud’s Terraform registry API is wire-compatible with HCP Terraform’s, which means the tools you already use for publishing — e.g., the [`go-tfe`](https://github.com/hashicorp/go-tfe) library or the [`hashicorp/tfe`](https://registry.terraform.io/providers/hashicorp/tfe/latest/docs) Terraform provider — need only be pointed at `tf.pulumi.com`. To make this easy, our project includes a small Go program that uses `go-tfe` to do just that. Set a Pulumi access token in your terminal (you may need to [obtain a new one](https://app.pulumi.com/user/settings/tokens)), then run it from the project root, passing your organization name:

```bash
<span class="line"><span class="cl">$ <span class="nb">export</span> <span class="nv">PULUMI_ACCESS_TOKEN</span><span class="o">=</span><your-access-token>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Install Go at https://go.dev/doc/install if you don't have it already.</span>
</span></span><span class="line"><span class="cl">$ go -C upload-module run . <span class="se">\
</span></span></span><span class="line"><span class="cl"> -org <your-org> <span class="se">\
</span></span></span><span class="line"><span class="cl"> -provider aws <span class="se">\
</span></span></span><span class="line"><span class="cl"> -version 0.1.0 <span class="se">\
</span></span></span><span class="line"><span class="cl"> -path ./modules/s3-bucket
</span></span>
```

You should see a confirmation:

```
creating module veridian/s3-bucket/aws
uploaded veridian/s3-bucket/aws@0.1.0
```

Then, in the Pulumi console, navigate to **Platform → Private components**, and you should see your newly uploaded module in the list. Click into it to see its details, available versions, usage instructions, and API docs:

![The published Terraform module’s detail page in the Pulumi Cloud registry](./module.png)

With the module now hosted in Pulumi Cloud, you can update your project to use the hosted version instead. Delete the local `./modules` folder entirely, and then in `main.tf`, update the `source` and add a `version`, replacing `<your-org>` with your chosen org name:

```hcl
<span class="line"><span class="cl"><span class="k">module</span> <span class="s2">"s3-bucket"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> source</span> <span class="o">=</span> <span class="s2">"tf.pulumi.com/<your-org>/s3-bucket/aws"</span>
</span></span><span class="line"><span class="cl"><span class="n"> version</span> <span class="o">=</span> <span class="s2">"0.1.0"</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1"> # ...
</span></span></span><span class="line"><span class="cl">}
</span></span>
```

Now run `terraform plan` and see that the module is downloaded and the plan, as expected, produces no changes:

```bash
<span class="line"><span class="cl">$ terraform plan
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Initializing modules...
</span></span><span class="line"><span class="cl">Downloading tf.pulumi.com/veridian/s3-bucket/aws 0.1.0 <span class="k">for</span> s3-bucket...
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">No changes. Your infrastructure matches the configuration.
</span></span>
```

Now let’s have a look at how you can use this module in other ways.

## Consume the module from a Pulumi program

The whole point of a shared module registry is to help you get more mileage out of the work you put into your Terraform modules. When you use Pulumi Cloud for that registry, you can consume those modules not just from a Terraform program, but from any Pulumi program as well — and in any supported language.

Let’s see how this looks with a TypeScript project. If you haven’t already, [install Pulumi](https://www.pulumi.com/docs/install/), then run:

```bash
<span class="line"><span class="cl">$ mkdir ../my-typescript-project <span class="o">&&</span> <span class="nb">cd</span> <span class="nv">$_</span>
</span></span><span class="line"><span class="cl">$ pulumi new typescript
</span></span>
```

Follow the prompts, accepting the defaults. Then, add the Terraform module to your TypeScript project with `pulumi package add`:

```bash
<span class="line"><span class="cl">$ pulumi package add s3-bucket-aws
</span></span>
```

This fairly magical command generates a local TypeScript SDK at `./sdks` that wraps the module as a [Pulumi component](https://www.pulumi.com/docs/iac/concepts/components/). To use it, open up `index.ts` and replace its contents with the following:

```typescript
<span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="kr">as</span> <span class="nx">bucket</span> <span class="kr">from</span> <span class="s2">"./sdks/s3-bucket"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">myModule</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">bucket</span><span class="p">.</span><span class="nx">Module</span><span class="p">(</span><span class="s2">"my-module"</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">bucketPrefix</span><span class="o">:</span> <span class="s2">"my-new-bucket"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">tags</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="p">{</span> <span class="nx">bucketName</span><span class="p">,</span> <span class="nx">bucketArn</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">myModule</span><span class="p">;</span>
</span></span>
```

Configure your AWS credentials locally as before — or alternatively, [use the ESC environment you created earlier](https://www.pulumi.com/docs/esc/concepts/environments/#using-environments-with-pulumi-iac) if you set one up — then run `pulumi up` to deploy in the usual way:

```bash
<span class="line"><span class="cl">$ pulumi up
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Updating <span class="o">(</span>dev<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> Type Name Status
</span></span><span class="line"><span class="cl"> + pulumi:pulumi:Stack my-typescript-project-dev created <span class="o">(</span>8s<span class="o">)</span>
</span></span><span class="line"><span class="cl"> + └─ s3-bucket:index:Module my-module created <span class="o">(</span>5s<span class="o">)</span>
</span></span><span class="line"><span class="cl"> + ├─ random:index:Id my-module-suffix created <span class="o">(</span>0.21s<span class="o">)</span>
</span></span><span class="line"><span class="cl"> + └─ aws:index:S3Bucket my-module-this created <span class="o">(</span>2s<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Outputs:
</span></span><span class="line"><span class="cl"> bucketArn : <span class="s2">"arn:aws:s3:::my-new-bucket-bbfa52db"</span>
</span></span><span class="line"><span class="cl"> bucketName: <span class="s2">"my-new-bucket-bbfa52db"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Resources:
</span></span><span class="line"><span class="cl"> + <span class="m">4</span> created
</span></span>
```

Having a language-specific, locally managed SDK at hand comes with many benefits, including IDE support, typed inputs and outputs, and more — but there may be times when you’ll prefer a simpler or more dynamic option. In these situations, you can instead choose to load the module at runtime.

In TypeScript, you’d do that with the `@pulumi/hcl` module. Try that now by installing it in your project:

```bash
<span class="line"><span class="cl">$ npm install @pulumi/hcl
</span></span>
```

… and using it in your program:

```typescript
<span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="kr">as</span> <span class="nx">hcl</span> <span class="kr">from</span> <span class="s2">"@pulumi/hcl"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">myModule</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">hcl</span><span class="p">.</span><span class="nx">Module</span><span class="p">(</span><span class="s2">"my-module"</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">source</span><span class="o">:</span> <span class="s2">"tf.pulumi.com/<your-org>/s3-bucket/aws"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span><span class="o">:</span> <span class="s2">"0.1.0"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">inputs</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">bucket_prefix</span><span class="o">:</span> <span class="s2">"my-new-bucket"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">tags</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="p">{</span> <span class="nx">bucket_name</span><span class="p">,</span> <span class="nx">bucket_arn</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">myModule</span><span class="p">.</span><span class="nx">outputs</span><span class="p">;</span>
</span></span>
```

Because this approach uses untyped references, you’ll trade a little type safety for flexibility — but either way, you’re able to keep using the modules you’ve already built, while migrating to *new* infrastructure in general-purpose languages on whatever schedule works best for your team.

## Write and run HCL, natively

For as much flexibility as general-purpose languages offer, some teams simply prefer to use HCL. So as of today, HCL is now a first-class language in the Pulumi engine, right alongside TypeScript, Python, Go, C#, Java, and YAML.

The easiest way to get a feel for it is to create a new project from a template:

```bash
<span class="line"><span class="cl">$ mkdir ../my-hcl-project <span class="o">&&</span> <span class="nb">cd</span> <span class="nv">$_</span>
</span></span><span class="line"><span class="cl">$ pulumi new aws-hcl
</span></span>
```

As before, step through the prompts, then open the generated Pulumi project’s `main.tf`:

```hcl
<span class="line"><span class="cl"><span class="k">terraform</span> {
</span></span><span class="line"><span class="cl"> <span class="k">required_providers</span> {
</span></span><span class="line"><span class="cl"><span class="n"> aws</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n"> source</span> <span class="o">=</span> <span class="s2">"pulumi/aws"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}<span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1"># Create an AWS resource (S3 Bucket)
</span></span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">"aws_s3_bucket" "my-bucket"</span> {}<span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1"># Export the name of the bucket
</span></span></span><span class="line"><span class="cl"><span class="k">output</span> <span class="s2">"bucket_name"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> value</span> <span class="o">=</span> <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">my</span><span class="err">-</span><span class="k">bucket</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl">}
</span></span>
```

You can deploy this out of the box (after setting your AWS credentials again, of course) to see the magic happen:

```bash
<span class="line"><span class="cl">$ pulumi up
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Updating <span class="o">(</span>dev<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> Type Name Status
</span></span><span class="line"><span class="cl"> + pulumi:pulumi:Stack my-hcl-project-dev created <span class="o">(</span>6s<span class="o">)</span>
</span></span><span class="line"><span class="cl"> + └─ aws:s3:Bucket my-bucket created <span class="o">(</span>3s<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Outputs:
</span></span><span class="line"><span class="cl"> bucket_name: <span class="s2">"my-bucket-7282e67"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Resources:
</span></span><span class="line"><span class="cl"> + <span class="m">2</span> created
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Duration: 7s
</span></span>
```

This template happens to use the `pulumi/aws` provider, but you’re free to pull in any others as well: [native Pulumi providers](https://www.pulumi.com/docs/iac/concepts/providers/), official `hashicorp/*` providers, community-supported providers, and more. In fact, you can try that now by replacing the code in `main.tf` with the same code you used in the Terraform program you left off with earlier — only without the explicit `terraform > backend` block, as it’s no longer needed:

```hcl
<span class="line"><span class="cl"><span class="k">module</span> <span class="s2">"s3-bucket"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> source</span> <span class="o">=</span> <span class="s2">"tf.pulumi.com/<your-org>/s3-bucket/aws"</span>
</span></span><span class="line"><span class="cl"><span class="n"> version</span> <span class="o">=</span> <span class="s2">"0.1.0"</span>
</span></span><span class="line"><span class="cl"><span class="n"> bucket_prefix</span> <span class="o">=</span> <span class="s2">"my-tf-project-bucket"</span>
</span></span><span class="line"><span class="cl"><span class="n"> tags</span> <span class="o">=</span> {}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">output</span> <span class="s2">"bucket_name"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> value</span> <span class="o">=</span> <span class="k">module</span><span class="p">.</span><span class="k">s3</span><span class="err">-</span><span class="k">bucket</span><span class="p">.</span><span class="k">bucket_name</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">output</span> <span class="s2">"bucket_arn"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> value</span> <span class="o">=</span> <span class="k">module</span><span class="p">.</span><span class="k">s3</span><span class="err">-</span><span class="k">bucket</span><span class="p">.</span><span class="k">bucket_arn</span>
</span></span><span class="line"><span class="cl">}
</span></span>
```

Run `pulumi up` again, and you’ll see the module get picked up and used, just as before:

```bash
<span class="line"><span class="cl">$ pulumi up
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Updating <span class="o">(</span>dev<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> Type Name Status
</span></span><span class="line"><span class="cl"> + pulumi:pulumi:Stack my-hcl-project-dev created <span class="o">(</span>10s<span class="o">)</span>
</span></span><span class="line"><span class="cl"> + └─ components:index:C619d883588c0366 s3-bucket created <span class="o">(</span>8s<span class="o">)</span>
</span></span><span class="line"><span class="cl"> + ├─ random:index:Id s3-bucket.suffix created <span class="o">(</span>0.22s<span class="o">)</span>
</span></span><span class="line"><span class="cl"> + └─ aws:index:S3Bucket s3-bucket.this created <span class="o">(</span>3s<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Outputs:
</span></span><span class="line"><span class="cl"> bucket_arn : <span class="s2">"arn:aws:s3:::my-tf-project-bucket-f61cea35"</span>
</span></span><span class="line"><span class="cl"> bucket_name: <span class="s2">"my-tf-project-bucket-f61cea35"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Resources:
</span></span><span class="line"><span class="cl"> + <span class="m">4</span> created
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Duration: 12s
</span></span>
```

And with that, our tour is complete. Be sure to clean up both projects with a `terraform destroy` and `pulumi destroy` when you’re done.

## Where to go next

Now you’ve seen it all come together: You can back your Terraform state with Pulumi Cloud, publish and share your modules, consume those modules from any Pulumi language, and write HCL that runs natively — all without having to rewrite what you’ve already built.

A few next steps to keep the learning going:

- Explore our [architecture templates](https://www.pulumi.com/templates/) to bootstrap new HCL projects easily
- Read up on [Terraform state](https://www.pulumi.com/docs/iac/get-started/terraform/terraform-state-backend/), [remote execution](https://www.pulumi.com/docs/iac/get-started/terraform/terraform-remote-execution/), and [Terraform modules](https://www.pulumi.com/docs/idp/concepts/terraform-modules/) in Pulumi Cloud
- Dive into [Pulumi and HCL](https://www.pulumi.com/docs/iac/languages-sdks/hcl/)
- Check out the [full set of features](https://www.pulumi.com/releases/terraform-state-backend-modules-hcl/) in this release

We’d love for you to kick the tires on all of this and let us know what you think. If something seems missing or doesn’t behave as you’d expect, [open an issue](https://github.com/pulumi/pulumi/issues), let us know in the [Pulumi Community Slack](https://slack.pulumi.com/), or [reach out](https://www.pulumi.com/contact/?form=sales) if you’d like to learn more.

Happy building!
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://www.pulumi.com/blog/bring-your-terraform-estate-into-the-agentic-era/" org="pulumi" source="pulumi-blog">
## Bring Your Terraform Estate Into the Agentic Era

![](https://www.pulumi.com/images/generated/blog/bring-your-terraform-estate-into-the-agentic-era/index.png)

At Pulumi, we are building the platform for agentic infrastructure. Pulumi Cloud provides the guardrails and enterprise readiness needed to safely move fast in this new era. While we are seeing extraordinary adoption — over 40% of our users now manage infrastructure using AI agents — we know many organizations are at different phases in their AI journey and have to balance building for the future with maintaining their existing infrastructure as code (IaC) solutions like Terraform. Today, we are launching three ways that Pulumi lets you avoid trading off building for the future against building for today. You don’t have to rip out Terraform to enter the agentic era. Pulumi Cloud brings agentic infrastructure to the IaC estate you already have.

## What we’re shipping today

Last December, our CEO [Joe Duffy laid out his vision](https://www.pulumi.com/blog/all-iac-including-terraform-and-hcl/) to make Pulumi the platform for all of your infrastructure as code. The capabilities we are launching today make that promise a reality. Pulumi is now fully interoperable with Terraform and OpenTofu and enables organizations to build upon their existing IaC estate rather than starting from scratch as they work toward the agentic infrastructure future.

### Pulumi Cloud as a Terraform backend

We are excited to announce the general availability of [Pulumi Cloud as the backend to your Terraform state](https://www.pulumi.com/docs/iac/get-started/terraform/terraform-state-backend/), enabling organizations to seamlessly lift and shift their existing Terraform estates.

We recognize there is lots of infrastructure that works as is, and switching over to a new management paradigm may not always be possible. Pulumi Cloud support for the Terraform state backend lets organizations maintain their existing Terraform deployment patterns while also unlocking the power of Pulumi Cloud. The following common patterns for running Terraform are now supported:

- **Plans and applies run remotely by default** for new Terraform stacks, following the behavior of HCP Terraform and Terraform Enterprise. When you run a Terraform operation, it executes on a Pulumi-hosted runner rather than your local machine. You get full visibility into these operations both on your local CLI and in the Pulumi Cloud console.
- **Production deployments may be gated with manual approvals** before applying a Terraform plan.

With this release, stacks with Terraform state are first-class entities in Pulumi Cloud. They get access to all of the capabilities that organizations need to scale in this new AI-first era.

- **[Manage access to your Terraform stacks at scale](https://www.pulumi.com/docs/administration/access-identity/rbac/)** using tag-based access control, team role assignments, and user role assignments.
- [**Take advantage of Neo code reviews**](https://www.pulumi.com/docs/ai/neo/code-reviews/). On every pull request, leverage what Pulumi Cloud knows about your running infrastructure and get clear feedback on whether it’s safe to merge changes to your Terraform and OpenTofu projects.
- [**Run preventive policies**](https://www.pulumi.com/docs/insights/policy/) after a Terraform plan to block non-compliant resources before deployment.
- **[Configure your Terraform deployments with Pulumi ESC](https://www.pulumi.com/docs/esc/)**, which is natively available to Pulumi Cloud-backed Terraform projects, to securely inject OIDC credentials at apply time and expose outputs to downstream stacks and services.

Learn more in [Using Pulumi Cloud as a Terraform state backend](https://www.pulumi.com/docs/iac/get-started/terraform/terraform-state-backend/).

### Reuse your Terraform modules

Pulumi programs, regardless of language, now support [importing Terraform modules](https://www.pulumi.com/docs/iac/get-started/terraform/terraform-modules/) natively. Organizations can leverage their existing reusable artifacts as is, without having to make a single change. This enables organizations to focus on new infrastructure projects and lets them build those projects in the language of their choice, regardless of what their existing IaC estate is.

In addition, Pulumi Cloud’s private registry can now host Terraform modules alongside Pulumi packages. This enables you to consolidate all of your IaC building blocks in a single source of truth rather than managing disparate solutions and making sure your teams know where to look. Terraform modules hosted in Pulumi Cloud provide maximum interoperability and can be used in both Pulumi and Terraform programs.

Learn more in [Using Terraform modules in Pulumi](https://www.pulumi.com/docs/iac/get-started/terraform/terraform-modules/).

### Build in HCL natively

[HashiCorp Configuration Language (HCL)](https://www.pulumi.com/docs/iac/languages-sdks/hcl/) is now generally available as a first-class language in Pulumi IaC. Like any other Pulumi language, it has full access to the entirety of the Pulumi ecosystem, including thousands of providers. Thanks to our Terraform bridge, if there’s a Terraform provider out there, it just works. Best of all, HCL in Pulumi is 100% [OpenTofu](https://github.com/opentofu/opentofu) compatible with no syntactical differences.

We recognize there are many teams out there that prefer to work in HCL over general-purpose languages but want to leverage the modern Pulumi IaC engine, or want to be able to use the reusable components their partner teams rely on — regardless of whether those teams work in Terraform or Pulumi.

Learn more in the [HCL language reference](https://www.pulumi.com/docs/iac/languages-sdks/hcl/).

## You are still covered until your HashiCorp renewal

We stand by the promise we made last December, and the last thing we want is for you to pay for two IaC solutions. To set your team up for long-term success and ensure your transition to Pulumi is as smooth as possible, we are continuing to offer three things:

- **An escape hatch for your current contract.** We know paying for two IaC solutions at once is a non-starter, so we’re letting you apply credits purchased from HashiCorp toward your Pulumi usage until your next renewal, avoiding double pay.
- **A free IaC modernization workshop.** Our professional services cloud architects host a free IaC modernization workshop to review where you’re at with your IaC already and share best practices for adopting the Pulumi platform at scale, learned from working with world-class organizations like BMW and Supabase. You will leave this session trained up and equipped to succeed with the next phase of your IaC journey.
- **A return on investment (ROI) calculation.** We will show you how the move to Pulumi will not only be spend-neutral thanks to the escape hatch, but how much value and savings you should expect to see, given our experience helping innovators like Snowflake accelerate their time to market — going from code to cloud in weeks to hours.

These ensure there’s no financial penalty for switching, a clear ROI, and no learning curve. We have always been proud to work with customers of all sizes in all industries, so these offers are available to you whether you’re a Global 2000, a startup, or somewhere in between.

## Get started today

The agentic infrastructure era is already here, and we want you to bring your IaC to it. [Get started with Pulumi free](https://app.pulumi.com/signup) and provision your first stack in minutes.

If you’d like to get started with the new Terraform/OpenTofu and HCL capabilities, or take advantage of the financial flexibility options, please [get in touch](https://www.pulumi.com/contact/?form=sales).
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://www.pulumi.com/blog/terraforms-data-model-on-pulumis-engine/" org="pulumi" source="pulumi-blog">
## Emulating Terraform on Pulumi's Engine

![](https://www.pulumi.com/images/generated/blog/terraforms-data-model-on-pulumis-engine/index.png)

The core promise of Pulumi’s HCL support is that you can bring your existing Terraform configuration and modules, and `pulumi` will run them. If it works in OpenTofu and doesn’t work in Pulumi, we would like to fix that. Given that goal, our HCL interpreter needs to take HCL as input and emit instructions to the Pulumi engine that semantically match how `tofu` would interpret the same input. This is made harder by the fact that Pulumi and OpenTofu have fundamentally different engine semantics and provider ecosystems. This blog post will explore how we have implemented that mapping well enough to get 96%1 of our top Terraform modules working on Pulumi. We’ll briefly walk through how Pulumi’s HCL interpreter handles Terraform’s resource semantics, providers, and modules. It will also call out where Pulumi’s HCL support lets you do things that Terraform and OpenTofu will not allow.

## Providers

Both Pulumi and Terraform have providers, but they don’t have the same providers. While there are providers that Terraform [does](https://www.pulumi.com/registry/packages/pulumiservice/) [not](https://www.pulumi.com/registry/packages/azure-native/) [have](https://www.pulumi.com/registry/packages/kubernetes/), Pulumi can always resolve a Terraform provider using Pulumi’s confusingly named [`terraform-provider`](https://www.pulumi.com/registry/packages/terraform-provider/) provider.2 This is the same provider that lets you consume *Any Terraform Provider* in another Pulumi program with `pulumi package add terraform-provider ...`. The `terraform-provider` provider acts as a relay: it speaks Pulumi’s protocol to the Pulumi engine, and speaks Terraform’s provider protocol to the Terraform provider it stands up. Because Pulumi HCL needs to work with all Pulumi providers and because `terraform-provider` lets Pulumi HCL speak to Terraform providers via the Pulumi protocol, Pulumi HCL actually only speaks Pulumi protocols directly:

```
flowchart LR
subgraph n2Entry[" "]
n2["terraform-provider"]
end
subgraph providerBox["Pulumi Provider"]
direction TD
n2Entry
n3["Terraform Provider"]
end
n0["Pulumi HCL"] <--> n1["Pulumi Engine"]
n1 <--> n2Entry
n2 <--> n3
n2@{ shape: rect}
n3@{ shape: rect}
n0@{ shape: rect}
n1@{ shape: rect}
style n2Entry fill:transparent,stroke:transparent
```

All internals of [github.com/pulumi/pulumi-hcl](https://github.com/pulumi/pulumi-hcl) are implemented in Pulumi’s language protocol.

Because the `terraform-provider` natively understands Terraform version ranges and defaults to the OpenTofu registry, we can directly translate normal provider requests to the `terraform-provider`. It takes its arguments as an untyped list of strings, since the interface was originally intended for the command line. Let’s walk through some simple examples:

```hcl
<span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">"aws_s3_bucket" "example"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> bucket</span> <span class="o">=</span> <span class="s2">"my-bucket-123"</span>
</span></span><span class="line"><span class="cl">}
</span></span>
```

Pulumi’s HCL interpreter sees that there is no `terraform.required_providers` block, so it cuts the resource token at the first `_` and uses the default registry and namespace. This is what the request that goes to the Pulumi engine looks like:

```go
<span class="line"><span class="cl"><span class="nx">pulumirpc</span><span class="p">.</span><span class="nx">PackageSpec</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Source</span><span class="p">:</span><span class="w"> </span><span class="s">"terraform-provider"</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Parameters</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"registry.opentofu.org/hashicorp/aws"</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span>
```

Because no version was specified, we leave it to `terraform-provider` to determine the version. It will [use the latest version](https://github.com/pulumi/pulumi-terraform-bridge/blob/v3.135.0/dynamic/internal/shim/run/loader.go#L240-L252).

Just like Terraform, you can override this with a `required_providers` block:

```hcl
<span class="line"><span class="cl"><span class="k">terraform</span> {
</span></span><span class="line"><span class="cl"> <span class="k">required_providers</span> {
</span></span><span class="line"><span class="cl"><span class="n"> example</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n"> source</span> <span class="o">=</span> <span class="s2">"my.custom.registry/me/example"</span>
</span></span><span class="line"><span class="cl"><span class="n"> version</span> <span class="o">=</span> <span class="s2">"~> 5.0"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">"example_resource" "another_example"</span> {
</span></span><span class="line"><span class="cl">}
</span></span>
```

We perform the same mechanical translation. The source is fully specified, and there is a version, so we [pass it along to the Pulumi engine](https://github.com/pulumi/pulumi-hcl/blob/3e810b1c378abc0e0134810f897a85903f4c0abf/pkg/server/server.go#L232-L239), which passes it along to `terraform-provider`:

```go
<span class="line"><span class="cl"><span class="nx">pulumirpc</span><span class="p">.</span><span class="nx">PackageSpec</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Source</span><span class="p">:</span><span class="w"> </span><span class="s">"terraform-provider"</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Parameters</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"my.custom.registry/me/example"</span><span class="p">,</span><span class="w"> </span><span class="s">"~> 5.0"</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span>
```

Pulumi HCL will automatically translate almost all provider calls through the `terraform-provider` translator. Providers with the source `pulumi/*` will instead be routed directly. This is how you can use a native Pulumi provider in Pulumi HCL:

```hcl
<span class="line"><span class="cl"><span class="k">terraform</span> {
</span></span><span class="line"><span class="cl"> <span class="k">required_providers</span> {
</span></span><span class="line"><span class="cl"><span class="n"> kubernetes</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n"> source</span> <span class="o">=</span> <span class="s2">"pulumi/kubernetes"</span>
</span></span><span class="line"><span class="cl"><span class="n"> version</span> <span class="o">=</span> <span class="s2">"4.33.0"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">"kubernetes_yaml_config_file" "app"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> file</span> <span class="o">=</span> <span class="s2">"app.yaml"</span>
</span></span><span class="line"><span class="cl">}
</span></span>
```

Our HCL interpreter routes this directly to the Pulumi Kubernetes package:

```go
<span class="line"><span class="cl"><span class="nx">pulumirpc</span><span class="p">.</span><span class="nx">PackageSpec</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Source</span><span class="p">:</span><span class="w"> </span><span class="s">"kubernetes"</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Version</span><span class="p">:</span><span class="w"> </span><span class="s">"4.33.0"</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span>
```

Observe that the version moved from `Parameters` to the `Version` field. That’s because providers written with `source = "pulumi/*"` are talking about the plugin directly.

## Resources

Both Pulumi programs and Terraform config exist to express a resource graph. It is their primary purpose. I don’t have the time or the pixels to explain everything, so I’ll restrict myself to three sub-topics here:

- Property name translation
- Provisioners and conditions
- Resource options

### Property name translation

If you’ve ever looked at the schema behind any of our providers, you will observe that names are camelCase.

```json
<span class="line"><span class="cl"> <span class="s2">"aws:s3/bucket:Bucket"</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"properties"</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"accelerationStatus"</span><span class="p">:</span> <span class="p">{</span> <span class="nt">"type"</span><span class="p">:</span> <span class="s2">"string"</span><span class="p">,</span> <span class="nt">"description"</span><span class="p">:</span> <span class="s2">"..."</span><span class="p">,</span> <span class="nt">"deprecationMessage"</span><span class="p">:</span> <span class="s2">"..."</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"bucketDomainName"</span><span class="p">:</span> <span class="p">{</span> <span class="nt">"type"</span><span class="p">:</span> <span class="s2">"string"</span><span class="p">,</span> <span class="nt">"description"</span><span class="p">:</span> <span class="s2">"..."</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"bucketNamespace"</span><span class="p">:</span> <span class="p">{</span> <span class="nt">"type"</span><span class="p">:</span> <span class="s2">"string"</span><span class="p">,</span> <span class="nt">"description"</span><span class="p">:</span> <span class="s2">"..."</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="err">...</span>
</span></span>
```

Our codegen actually relies on this fact, which means that even the schemas produced by `terraform-provider` use camelCase property names. The clever among you will see the problem: Terraform uses snake\_case by convention. When Pulumi HCL sends a resource request with property values, it needs to translate from the snake\_case text to camelCase for the engine, and then back again on values returned by the engine.

```
sequenceDiagram
participant Program as HCL program<br/>(snake_case)
participant Interp as HCL interpreter
participant Engine as Pulumi engine<br/>(camelCase)
Interp->>Engine: GetSchema("example_resource")
Engine-->>Interp: schema (property names + types)
Program->>Interp: some_value = "true"
Interp->>Engine: RegisterResource { someValue: true }
Engine-->>Interp: outputs { someValue: true, computedValue: "..." }
Interp-->>Program: example_resource.example.computed_value
```

Doing this correctly actually requires you to be aware of the resource’s schema; the raw value is not enough. Let’s work through an example to understand why:

```hcl
<span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">"example_resource" "example"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> some_value</span> <span class="o">=</span> <span class="s2">"true"</span>
</span></span><span class="line"><span class="cl"> <span class="k">block</span> {
</span></span><span class="line"><span class="cl"><span class="n"> inner_value</span> <span class="o">=</span> <span class="kt">false</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"><span class="n"> another_attribute</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n"> "trap_one"</span> <span class="o">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="n"> "trap_two"</span> <span class="o">=</span> <span class="m">2</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span>
```

Here are two different value blobs (represented as JSON) that correspond to the inputs for `"example"` above. Both are valid, depending on the type of `"example_resource"`:

```json
<span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"someValue"</span><span class="p">:</span> <span class="s2">"true"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"block"</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"innerValue"</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"anotherAttribute"</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"trap_one"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"trap_two"</span><span class="p">:</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span>
```

```json
<span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"someValue"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"block"</span><span class="p">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"innerValue"</span><span class="p">:</span> <span class="s2">"false"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}],</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"anotherAttribute"</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"trapOne"</span><span class="p">:</span> <span class="s2">"1"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"trapTwo"</span><span class="p">:</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span>
```

Let’s break down the difference between the two potential value blobs:

- HCL converts scalar values to the type the provider expects, so if `someValue` is typed as a string, we keep the `"true"`. If it’s typed as a boolean, we convert and pass the provider the converted value: `true`.
- If `block` is typed as an object, the Pulumi provider expects to see an object back on the wire. If `block` is typed as a list of objects, then the Pulumi provider needs to see a list of objects back, even if it’s a list of one.
- `innerValue` can have the same type conversion as `someValue`. Type conversion doesn’t stop at the top level.
- If `anotherAttribute` is a map type, then its keys are user-provided values and need to be kept as they are. If it’s an object type, then its keys need to be shifted back to camelCase.

Pulumi’s HCL interpreter is thus type aware. It queries the engine for the schema of each provider it translates for and does the correct conversion, tracking the target type as it walks the value for translation. This is not limited to resources; it does the same translation for data sources, modules, and provider blocks.

For Pulumi providers that are [bridged](https://github.com/pulumi/pulumi-terraform-bridge) from Terraform providers, we apply an additional step. Bridged providers expose an explicit naming table used by [`pulumi convert --from terraform`](https://www.pulumi.com/docs/iac/guides/migration/migrating-to-pulumi/from-terraform/). Pulumi HCL queries the engine for this mapping also, and uses that to make sure that resource and property names line up exactly with the underlying Terraform provider, even if the property was renamed or hidden in Pulumi.

### Provisioners and conditions

[Terraform provisioners](https://opentofu.org/docs/language/resources/provisioners/syntax/) allow the Terraform config to specify commands to run at various points in the resource lifecycle. [Conditions](https://opentofu.org/docs/language/expressions/custom-conditions/) allow the same, but for boolean expressions to be evaluated instead. For example:

```hcl
<span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">"example_resource" "example"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> input</span> <span class="o">=</span> <span class="k">var</span><span class="p">.</span><span class="k">input</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">lifecycle</span> {
</span></span><span class="line"><span class="cl"> <span class="k">precondition</span> {
</span></span><span class="line"><span class="cl"><span class="n"> condition</span> <span class="o">=</span> <span class="k">var</span><span class="p">.</span><span class="k">input</span> <span class="err">></span> <span class="m">3</span>
</span></span><span class="line"><span class="cl"><span class="n"> error_message</span> <span class="o">=</span> <span class="s2">"var.input must exceed 3"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">provisioner</span> <span class="s2">"local-exec"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> command</span> <span class="o">=</span> <span class="s2">"echo An example output is ${self.output} >> file.txt"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span>
```

Pulumi only has one mechanism to hook into the resource lifecycle: [hooks](https://www.pulumi.com/docs/iac/concepts/resources/options/hooks/). Pulumi’s resource hooks are powerful enough to express both provisioners and conditions. The easiest way I can express this is by comparison to another language. I’ll use TypeScript. The example configuration block above is equivalent to this TypeScript program:

```typescript
<span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">config</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">pulumi</span><span class="p">.</span><span class="nx">Config</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">precondition</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">pulumi</span><span class="p">.</span><span class="nx">ResourceHook</span><span class="p">(</span><span class="s2">"example_resource:precondition:example"</span><span class="p">,</span> <span class="kr">async</span> <span class="nx">args</span> <span class="o">=></span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">inputs</span> <span class="o">=</span> <span class="nx">args</span><span class="p">.</span><span class="nx">newInputs</span> <span class="kr">as</span> <span class="nx">example</span><span class="p">.</span><span class="nx">ResourceArgs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">.</span><span class="nx">input</span> <span class="o"><=</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"var.input must exceed 3"</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">provisioner</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">pulumi</span><span class="p">.</span><span class="nx">ResourceHook</span><span class="p">(</span><span class="s2">"example_resource:provisioner:example"</span><span class="p">,</span> <span class="kr">async</span> <span class="nx">args</span> <span class="o">=></span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">self</span> <span class="o">=</span> <span class="nx">args</span><span class="p">.</span><span class="nx">newOutputs</span> <span class="kr">as</span> <span class="nx">example</span><span class="p">.</span><span class="nx">ResourceState</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="p">{</span> <span class="nx">exec</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">require</span><span class="p">(</span><span class="s1">'child_process'</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exec</span><span class="p">(</span><span class="sb">`echo An example output is </span><span class="si">${</span><span class="nx">self</span><span class="p">.</span><span class="nx">output</span><span class="si">}</span><span class="sb"> >> file.txt`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">example</span><span class="p">.</span><span class="nx">Resource</span><span class="p">(</span><span class="s2">"example"</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">input</span>: <span class="kt">config.requireNumber</span><span class="p">(</span><span class="s2">"input"</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">hooks</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">beforeCreate</span><span class="o">:</span> <span class="p">[</span><span class="nx">precondition</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"> <span class="nx">beforeUpdate</span><span class="o">:</span> <span class="p">[</span><span class="nx">precondition</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"> <span class="nx">afterCreate</span><span class="o">:</span> <span class="p">[</span><span class="nx">provisioner</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span>
```

The full mapping between Terraform provisioners and conditions and Pulumi hooks can be found below:

Terraform

Pulumi

`lifecycle.precondition`

`beforeCreate`, `beforeUpdate`

`lifecycle.postcondition`

`afterCreate`, `afterUpdate`

`provisioner` (creation-time)

`afterCreate`

`provisioner` with `when = destroy`

`beforeDelete`

`lifecycle.prevent_destroy`

`beforeDelete` (always errors)

Creation-time provisioners bind only to `afterCreate`: Terraform does not re-run provisioners on update, and neither do we.

### Resource options

Our final challenge for resources is Terraform’s various resource options. Resource options in both Terraform and Pulumi exist to give special instructions to the engine concerning a specific resource. We can classify Terraform’s resource options into two kinds:

- Those that can be handled at the language level.
- Those that need engine support.

Let’s start by going through those that can be handled at the language level without engine support in Pulumi:

- `count`/`for_each`: The HCL interpreter unrolls `count` and `for_each` and sends a request for each underlying resource. These are equivalent to using a for-loop in any of Pulumi’s programming languages.
- `lifecycle.prevent_destroy`: Pulumi doesn’t have an equivalent in-language hook ([`protect`](https://www.pulumi.com/docs/iac/concepts/resources/options/protect/) is stored in state). We emulate `prevent_destroy` with a `beforeDelete` hook, exactly like our destroy-time provisioners.

For most of the resource options that require real engine support, Pulumi has equivalent options:

- `lifecycle.replace_triggered_by`: Terraform allows specifying either resources or values here. We evaluate every element and feed it to [`replacementTrigger`](https://www.pulumi.com/docs/iac/concepts/resources/options/replacementtrigger/). Elements that reference a resource also contribute that resource to [`replaceWith`](https://www.pulumi.com/docs/iac/concepts/resources/options/replacewith/), which covers the case where the referenced resource is replaced without any of its attribute values changing.
- `lifecycle.ignore_changes`: We map this directly to Pulumi’s `ignoreChanges`. We translate the paths from Terraform’s snake\_case to Pulumi’s camelCase for you.
- `lifecycle.create_before_destroy`: To replicate Terraform’s default behavior, we register all resources with [`deleteBeforeReplace`](https://www.pulumi.com/docs/iac/concepts/resources/options/deletebeforereplace/) set to true. When `lifecycle.create_before_destroy` is set, we go back to Pulumi’s default behavior here.
- `provider`: Pulumi has exactly [this concept](https://www.pulumi.com/docs/iac/concepts/resources/options/provider/). We pass it through to the engine as is.
- `depends_on`: We pass this directly to the Pulumi engine.

That’s how we map Terraform’s resource options. Of course, Pulumi has its own set of resource options, and we expose those on resources with a `pulumi` block. You can see the remaining resource options in the [Pulumi resource options docs page](https://www.pulumi.com/docs/iac/concepts/resources/options/).

## Modules

Terraform has modules, and Pulumi has [components](https://www.pulumi.com/docs/iac/concepts/components/). Naturally, we represent Terraform modules as components. Under the hood, Pulumi has two different kinds of components:

- [In-language components](https://www.pulumi.com/docs/iac/concepts/components/)
- [Multi-language components](https://www.pulumi.com/docs/iac/guides/building-extending/components/packaging-components/) (MLCs)

In-language components are components that are consumed directly within the language. They don’t need the Pulumi engine’s intervention to serve them. In Pulumi HCL, this is what you get when you write a `module` block. The same language host running the rest of your program loads that HCL and interprets it.

Pulumi HCL also supports MLCs as both a consumer and a provider. That means it can work with the engine to let you consume MLCs written in other Pulumi languages in HCL, and that you can consume HCL modules in other Pulumi languages.

From HCL’s perspective, an MLC is just like any other resource, so consuming one looks like any other resource construction. Here is what it looks like to consume our [AWSx VPC resource](https://www.pulumi.com/registry/packages/awsx/api-docs/ec2/vpc/) (written in TypeScript) in HCL:

```hcl
<span class="line"><span class="cl"><span class="k">terraform</span> {
</span></span><span class="line"><span class="cl"> <span class="k">required_providers</span> {
</span></span><span class="line"><span class="cl"><span class="n"> awsx</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n"> source</span> <span class="o">=</span> <span class="s2">"pulumi/awsx"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">"awsx_ec2_vpc" "example"</span> {
</span></span><span class="line"><span class="cl"><span class="n"> availability_zone_names</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"us-west-2a", "us-west-2b"</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">}
</span></span>
```

We can consume HCL modules in other Pulumi languages as well. Here is what it looks like to consume the unrelated [`terraform-aws-modules/vpc/aws`](https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest) module in a Pulumi language:

```typescript
<span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="kr">as</span> <span class="nx">pulumi</span> <span class="kr">from</span> <span class="s2">"@pulumi/pulumi"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="kr">as</span> <span class="nx">hcl</span> <span class="kr">from</span> <span class="s2">"@pulumi/hcl"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">vpc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">hcl</span><span class="p">.</span><span class="nx">Module</span><span class="p">(</span><span class="s2">"vpc"</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">source</span><span class="o">:</span> <span class="s2">"terraform-aws-modules/vpc/aws"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span><span class="o">:</span> <span class="s2">"5.0.0"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">inputs</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s2">"example-vpc"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cidr</span><span class="o">:</span> <span class="s2">"10.0.0.0/16"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">vpcId</span> <span class="o">=</span> <span class="nx">vpc</span><span class="p">.</span><span class="nx">outputs</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">o</span> <span class="o">=></span> <span class="nx">o</span><span class="p">[</span><span class="s2">"vpc_id"</span><span class="p">]);</span>
</span></span>
```

```python
<span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pulumi</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pulumi_hcl</span> <span class="k">as</span> <span class="nn">hcl</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">vpc</span> <span class="o">=</span> <span class="n">hcl</span><span class="o">.</span><span class="n">Module</span><span class="p">(</span><span class="s2">"vpc"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">source</span><span class="o">=</span><span class="s2">"terraform-aws-modules/vpc/aws"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">version</span><span class="o">=</span><span class="s2">"5.0.0"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">inputs</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"name"</span><span class="p">:</span> <span class="s2">"example-vpc"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"cidr"</span><span class="p">:</span> <span class="s2">"10.0.0.0/16"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">pulumi</span><span class="o">.</span><span class="n">export</span><span class="p">(</span><span class="s2">"vpc_id"</span><span class="p">,</span> <span class="n">vpc</span><span class="o">.</span><span class="n">outputs</span><span class="p">[</span><span class="s2">"vpc_id"</span><span class="p">])</span>
</span></span>
```

```go
<span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"github.com/pulumi/pulumi-hcl/sdk/go/hcl"</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"github.com/pulumi/pulumi/sdk/v3/go/pulumi"</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">pulumi</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="o">*</span><span class="nx">pulumi</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">vpc</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">hcl</span><span class="p">.</span><span class="nf">NewModule</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="s">"vpc"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="nx">hcl</span><span class="p">.</span><span class="nx">ModuleArgs</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Source</span><span class="p">:</span><span class="w"> </span><span class="s">"terraform-aws-modules/vpc/aws"</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Version</span><span class="p">:</span><span class="w"> </span><span class="nx">pulumi</span><span class="p">.</span><span class="nf">StringRef</span><span class="p">(</span><span class="s">"5.0.0"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">Inputs</span><span class="p">:</span><span class="w"> </span><span class="nx">pulumi</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"name"</span><span class="p">:</span><span class="w"> </span><span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="s">"example-vpc"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"cidr"</span><span class="p">:</span><span class="w"> </span><span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="s">"10.0.0.0/16"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Export</span><span class="p">(</span><span class="s">"vpcId"</span><span class="p">,</span><span class="w"> </span><span class="nx">vpc</span><span class="p">.</span><span class="nx">Outputs</span><span class="p">.</span><span class="nf">MapIndex</span><span class="p">(</span><span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="s">"vpc_id"</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span>
```

```csharp
<span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">Pulumi</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">Pulumi.Hcl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="k">await</span> <span class="n">Deployment</span><span class="p">.</span><span class="n">RunAsync</span><span class="p">(()</span> <span class="p">=></span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">var</span> <span class="n">vpc</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Module</span><span class="p">(</span><span class="s">"vpc"</span><span class="p">,</span> <span class="k">new</span> <span class="n">ModuleArgs</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">Source</span> <span class="p">=</span> <span class="s">"terraform-aws-modules/vpc/aws"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">Version</span> <span class="p">=</span> <span class="s">"5.0.0"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">Inputs</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="s">"name"</span><span class="p">,</span> <span class="s">"example-vpc"</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="s">"cidr"</span><span class="p">,</span> <span class="s">"10.0.0.0/16"</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">new</span> <span class="n">Dictionary</span><span class="p"><</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object?</span><span class="p">></span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na"> ["vpcId"]</span> <span class="p">=</span> <span class="n">vpc</span><span class="p">.</span><span class="n">Outputs</span><span class="p">.</span><span class="n">Apply</span><span class="p">(</span><span class="n">o</span> <span class="p">=></span> <span class="n">o</span><span class="p">[</span><span class="s">"vpc_id"</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span>
```

```java
<span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">myapp</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">com.pulumi.Pulumi</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">com.pulumi.hcl.Module</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">com.pulumi.hcl.ModuleArgs</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.Map</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">App</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Pulumi</span><span class="p">.</span><span class="na">run</span><span class="p">(</span><span class="n">ctx</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="n">vpc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Module</span><span class="p">(</span><span class="s">"vpc"</span><span class="p">,</span><span class="w"> </span><span class="n">ModuleArgs</span><span class="p">.</span><span class="na">builder</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">source</span><span class="p">(</span><span class="s">"terraform-aws-modules/vpc/aws"</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">version</span><span class="p">(</span><span class="s">"5.0.0"</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">inputs</span><span class="p">(</span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"name"</span><span class="p">,</span><span class="w"> </span><span class="s">"example-vpc"</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"cidr"</span><span class="p">,</span><span class="w"> </span><span class="s">"10.0.0.0/16"</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">build</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">ctx</span><span class="p">.</span><span class="na">export</span><span class="p">(</span><span class="s">"vpcId"</span><span class="p">,</span><span class="w"> </span><span class="n">vpc</span><span class="p">.</span><span class="na">outputs</span><span class="p">().</span><span class="na">applyValue</span><span class="p">(</span><span class="n">o</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="n">o</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="s">"vpc_id"</span><span class="p">)));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span>
```

```yaml
<span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">example</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">runtime</span><span class="p">:</span><span class="w"> </span><span class="l">yaml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">vpc</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">hcl:index:Module</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">terraform-aws-modules/vpc/aws</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.0.0"</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">inputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">example-vpc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cidr</span><span class="p">:</span><span class="w"> </span><span class="m">10.0.0.0</span><span class="l">/16</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">outputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">vpcId</span><span class="p">:</span><span class="w"> </span><span class="l">${vpc.outputs["vpc_id"]}</span><span class="w">
</span></span></span>
```

If you want strongly typed SDKs for your Terraform modules, you can generate them with [`pulumi package add`](https://www.pulumi.com/docs/iac/guides/building-extending/using-existing-tools/use-terraform-module/).

## Conclusion

This has been a brief survey of how we have mapped Terraform’s semantics onto Pulumi’s engine. Providers are bridged into Pulumi, resource options are translated or handled directly in the Pulumi HCL interpreter, and modules are components… in any language.

If you want to try it yourself, start with the [get-started guide](https://www.pulumi.com/docs/iac/get-started/terraform/). And if you find a program that works in OpenTofu but not in Pulumi, that’s a bug: [file an issue](https://github.com/pulumi/pulumi-hcl/issues) and we would love to fix it.

---

1. This is 56/58 of the top Terraform AWS Module by usage. Failures are due to ephemeral resources. ↩︎
    
2. I named it, naming is hard. I wanted you to be able to type `pulumi package add terraform-provider <your-provider>`. ↩︎
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-04-free-dashboard-button/" org="cloudflare" source="cloudflare-core-platform">
## Cloudflare Fundamentals - Create Free accounts from the dashboard

You can now create standalone Free accounts directly from the Cloudflare dashboard using the new **Create Account** button. This feature is currently available to all users.

When creating a Free account:

- You can create up to **5 Free accounts**.
- Your user account must have at least **7 days of tenure** to be eligible.
- The account is created immediately and ready to use.

To create a Free account, go to the [**Cloudflare dashboard** ↗](https://dash.cloudflare.com/) and select **Create Account** from either the account switcher in the top left (where your account name appears) or from the **Accounts** page.

#### Limitations

- This feature can only be used to create a Cloudflare Free account. To create an Enterprise Account under your existing contract, please contact Cloudflare Support.
- All users can create a Cloudflare Free account, however, Enterprises wish to restrict this action to only Super Administrators. We will deliver this improvement in a future release.

#### Next steps

After creating your Free account, you can:

- [Add a payment method](https://developers.cloudflare.com/billing/get-started/create-billing-profile/) to enable additional Cloudflare products and services.
- [Update billing information](https://developers.cloudflare.com/billing/get-started/update-billing-info/) to manage payment methods, billing address, or tax IDs.
- [Review how Cloudflare billing works](https://developers.cloudflare.com/billing/understand/how-billing-works/) to understand the billing lifecycle and charge types.
- [Assign accounts to an Enterprise Organization](https://developers.cloudflare.com/fundamentals/organizations/for-enterprise/) to centrally manage multiple accounts from a single dashboard.
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://www.pulumi.com/blog/sandboxing-coding-agents-yolo-mode/" org="pulumi" source="pulumi-blog">
## YOLO Mode Is the Right Default. Your Laptop Is the Wrong Place for It.

![](https://www.pulumi.com/images/generated/blog/sandboxing-coding-agents-yolo-mode/index.png)

[Claude Code](https://claude.com/claude-code) calls the flag `--dangerously-skip-permissions`, and the community long ago renamed it YOLO mode. It lets your coding agent run any command it wants without ever asking for permission. Every agent has some version of it, [Codex](https://openai.com/codex/) and [Cursor](https://cursor.com) included, and if you use these tools seriously, you are probably running one of them every day. I am.

YOLO mode is also what makes a coding agent worth having. An agent that stops for approval before every command is not autonomous; it’s a slow pair programmer. But you cannot let it run wild on your machine without real guardrails either. You have heard the horror stories: wiped databases, deleted home directories, vanished git history. The odds on any given day are low, and it’s tempting to conclude it will never happen to you. It only has to happen once.

My version of this problem is worse than most, because the code in my working directory is infrastructure code. My laptop holds more than source files. It holds AWS credentials, kubeconfigs that point at real clusters, and Pulumi access tokens. This post walks through what an unsandboxed agent can actually do, why prompt guardrails fail exactly when you need them, and how a sandbox lets you keep full YOLO-mode autonomy anyway. Then I will show you the kit I built for doing infrastructure work this way.

## Approving every command is not a security model

Let me get one thing out of the way first: the answer is not to turn YOLO mode off. A real working session involves hundreds of commands, and nobody reviews the hundredth `npm install` any better than the first. You are not evaluating at that point; you are pressing enter. Approval fatigue turns the permission prompt into a formality while destroying the one thing you wanted from the agent, which is that it works while you do something else.

The goal instead is to run YOLO mode somewhere it cannot hurt you. That somewhere is a sandbox: an isolated environment where the agent has full autonomy and your machine is no longer part of the blast radius. This used to be a weekend project involving VM images and network configs. It’s now a single command, and I will get to that. First, the risks, because even if you think you know the list, parts of it will probably surprise you.

## What an unsandboxed agent can reach

Start with the file system. Caveats around OS-level permissions exist, but for all practical purposes you should assume that if a file exists on your computer, an agent running directly on it can read that file, edit it, and delete it. Not only in your project. Anywhere.

The same goes for your environment. The agent can kill processes, and it will, most often when it hits a port conflict and decides the fastest way to free port 5432 is to kill whatever is squatting on it. That might be the database another project depends on. It can edit environment variables, and it shares your Docker engine, which means the containers you actually care about are one confused cleanup command away from being pruned.

Then there is the network. Nothing stops an unsandboxed agent from sending an outbound request to any URL. That is precisely what a prompt injection attack needs: the agent reads a poisoned document or issue somewhere, the hidden instructions tell it to collect an API key and POST it to an attacker’s endpoint, and the exfiltration looks like any other `curl` in a busy session. An allowlist of reachable hosts is the control that breaks this chain, and almost nobody runs one on their laptop.

And if you do infrastructure work, add the credential files: `~/.aws/credentials`, `~/.kube/config`, cloud CLI session tokens. An agent debugging a provider authentication error will go read those without asking twice. For application code the worst case is a broken machine. For infrastructure code the worst case has an incident number.

## It protests, then it does it anyway

Anyone who runs agents daily has seen versions of the following patterns, and they all share one shape.

Deep in a debugging rabbit hole, options exhausted, the agent decides the dependency tree itself must be the problem and proposes deleting `node_modules` and the lock file to reinstall everything. What is unsettling is not the proposal. It’s that the agent protests at first, correctly flagging the operation as risky, and then a single follow-up, “no, go ahead,” flips it from refusal to execution. One prompt is the entire distance between “that’s too risky” and running the command.

The second pattern is keys. Ask an agent to read a private key from `~/.ssh` and it often does it immediately, no protest at all, even though that folder has nothing to do with the working directory. In the wild this happens while debugging an SSH connection, or in my world, while debugging why `pulumi up` cannot authenticate to AWS.

The third is the database. A bug refuses to be found, the code talks to a database, so the agent concludes the schema must be wrong and rolls back a migration to rebuild it. Sometimes it takes a backup first, unprompted, which is genuinely good judgment. That is exactly the point: the intelligence is real, the precautions are real, and none of it is guaranteed. A precaution the agent takes most of the time is not a safety mechanism.

## Every model has a dumb zone

If a coding agent has ever done something truly destructive to you, odds are it happened late in a long session. For the first couple hundred thousand tokens a model operates near its peak. Push a long troubleshooting session past that, and the instructions from the start of the conversation start losing their grip, system prompt included. The careful guardrails you wrote, here is what you must never touch, here are the risks to keep in mind, fade exactly when the agent is most frustrated and most inclined to reach for last-resort options.

That is the whole argument in one sentence: prompt guardrails degrade with context length, so the protections have to live somewhere the model cannot forget them.

You do not have to take my word for it. The [Claude Code issue tracker](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20%22rm%20-rf%22) has more than one entry from users whose agent executed `rm -rf` on their home directory. Several were closed as “not planned,” which is fair: it’s not a bug in the tool. It’s what a large language model at the end of its rope sometimes decides to do. Users have also reported a production database wiped by an agent that had been given credentials it never should have held, and an entire git stash dropped by an agent that got confused reconciling branches.

And if you think the people who build infrastructure tooling for a living are immune, this landed in Pulumi’s internal Slack two weeks before this post went out:

![A Pulumi internal Slack thread: an engineer posts an agent’s apology reading “Again, I’m sorry I caused this. The rm -rf $HOME outside the sandbox was a serious error I should never have made,” and a colleague replies “Crazy that auto-mode didn’t block that one.”](rm-rf-home-slack.png)

Read the agent’s apology again. It names the fix itself: outside the sandbox. None of these people expected it. That is rather the nature of the thing.

## An isolated VM in one command

[Docker Sandboxes](https://docs.docker.com/ai/sandboxes/) is the first tool I have used that makes sandboxing an agent easier than not sandboxing it. It’s free, it runs locally, installation is a single command, and it works with Claude Code out of the box:

```bash
<span class="line"><span class="cl">sbx run claude
</span></span>
```

What you get looks like a normal Claude Code session. Under the hood it runs inside a microVM with its own filesystem, its own processes, and its own Docker engine. Run `!ls` and you see your project, mounted into the VM. Run `!ls ~/.ssh` and you get “no such file or directory.” The rest of your machine is not there.

You also lose nothing you actually need. Inside the sandbox the agent installs dependencies, starts the dev server, and builds container images against the sandbox’s own nested Docker daemon, exactly as it would on the host. The one thing you manage is the network policy: a request to a host that is not on the allowlist comes back as a 403, and not from the server. The sandbox proxy blocked it; the request never left the box. You add hosts to the allowlist deliberately, one decision at a time, and a prompt-injected exfiltration attempt dies at the proxy instead of succeeding quietly.

If the setup commands look like one more thing to learn, skip learning them. Point your agent at the Docker Sandboxes documentation and have it configure its own cage. That is how I set mine up.

And before you trust the walls with real work, test them. My favorite prompt to run in a fresh sandbox: “Before I trust this sandbox, verify the isolation. Can you see any host files, no matter how hard you try? Can you reach a service running on my host? Can you touch the host Docker socket?” The agent will spend a while honestly trying, and the report you want comes back in three lines: host files not visible, host services not reachable, Docker socket is the nested daemon.

## When even the mount is too much

The default `sbx run claude` mounts your current directory into the VM, so the agent edits your real files while everything else stays sealed. If you want complete isolation, add one flag:

```bash
<span class="line"><span class="cl">sbx run --clone claude
</span></span>
```

Clone mode copies the codebase and mounts the copy, so nothing the agent does can touch your git history, your stash, or your uncommitted work. The original repo stays visible inside the sandbox at `/run/sandbox/source`, but read-only: the agent can look, not write. For long unattended runs, that is the mode I use.

## The four layers

It helps to see the isolation as four separate layers, because each one answers a different failure story from earlier:

```
flowchart LR
subgraph vm["MicroVM: hypervisor isolation"]
agent["Agent with sudo,<br/>YOLO mode on"]
ws["Workspace:<br/>mount or clone"]
engine["Nested Docker engine"]
agent --> ws
agent --> engine
end
agent --> proxy["Sandbox proxy"]
proxy -->|"on the allowlist"| ok(("Allowed hosts"))
proxy x--x|"everything else"| blocked(("Blocked"))
```

1. **Hypervisor isolation.** The VM boundary is what protects your files and processes. Inside it, the agent runs as a user with sudo, and that is fine; the entire point is that nothing inside the VM matters to your host. When you are done, `sbx rm` deletes the VM and everything in it, so experiments do not accumulate on your disk.
2. **Network isolation.** The proxy and its allowlist decide which hosts the agent can reach. This is the layer that turns prompt injection from a data breach into a log line.
3. **Docker engine isolation.** The sandbox maintains its own daemon and its own set of containers. Your host Docker Desktop stays reserved for the things you actually run, instead of accreting every container an agent ever spun up while testing.
4. **Workspace isolation.** Direct mount when you want the agent editing real files, clone when you want it working on a disposable copy.

## A sandbox that speaks infrastructure

A stock sandbox image is tuned for application work: language runtimes, a package manager, git. Infrastructure work needs more, and I got tired of reinstalling it into every fresh sandbox. So I packaged the whole setup as [infrastructure-sandbox-kit](https://github.com/dirien/infrastructure-sandbox-kit), a Docker Sandboxes template and kit for exactly this job.

[![GitHub repository: dirien/infrastructure-sandbox-kit](https://opengraph.githubassets.com/1/dirien/infrastructure-sandbox-kit)

github.com/dirien/infrastructure-sandbox-kit

](https://github.com/dirien/infrastructure-sandbox-kit)

It comes preloaded with the [Pulumi CLI](https://www.pulumi.com/docs/iac/download-install/), [Terraform](https://www.terraform.io/), [OpenTofu](https://opentofu.org/), and the AWS, Azure, and Google Cloud CLIs, every binary installed from SHA256-checksummed releases or GPG-signed vendor repositories, because an agent workspace is the last place to be casual about supply chain. On top of the tools sits the agent-side configuration that makes an agent good at infrastructure: 33 skills, including the official [Pulumi Agent Skills](https://www.pulumi.com/docs/ai/skills/), three subagents, and two guardrail hooks. Credentials go in through the sandbox proxy’s secret injection, so the Pulumi access token never lands in a file inside the workspace for the agent, or a prompt injection, to read.

You can use it two ways: build the full template image, or apply the kit to the stock Claude image at creation time:

```bash
<span class="line"><span class="cl">sbx run --kit ghcr.io/dirien/infrastructure-kit:latest claude .
</span></span>
```

Either way you land in a sandbox with starter runbooks waiting in `~/runbooks/`, ready to let an agent do real infrastructure work at full autonomy without holding your laptop hostage.

## The sandbox is half the answer

One boundary this post has deliberately stayed inside: everything here protects your machine. Infrastructure work has a second blast radius, which is what the agent’s changes do to your cloud, and a sandbox cannot help you there, because the whole point is that legitimate changes do leave the box. That half of the problem is about previews, policy as code, approvals, and audit trails, and I made that case in [the agent sprawl post](https://www.pulumi.com/blog/agent-sprawl-iac-platform-is-the-answer/). The sandbox protects your laptop; the control plane protects production. You want both.

## Watch it happen live

If you would rather see this running than read about it, Docker and Pulumi are teaching it together: a hands-on workshop where we put [Pulumi Neo](https://www.pulumi.com/product/neo/), Pulumi’s infrastructure coding agent, inside a Docker Sandbox and turn a plain-English request into running infrastructure in a real cloud account, with [Pulumi ESC](https://www.pulumi.com/docs/esc/) issuing short-lived cloud credentials instead of static keys. Same session, two time zones:

Neo in a Docker Sandbox (Americas): September 16

A 60-minute live session with Docker and Pulumi: sandbox internals, sealed agent workspaces, and real infrastructure from a plain-English request. September 16, 9:00 AM Pacific.

[Get started](https://www.pulumi.com/events/neo-in-a-docker-sandbox/)

Neo in a Docker Sandbox (EMEA): October 14

The same live session at a Europe-friendly hour: sandbox internals, sealed agent workspaces, and real infrastructure from a plain-English request. October 14, 10:00 AM CEST.

[Get started](https://www.pulumi.com/events/neo-in-a-docker-sandbox-eu/)

And if your coding agent has already written its own horror story, come tell me in the [Pulumi Community Slack](https://slack.pulumi.com/). I collect them.
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-04-agent-tracing/" org="cloudflare" source="cloudflare-developer-platform">
## Agents, Workers - Agent traces for Think, Flue, and AI SDK instrumented by Agents SDK

Agent tracing is now available for applications built with the Agents SDK. Traces show each agent turn alongside model calls, tool runs, approvals, token usage, and Workers runtime operations.

Turn on Workers tracing in your Wrangler configuration:

```
<span class="line"><span class="nb-shiki-140thh">{</span></span>
<span class="line"><span class="nb-shiki-dzsirb">  "$schema"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"./node_modules/wrangler/config-schema.json"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-dzsirb">  "observability"</span><span class="nb-shiki-140thh">: {</span></span>
<span class="line"><span class="nb-shiki-dzsirb">    "traces"</span><span class="nb-shiki-140thh">: {</span></span>
<span class="line"><span class="nb-shiki-dzsirb">      "enabled"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-dzsirb">true</span></span>
<span class="line"><span class="nb-shiki-140thh">    }</span></span>
<span class="line"><span class="nb-shiki-140thh">  }</span></span>
<span class="line"><span class="nb-shiki-140thh">}</span></span>
```

```
<span class="line"><span class="nb-shiki-140thh">[</span><span class="nb-shiki-1t8gfj">observability</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">traces</span><span class="nb-shiki-140thh">]</span></span>
<span class="line"><span class="nb-shiki-140thh">enabled = </span><span class="nb-shiki-dzsirb">true</span></span>
```

Think and Flue applications emit agent traces automatically. For direct AI SDK calls, wrap the AI SDK namespace once. `wrapAISDK()` supports AI SDK v6 and v7. This AI SDK v7 example also supplies the agent identity:

```
<span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-dzsirb"> *</span><span class="nb-shiki-1itgoe"> as</span><span class="nb-shiki-140thh"> ai </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "ai"</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { wrapAISDK } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/observability/ai"</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">const</span><span class="nb-shiki-dzsirb"> tracedAI</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> wrapAISDK</span><span class="nb-shiki-140thh">(ai);</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-140thh"> tracedAI.</span><span class="nb-shiki-1t8gfj">generateText</span><span class="nb-shiki-140thh">({</span></span>
<span class="line"><span class="nb-shiki-140thh">	model,</span></span>
<span class="line"><span class="nb-shiki-140thh">	prompt: </span><span class="nb-shiki-mdbnqw">"Find an available appointment"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	runtimeContext: {</span></span>
<span class="line"><span class="nb-shiki-140thh">		agentId: </span><span class="nb-shiki-mdbnqw">"booking-agent-production"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">		conversationId: </span><span class="nb-shiki-mdbnqw">"conversation-123"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	},</span></span>
<span class="line"><span class="nb-shiki-140thh">	telemetry: {</span></span>
<span class="line"><span class="nb-shiki-140thh">		functionId: </span><span class="nb-shiki-mdbnqw">"booking-agent"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">		includeRuntimeContext: {</span></span>
<span class="line"><span class="nb-shiki-140thh">			agentId: </span><span class="nb-shiki-dzsirb">true</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">			conversationId: </span><span class="nb-shiki-dzsirb">true</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">		},</span></span>
<span class="line"><span class="nb-shiki-140thh">	},</span></span>
<span class="line"><span class="nb-shiki-140thh">});</span></span>
```

```
<span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-dzsirb"> *</span><span class="nb-shiki-1itgoe"> as</span><span class="nb-shiki-140thh"> ai </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "ai"</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { wrapAISDK } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/observability/ai"</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">const</span><span class="nb-shiki-dzsirb"> tracedAI</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> wrapAISDK</span><span class="nb-shiki-140thh">(ai);</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-140thh"> tracedAI.</span><span class="nb-shiki-1t8gfj">generateText</span><span class="nb-shiki-140thh">({</span></span>
<span class="line"><span class="nb-shiki-140thh">	model,</span></span>
<span class="line"><span class="nb-shiki-140thh">	prompt: </span><span class="nb-shiki-mdbnqw">"Find an available appointment"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	runtimeContext: {</span></span>
<span class="line"><span class="nb-shiki-140thh">		agentId: </span><span class="nb-shiki-mdbnqw">"booking-agent-production"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">		conversationId: </span><span class="nb-shiki-mdbnqw">"conversation-123"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	},</span></span>
<span class="line"><span class="nb-shiki-140thh">	telemetry: {</span></span>
<span class="line"><span class="nb-shiki-140thh">		functionId: </span><span class="nb-shiki-mdbnqw">"booking-agent"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">		includeRuntimeContext: {</span></span>
<span class="line"><span class="nb-shiki-140thh">			agentId: </span><span class="nb-shiki-dzsirb">true</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">			conversationId: </span><span class="nb-shiki-dzsirb">true</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">		},</span></span>
<span class="line"><span class="nb-shiki-140thh">	},</span></span>
<span class="line"><span class="nb-shiki-140thh">});</span></span>
```

Message and tool payload recording is off by default. Turn it on only when the payloads are safe to store:

```
<span class="line"><span class="nb-shiki-1itgoe">const</span><span class="nb-shiki-dzsirb"> tracedAI</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> wrapAISDK</span><span class="nb-shiki-140thh">(ai, {</span></span>
<span class="line"><span class="nb-shiki-140thh">	storeMessages: </span><span class="nb-shiki-dzsirb">true</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	storeTools: </span><span class="nb-shiki-dzsirb">true</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">});</span></span>
```

```
<span class="line"><span class="nb-shiki-1itgoe">const</span><span class="nb-shiki-dzsirb"> tracedAI</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> wrapAISDK</span><span class="nb-shiki-140thh">(ai, {</span></span>
<span class="line"><span class="nb-shiki-140thh">	storeMessages: </span><span class="nb-shiki-dzsirb">true</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	storeTools: </span><span class="nb-shiki-dzsirb">true</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">});</span></span>
```

Open the [**Agents** tab ↗](https://dash.cloudflare.com/?to=/:account/agents) in the Cloudflare dashboard to inspect sessions, replay conversations, and view trace waterfalls. For advanced setup, privacy controls, and trace structure, refer to [Agent tracing](https://developers.cloudflare.com/agents/runtime/operations/observability/tracing/).
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-04-build-and-deploy-on-push/" org="cloudflare" source="cloudflare-developer-platform">
## Artifacts, Workflows - Build and deploy Artifacts repos on every push

You can now run your CI/CD pipeline on your [Artifacts](https://developers.cloudflare.com/artifacts/) repo by defining a CI [Workflow](https://developers.cloudflare.com/workflows/) with the [CI SDK ↗](https://github.com/cloudflare/ci), automatically triggered on Artifacts push events.

This allows you to:

- Automatically build and deploy application code stored in Artifacts.
- Run linting, type checking, tests, and other checks on every push.
- Reuse dependencies when the lockfile (i.e. `pnpm-lock.yaml`) has not changed.
- Stop deployment when a check or build fails.
- Restrict API token access to the deployment step.
- Deploy the output to a [Worker](https://developers.cloudflare.com/workers/) or a [Workers for Platforms](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/) User Worker.

Define your CI steps with `@cloudflare/ci`. Each `ci.runner()` spins up an isolated sandbox, and the `cache` option reuses installed dependencies across each sandboxed step in your CI job.

Point `cache.inputs` at your lockfile (i.e. `pnpm-lock.yaml`, `bun.lock`), and the install step only runs again when that lockfile changes:

src/index.jsjs

```
<span class="line"><span class="nb-shiki-1itgoe">const</span><span class="nb-shiki-dzsirb"> deps</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-140thh"> ci.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({</span></span>
<span class="line"><span class="nb-shiki-140thh">	name: </span><span class="nb-shiki-mdbnqw">"install"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	command: </span><span class="nb-shiki-mdbnqw">"bun install --frozen-lockfile"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	cache: { inputs: [</span><span class="nb-shiki-mdbnqw">"package.json"</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-mdbnqw">"bun.lock"</span><span class="nb-shiki-140thh">] },</span></span>
<span class="line"><span class="nb-shiki-140thh">});</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-dzsirb"> Promise</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">all</span><span class="nb-shiki-140thh">([</span></span>
<span class="line"><span class="nb-shiki-140thh">	deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"lint"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun run lint"</span><span class="nb-shiki-140thh"> }),</span></span>
<span class="line"><span class="nb-shiki-140thh">	deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"test"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun run test"</span><span class="nb-shiki-140thh"> }),</span></span>
<span class="line"><span class="nb-shiki-140thh">	deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"typecheck"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun run typecheck"</span><span class="nb-shiki-140thh"> }),</span></span>
<span class="line"><span class="nb-shiki-140thh">	deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"build"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun run build"</span><span class="nb-shiki-140thh"> }),</span></span>
<span class="line"><span class="nb-shiki-140thh">]);</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-140thh"> deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"deploy"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun wrangler deploy"</span><span class="nb-shiki-140thh"> });</span></span>
```

src/index.tsts

```
<span class="line"><span class="nb-shiki-1itgoe">const</span><span class="nb-shiki-dzsirb"> deps</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-140thh"> ci.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({</span></span>
<span class="line"><span class="nb-shiki-140thh">	name: </span><span class="nb-shiki-mdbnqw">"install"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	command: </span><span class="nb-shiki-mdbnqw">"bun install --frozen-lockfile"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">	cache: { inputs: [</span><span class="nb-shiki-mdbnqw">"package.json"</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-mdbnqw">"bun.lock"</span><span class="nb-shiki-140thh">] },</span></span>
<span class="line"><span class="nb-shiki-140thh">});</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-dzsirb"> Promise</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">all</span><span class="nb-shiki-140thh">([</span></span>
<span class="line"><span class="nb-shiki-140thh">	deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"lint"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun run lint"</span><span class="nb-shiki-140thh"> }),</span></span>
<span class="line"><span class="nb-shiki-140thh">	deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"test"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun run test"</span><span class="nb-shiki-140thh"> }),</span></span>
<span class="line"><span class="nb-shiki-140thh">	deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"typecheck"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun run typecheck"</span><span class="nb-shiki-140thh"> }),</span></span>
<span class="line"><span class="nb-shiki-140thh">	deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"build"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun run build"</span><span class="nb-shiki-140thh"> }),</span></span>
<span class="line"><span class="nb-shiki-140thh">]);</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-140thh"> deps.</span><span class="nb-shiki-1t8gfj">runner</span><span class="nb-shiki-140thh">({ name: </span><span class="nb-shiki-mdbnqw">"deploy"</span><span class="nb-shiki-140thh">, command: </span><span class="nb-shiki-mdbnqw">"bun wrangler deploy"</span><span class="nb-shiki-140thh"> });</span></span>
```

To start the Workflow automatically after each push, add a `cf.artifacts.repo.pushed` trigger to your Wrangler configuration:

```
<span class="line"><span class="nb-shiki-140thh">{</span></span>
<span class="line"><span class="nb-shiki-dzsirb">	"triggers"</span><span class="nb-shiki-140thh">: {</span></span>
<span class="line"><span class="nb-shiki-dzsirb">		"events"</span><span class="nb-shiki-140thh">: [</span></span>
<span class="line"><span class="nb-shiki-140thh">			{</span></span>
<span class="line"><span class="nb-shiki-dzsirb">				"type"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"cf.artifacts.repo.pushed"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-dzsirb">				"filter"</span><span class="nb-shiki-140thh">: {</span></span>
<span class="line"><span class="nb-shiki-dzsirb">					"namespace"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"CI"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-dzsirb">					"repoName"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"my-repo"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">				},</span></span>
<span class="line"><span class="nb-shiki-dzsirb">				"target"</span><span class="nb-shiki-140thh">: {</span></span>
<span class="line"><span class="nb-shiki-dzsirb">					"scriptName"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"my-ci-worker"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-dzsirb">					"workflowName"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"ci-workflow"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-140thh">				},</span></span>
<span class="line"><span class="nb-shiki-140thh">			},</span></span>
<span class="line"><span class="nb-shiki-140thh">		],</span></span>
<span class="line"><span class="nb-shiki-140thh">	},</span></span>
<span class="line"><span class="nb-shiki-140thh">}</span></span>
```

```
<span class="line"><span class="nb-shiki-140thh">[[</span><span class="nb-shiki-1t8gfj">triggers</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">events</span><span class="nb-shiki-140thh">]]</span></span>
<span class="line"><span class="nb-shiki-140thh">type = </span><span class="nb-shiki-mdbnqw">"cf.artifacts.repo.pushed"</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-140thh">  [</span><span class="nb-shiki-1t8gfj">triggers</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">events</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">filter</span><span class="nb-shiki-140thh">]</span></span>
<span class="line"><span class="nb-shiki-140thh">  namespace = </span><span class="nb-shiki-mdbnqw">"CI"</span></span>
<span class="line"><span class="nb-shiki-140thh">  repoName = </span><span class="nb-shiki-mdbnqw">"my-repo"</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-140thh">  [</span><span class="nb-shiki-1t8gfj">triggers</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">events</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">target</span><span class="nb-shiki-140thh">]</span></span>
<span class="line"><span class="nb-shiki-140thh">  scriptName = </span><span class="nb-shiki-mdbnqw">"my-ci-worker"</span></span>
<span class="line"><span class="nb-shiki-140thh">  workflowName = </span><span class="nb-shiki-mdbnqw">"ci-workflow"</span></span>
```

To learn more, refer to [Build and deploy Artifacts repos](https://developers.cloudflare.com/artifacts/guides/build-and-deploy-on-push/).
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-04-waf-release/" org="cloudflare" source="cloudflare-application-security">
## WAF - WAF Release - 2026-08-04

This release introduces new rules and updates Microsoft SharePoint RCE alongside enhanced SSRF cloud protection rule actions.

**Key Findings**

- CVE-2026-50522: An insecure deserialization vulnerability in Microsoft SharePoint Server. This may allow an unauthenticated attacker to execute arbitrary code using crafted requests.
- CVE-2026-66066: An improper input processing vulnerability in Ruby on Rails Active Storage image variant transformations. This may allow an unauthenticated attacker to perform arbitrary file reads and achieve Remote Code Execution (RCE) using maliciously crafted payload requests.
- Generic Cloud Protections: Added improved detection logic targeting Server-Side Request Forgery (SSRF) in cloud-hosted applications.

Ruleset

Rule ID

Legacy Rule ID

Description

Previous Action

New Action

Comments

Cloudflare Managed Ruleset

...052b07cf

N/A

Microsoft SharePoint - Remote Code Execution - CVE:CVE-2026-50522

Log

Block

This is a new detection.

Cloudflare Managed Ruleset

...3a5b40d6

N/A

Rails - Arbitrary File Read & RCE - CVE:CVE-2026-66066

Block

Block

This was labeled as File Upload - RCE.

Cloudflare Managed Ruleset

...8242627b

N/A

SSRF - Local

Disabled

\-

This detection has been removed.

Cloudflare Managed Ruleset

...743a63ec

N/A

SSRF - Local - 2 - Beta

Disabled

\-

This detection has been removed.

Cloudflare Managed Ruleset

...c2e84e2d

N/A

SSRF - Cloud - Beta

Disabled

\-

This detection has been removed.

Cloudflare Managed Ruleset

...ab8af26f

N/A

SSRF - Cloud - 2 - Beta

Disabled

\-

This detection has been removed.

Cloudflare Managed Ruleset

...25ba9d7c

N/A

SSRF - Cloud

Disabled

Block

We are changing the action for this rule from Disabled to BLOCK

Cloudflare Managed Ruleset

...01a076eb

N/A

SSRF - Local - Beta

Disabled

\-

This detection has been removed.
</Release>

<Release date="August 4, 2026" published="2026-08-04T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-04-local-tracing/" org="cloudflare" source="cloudflare-developer-platform">
## Workers - AI agents can debug Workers with local tracing

`wrangler dev` and `vite dev` automatically capture structured OpenTelemetry traces and correlated console logs during local Worker invocations.

#### Debug with AI agents

When the tooling detects an AI agent session, it prints a terminal hint pointing to the [Local Explorer API](https://developers.cloudflare.com/workers/local-development/local-explorer/#api) at `/cdn-cgi/explorer/api`. The API serves an OpenAPI schema and exposes a read-only observability query endpoint for discovering telemetry, querying traces and logs, and inspecting binding state.

The agent can identify the exact failing operation, fix the code, rerun the request, and verify the result. This debug loop requires no deployment or temporary logs.

#### Inspect traces in Local Explorer

Humans can inspect the same [traces](https://developers.cloudflare.com/workers/observability/traces/) and correlated console logs in the Local Explorer browser UI. Each trace shows spans, timing, attributes, and errors.

![Local Explorer showing a failed Worker trace with spans, timing, and errors](https://developers.cloudflare.com/_astro/local-trace-failed-request.Bf0avznU_1ouk56.webp)

Automatic spans cover handler calls, outbound `fetch()` calls, and binding calls. Custom spans appear alongside these automatic spans.

For more details, refer to the [Local Explorer documentation](https://developers.cloudflare.com/workers/local-development/local-explorer/).
</Release>

<Release date="August 3, 2026" published="2026-08-03T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-03-r2-sql-billing-enabled/" org="cloudflare" source="cloudflare-developer-platform">
## R2 SQL - Billing is now enabled for R2 SQL

Billing is now enabled for [R2 SQL](https://developers.cloudflare.com/r2-sql/) on non-enterprise accounts. R2 SQL usage beyond the included free tier will appear on your next invoice.

R2 SQL charges based on a single dimension:

- **Data scanned**: $0.0025 / GB ($2.50 / TB) of compressed data read from R2 to execute your query.

All plans include 10 GB of data scanned per month. Each query is billed for a minimum of 10 MB of data scanned. R2 SQL pricing is additive to standard [R2 storage and operations](https://developers.cloudflare.com/r2/pricing/) and [R2 Data Catalog](https://developers.cloudflare.com/r2/data-catalog/platform/pricing/) charges. R2 does not charge for egress, so there is no additional data transfer cost.

For example, a user who stores 500 GB of Parquet data in R2 Data Catalog and runs queries that scan a total of 50 GB of compressed data during the month would be billed as follows:

Dimension

Usage

Included

Billable

Cost

R2 storage

500 GB-month

10 GB-month

490 GB-month

$7.35

R2 SQL (data scanned)

50 GB

10 GB

40 GB

$0.10

**Total**

**$7.45**

For full pricing details and billing examples, refer to [R2 SQL pricing](https://developers.cloudflare.com/r2-sql/platform/pricing/).
</Release>

<Release date="August 3, 2026" published="2026-08-03T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-03-r2-data-catalog-billing-enabled/" org="cloudflare" source="cloudflare-developer-platform">
## R2 - Billing is now enabled for R2 Data Catalog

Billing is now enabled for [R2 Data Catalog](https://developers.cloudflare.com/r2/data-catalog/) on non-enterprise accounts. R2 Data Catalog usage beyond the included free tier will appear on your next invoice.

R2 Data Catalog charges based on two dimensions, in addition to standard [R2 storage and operations](https://developers.cloudflare.com/r2/pricing/):

- **Catalog operations**: $9.00 / million operations for metadata requests such as creating tables, reading table metadata, and updating table properties.
- **Compaction**: $0.005 / GB processed and $2.00 / million objects processed. These charges only apply when [automatic compaction](https://developers.cloudflare.com/r2/data-catalog/table-maintenance/) is turned on for a table.

Each dimension includes a monthly free tier: 1 million catalog operations, 10 GB of compaction data processed, and 1 million compaction objects processed.

For example, a single Iceberg table with 50 GB of data, 500,000 catalog operations per month, and compaction turned on that processes 20 GB across 200,000 files would be billed as follows:

Dimension

Usage

Included

Billable

Cost

Catalog operations

500,000

1,000,000

0

$0.00

Compaction (data processed)

20 GB

10 GB

10 GB

$0.05

Compaction (objects)

200,000

1,000,000

0

$0.00

**Total (Data Catalog)**

**$0.05**

Standard R2 storage charges ($0.015 / GB-month) apply separately for the 50 GB of data stored.

For full pricing details and billing examples, refer to [R2 Data Catalog pricing](https://developers.cloudflare.com/r2/data-catalog/platform/pricing/).
</Release>

<Release date="August 3, 2026" published="2026-08-03T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-03-pipelines-billing-enabled/" org="cloudflare" source="cloudflare-developer-platform">
## Pipelines - Billing is now enabled for Pipelines

Billing is now enabled for [Cloudflare Pipelines](https://developers.cloudflare.com/pipelines/) on non-enterprise accounts. Pipelines usage beyond the included free tier will appear on your next invoice.

Pipelines charges based on two usage dimensions. Ingress into a Pipeline stream remains free regardless of volume:

- **SQL transforms**: $0.04 / GB for stateless transforms (filter, reshape, unnest, cast, compute).
- **Sinks (egress)**: $0.03 / GB for JSON output, $0.06 / GB for Parquet or Iceberg output.

Workers Paid plans include 50 GB / month for both SQL transforms and sinks. Standard [R2 storage and operations](https://developers.cloudflare.com/r2/pricing/) charges apply for data written to R2 buckets, and [R2 Data Catalog](https://developers.cloudflare.com/r2/data-catalog/platform/pricing/) charges apply when writing to Iceberg tables.

For example, a pipeline that ingests 500 GB of event data per month, uses a SQL transform to filter and reshape it, and writes 300 GB to an R2 Data Catalog Iceberg table would be billed as follows:

Dimension

Usage

Included

Billable

Cost

Streams

500 GB

Unlimited

0 GB

$0.00

SQL transforms

500 GB

50 GB

450 GB

$18.00

Sinks (Iceberg)

300 GB

50 GB

250 GB

$15.00

**Total**

**$33.00**

For full pricing details and billing examples, refer to [Pipelines pricing](https://developers.cloudflare.com/pipelines/platform/pricing/).
</Release>

<Release date="August 3, 2026" published="2026-08-03T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-03-python-javascript-rpc/" org="cloudflare" source="cloudflare-developer-platform">
## Workers - Python and JavaScript Workers can now call each other via RPC

You can now call methods between Python and JavaScript Workers using [Workers RPC](https://developers.cloudflare.com/workers/runtime-apis/rpc/). This works through [Service bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/rpc/) without extra dependencies, schema definitions, or serialization code.

Cross-language RPC calls behave like ordinary function calls. Exceptions propagate to the call site. You can pass [structured cloneable types ↗](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types) as parameters or return values, and Pyodide Foreign Function Interface (FFI) automatically converts types between languages.

#### Call a TypeScript Worker from Python

Define a method in a TypeScript Worker:

index.jsjs

```
<span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { WorkerEntrypoint } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "cloudflare:workers"</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> class</span><span class="nb-shiki-1t8gfj"> RpcService</span><span class="nb-shiki-1itgoe"> extends</span><span class="nb-shiki-1t8gfj"> WorkerEntrypoint</span><span class="nb-shiki-140thh"> {</span></span>
<span class="line"><span class="nb-shiki-1itgoe">	async</span><span class="nb-shiki-1t8gfj"> add</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">a</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">b</span><span class="nb-shiki-140thh">) {</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		return</span><span class="nb-shiki-140thh"> a </span><span class="nb-shiki-1itgoe">+</span><span class="nb-shiki-140thh"> b;</span></span>
<span class="line"><span class="nb-shiki-140thh">	}</span></span>
<span class="line"><span class="nb-shiki-140thh">}</span></span>
```

index.tsts

```
<span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { WorkerEntrypoint } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "cloudflare:workers"</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> class</span><span class="nb-shiki-1t8gfj"> RpcService</span><span class="nb-shiki-1itgoe"> extends</span><span class="nb-shiki-1t8gfj"> WorkerEntrypoint</span><span class="nb-shiki-140thh"> {</span></span>
<span class="line"><span class="nb-shiki-1itgoe">	async</span><span class="nb-shiki-1t8gfj"> add</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">a</span><span class="nb-shiki-1itgoe">:</span><span class="nb-shiki-dzsirb"> number</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">b</span><span class="nb-shiki-1itgoe">:</span><span class="nb-shiki-dzsirb"> number</span><span class="nb-shiki-140thh">)</span><span class="nb-shiki-1itgoe">:</span><span class="nb-shiki-1t8gfj"> Promise</span><span class="nb-shiki-140thh"><</span><span class="nb-shiki-dzsirb">number</span><span class="nb-shiki-140thh">> {</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		return</span><span class="nb-shiki-140thh"> a </span><span class="nb-shiki-1itgoe">+</span><span class="nb-shiki-140thh"> b;</span></span>
<span class="line"><span class="nb-shiki-140thh">	}</span></span>
<span class="line"><span class="nb-shiki-140thh">}</span></span>
```

Call it from a Python Worker through a Service binding:

```
<span class="line"><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-140thh"> workers </span><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> Response, WorkerEntrypoint</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">class</span><span class="nb-shiki-1t8gfj"> Default</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1t8gfj">WorkerEntrypoint</span><span class="nb-shiki-140thh">):</span></span>
<span class="line"><span class="nb-shiki-1itgoe">	async</span><span class="nb-shiki-1itgoe"> def</span><span class="nb-shiki-1t8gfj"> fetch</span><span class="nb-shiki-140thh">(self, request):</span></span>
<span class="line"><span class="nb-shiki-140thh">		rpc </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-dzsirb"> self</span><span class="nb-shiki-140thh">.env.</span><span class="nb-shiki-dzsirb">RPC</span></span>
<span class="line"><span class="nb-shiki-140thh">		result </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-140thh"> rpc.add(</span><span class="nb-shiki-dzsirb">42</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-dzsirb">144</span><span class="nb-shiki-140thh">)</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		return</span><span class="nb-shiki-140thh"> Response.json({</span><span class="nb-shiki-mdbnqw">"result"</span><span class="nb-shiki-140thh">: result})</span></span>
```

Configure the Service binding in the Python Worker's Wrangler configuration:

```
<span class="line"><span class="nb-shiki-140thh">{</span></span>
<span class="line"><span class="nb-shiki-dzsirb">	"services"</span><span class="nb-shiki-140thh">: [</span></span>
<span class="line"><span class="nb-shiki-140thh">		{</span></span>
<span class="line"><span class="nb-shiki-dzsirb">			"binding"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"RPC"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-dzsirb">			"service"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"ts-rpc-server"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-dzsirb">			"entrypoint"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"RpcService"</span></span>
<span class="line"><span class="nb-shiki-140thh">		}</span></span>
<span class="line"><span class="nb-shiki-140thh">	]</span></span>
<span class="line"><span class="nb-shiki-140thh">}</span></span>
```

```
<span class="line"><span class="nb-shiki-140thh">[[</span><span class="nb-shiki-1t8gfj">services</span><span class="nb-shiki-140thh">]]</span></span>
<span class="line"><span class="nb-shiki-140thh">binding = </span><span class="nb-shiki-mdbnqw">"RPC"</span></span>
<span class="line"><span class="nb-shiki-140thh">service = </span><span class="nb-shiki-mdbnqw">"ts-rpc-server"</span></span>
<span class="line"><span class="nb-shiki-140thh">entrypoint = </span><span class="nb-shiki-mdbnqw">"RpcService"</span></span>
```

#### Call a Python Worker from JavaScript

Define a method in a Python Worker:

```
<span class="line"><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-140thh"> workers </span><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> WorkerEntrypoint</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">class</span><span class="nb-shiki-1t8gfj"> Default</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1t8gfj">WorkerEntrypoint</span><span class="nb-shiki-140thh">):</span></span>
<span class="line"><span class="nb-shiki-1itgoe">	async</span><span class="nb-shiki-1itgoe"> def</span><span class="nb-shiki-1t8gfj"> highlight_code</span><span class="nb-shiki-140thh">(self, code: </span><span class="nb-shiki-dzsirb">str</span><span class="nb-shiki-140thh">, language: </span><span class="nb-shiki-dzsirb">str</span><span class="nb-shiki-140thh">) -> </span><span class="nb-shiki-dzsirb">dict</span><span class="nb-shiki-140thh">:</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		from</span><span class="nb-shiki-140thh"> pygments.formatters </span><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> HtmlFormatter</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		from</span><span class="nb-shiki-140thh"> pygments </span><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> highlight</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		from</span><span class="nb-shiki-140thh"> pygments.lexers </span><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> get_lexer_by_name</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-140thh">		lexer </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-140thh"> get_lexer_by_name(language, </span><span class="nb-shiki-1jdh33">stripall</span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-dzsirb">True</span><span class="nb-shiki-140thh">)</span></span>
<span class="line"><span class="nb-shiki-140thh">		formatter </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-140thh"> HtmlFormatter(</span><span class="nb-shiki-1jdh33">linenos</span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-dzsirb">True</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">cssclass</span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-mdbnqw">"highlight"</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">style</span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-mdbnqw">"monokai"</span><span class="nb-shiki-140thh">)</span></span>
<span class="line"><span class="nb-shiki-140thh">		highlighted_html </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-140thh"> highlight(code, lexer, formatter)</span></span>
<span class="line"><span class="nb-shiki-140thh">		css </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-140thh"> formatter.get_style_defs(</span><span class="nb-shiki-mdbnqw">".highlight"</span><span class="nb-shiki-140thh">)</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">		return</span><span class="nb-shiki-140thh"> {</span></span>
<span class="line"><span class="nb-shiki-mdbnqw">			"html"</span><span class="nb-shiki-140thh">: highlighted_html,</span></span>
<span class="line"><span class="nb-shiki-mdbnqw">			"css"</span><span class="nb-shiki-140thh">: css</span></span>
<span class="line"><span class="nb-shiki-140thh">		}</span></span>
```

Call it from a JavaScript Worker through a Service binding:

index.jsjs

```
<span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> default</span><span class="nb-shiki-140thh"> {</span></span>
<span class="line"><span class="nb-shiki-1itgoe">	async</span><span class="nb-shiki-1t8gfj"> fetch</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">request</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">env</span><span class="nb-shiki-140thh">) {</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		const</span><span class="nb-shiki-dzsirb"> rpc</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-140thh"> env.</span><span class="nb-shiki-dzsirb">PYTHON_RPC</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		const</span><span class="nb-shiki-dzsirb"> result</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-140thh"> rpc.</span><span class="nb-shiki-1t8gfj">highlight_code</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-mdbnqw">"print(42)"</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-mdbnqw">"python"</span><span class="nb-shiki-140thh">);</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		return</span><span class="nb-shiki-140thh"> Response.</span><span class="nb-shiki-1t8gfj">json</span><span class="nb-shiki-140thh">(result);</span></span>
<span class="line"><span class="nb-shiki-140thh">	},</span></span>
<span class="line"><span class="nb-shiki-140thh">};</span></span>
```

index.tsts

```
<span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> default</span><span class="nb-shiki-140thh"> {</span></span>
<span class="line"><span class="nb-shiki-1itgoe">	async</span><span class="nb-shiki-1t8gfj"> fetch</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">request</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">env</span><span class="nb-shiki-140thh">) {</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		const</span><span class="nb-shiki-dzsirb"> rpc</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-140thh"> env.</span><span class="nb-shiki-dzsirb">PYTHON_RPC</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		const</span><span class="nb-shiki-dzsirb"> result</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-140thh"> rpc.</span><span class="nb-shiki-1t8gfj">highlight_code</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-mdbnqw">"print(42)"</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-mdbnqw">"python"</span><span class="nb-shiki-140thh">);</span></span>
<span class="line"><span class="nb-shiki-1itgoe">		return</span><span class="nb-shiki-140thh"> Response.</span><span class="nb-shiki-1t8gfj">json</span><span class="nb-shiki-140thh">(result);</span></span>
<span class="line"><span class="nb-shiki-140thh">	},</span></span>
<span class="line"><span class="nb-shiki-140thh">};</span></span>
```

Configure the Service binding in the JavaScript Worker's Wrangler configuration:

```
<span class="line"><span class="nb-shiki-140thh">{</span></span>
<span class="line"><span class="nb-shiki-dzsirb">	"services"</span><span class="nb-shiki-140thh">: [</span></span>
<span class="line"><span class="nb-shiki-140thh">		{</span></span>
<span class="line"><span class="nb-shiki-dzsirb">			"binding"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"PYTHON_RPC"</span><span class="nb-shiki-140thh">,</span></span>
<span class="line"><span class="nb-shiki-dzsirb">			"service"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"py-rpc-server"</span></span>
<span class="line"><span class="nb-shiki-140thh">		}</span></span>
<span class="line"><span class="nb-shiki-140thh">	]</span></span>
<span class="line"><span class="nb-shiki-140thh">}</span></span>
```

```
<span class="line"><span class="nb-shiki-140thh">[[</span><span class="nb-shiki-1t8gfj">services</span><span class="nb-shiki-140thh">]]</span></span>
<span class="line"><span class="nb-shiki-140thh">binding = </span><span class="nb-shiki-mdbnqw">"PYTHON_RPC"</span></span>
<span class="line"><span class="nb-shiki-140thh">service = </span><span class="nb-shiki-mdbnqw">"py-rpc-server"</span></span>
```

For more details on the announcement, read the [blog post ↗](https://blog.cloudflare.com/python-workers-rpc/).

For more information, refer to the [Workers RPC documentation](https://developers.cloudflare.com/workers/runtime-apis/rpc/) and the [Python Workers overview](https://developers.cloudflare.com/workers/languages/python/).
</Release>

<Release date="August 3, 2026" published="2026-08-03T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-03-cloudflare-computer/" org="cloudflare" source="cloudflare-developer-platform">
## Agents, Workers - Preview: @cloudflare/computer agent runtime

We're releasing an early preview of [`@cloudflare/computer` ↗](https://github.com/cloudflare/computer), an open-source agent runtime that gives every agent its own computer. The runtime dynamically orchestrates between fast, efficient isolates and full Linux containers, so the agent always runs on the right compute primitive for the task at hand.

`@cloudflare/computer` provides a virtual filesystem backed by SQLite, which you can populate from cloud storage, source control, or any files you choose. Agents can read, write, and edit files, run shell commands, and interact with Git repositories. All operations are gated, audited, and observed.

Install the package via npm:

```
<span class="line"><span class="nb-shiki-1t8gfj">npm</span><span class="nb-shiki-mdbnqw"> install</span><span class="nb-shiki-mdbnqw"> @cloudflare/computer</span></span>
```

Instantiate a `Workspace` inside any Durable Object to give your agent a filesystem and execution runtime:

```
<span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { Workspace } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "@cloudflare/computer"</span><span class="nb-shiki-140thh">;</span></span>
<span class="line"></span>
<span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> class</span><span class="nb-shiki-1t8gfj"> Agent</span><span class="nb-shiki-140thh"> {</span></span>
<span class="line"><span class="nb-shiki-1jdh33">	workspace</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Workspace</span><span class="nb-shiki-140thh">({</span></span>
<span class="line"><span class="nb-shiki-140thh">		storage: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.ctx.storage,</span></span>
<span class="line"><span class="nb-shiki-140thh">	});</span></span>
<span class="line"><span class="nb-shiki-140thh">}</span></span>
```

Several execution backends are included or you can write your own:

- **Isolate runtime** — fast, horizontally scalable execution via `just-bash` and Dynamic Workers, ideal for file manipulation and data processing.
- **Container runtime** — full Linux environment via Cloudflare Containers, mounted through FUSE, for tasks that need native binaries, package managers, or a complete userland.

The AI SDK-compatible toolkit provides common agent tools (`read`, `write`, `edit`, `ls`, `exec`) and guides the model to choose the appropriate backend for each task.

For more examples, including a step-by-step tutorial, visit the [`@cloudflare/computer` repository ↗](https://github.com/cloudflare/computer).

Read the announcement blog post for more details: [Your agent needs a computer, not a container ↗](https://blog.cloudflare.com/cloudflare-computer/).
</Release>

<Release date="August 3, 2026" published="2026-08-03T00:00:00.000Z" url="https://developers.cloudflare.com/changelog/post/2026-08-03-eager-redirect-cookie-setting/" org="cloudflare" source="cloudflare-cloudflare-one">
## Access - Control authorization cookies for multi-domain Access applications

Cloudflare Access administrators can now control whether a self-hosted application preemptively sets authorization cookies across its public hostnames.

Previously, Access automatically used eager redirects for applications with five or fewer hostnames. Applications with more than five hostnames received cookies as users visited each hostname. Administrators can now choose either behavior, regardless of the number of hostnames.

The new **Eager redirect cookie** setting is turned on by default for new applications. After a user signs in, Access redirects the browser through each hostname and sets a `CF_Authorization` cookie. This supports applications that need to make requests across hostnames before the user visits each one.

For applications with many hostnames, the redirect chain can cause sign-in loops in some browsers. Turn off the setting to issue the cookie only when a user visits each hostname.

To configure the setting, refer to [Authorization cookie](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/#eager-redirect-cookie).
</Release>

<Release version="2026.7.1210" date="July 31, 2026" published="2026-07-31T13:55:39.000Z" url="https://developers.cloudflare.com/changelog/post/2026-07-31-warp-windows-beta/" org="cloudflare" source="cloudflare-cloudflare-one">
## Cloudflare One Client - Cloudflare One Client for Windows (version 2026.7.1210.1)

A new Beta release for the Windows Cloudflare One Client is now available on the [beta releases downloads page](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/beta-releases/).

This beta release includes the following changes and improvements:

- Improved connection reliability: the client now swaps protocol order after repeated connectivity-check failures, which helps when HTTP/3 is blocked after the QUIC handshake.
- Fixed issue where a certificate error could be incorrectly displayed right after the connection is established.
- A [DNS search domain](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#dns-search-suffixes) parsing failure no longer prevents connection.
- Fixed a [MASQUE](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol) issue where the tunnel could stall while uploading at a high rate.
- Fixed being unable to [switch organizations](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/switch-organizations/) when the client was stuck in the "Device not in organization" state.
- Fixed the Home Screen dropdown popup not anchoring correctly.
- Fixed a crash during dialog dismissal.
- Increased tolerance for configurations with a large number of [local domain fallback](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) resolver IPs, so DNS resolution behaves correctly even when more fallback resolvers are configured than recommended.
- Fixed a networking issue where IPv6 multicast routes were being assigned to the WARP tunnel interface.
- Fixed fatal errors on UI load on Windows 10.
- Fixed a crash during Windows notification initialization.
- Made the Windows [domain-joined posture check](https://developers.cloudflare.com/cloudflare-one/reusable-components/posture-checks/client-checks/domain-joined/) more reliable.
- Fixed orphaned credentials left behind on multi-user uninstall.
- A successful re-authentication will cause the [device profile](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) to be re-evaluated.
- Improved [dashboard-managed client updates](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/client-version-assignments/) by running the updater only when needed.
</Release>

<Pagination cursor="2026-07-31T13:55:39.000Z|2026-07-31T17:24:59.704Z|rel_riPlU5w-vLoL04RMx5VVX" next="https://releases.sh/categories/infrastructure/releases?cursor=2026-07-31T13%3A55%3A39.000Z%7C2026-07-31T17%3A24%3A59.704Z%7Crel_riPlU5w-vLoL04RMx5VVX&limit=20" />
