Paste Terraform HCL and get a Pulumi program in TypeScript: @pulumi/aws resources, Pulumi config for variables and exported outputs, ready to save as index.ts.
This looks like it contains a secret.
Your code is sent to an AI service for conversion. Replace real keys and tokens with placeholders before converting.
How to convert Terraform to Pulumi TypeScript
To convert Terraform to Pulumi TypeScript, paste your HCL above and press Convert to Pulumi. Each resource block becomes a new aws.* class, arguments become camelCase properties, variables become pulumi.Config values and outputs become exports. Save it as index.ts, set your config, import existing resources and run pulumi preview.
Add your TerraformPaste HCL into the Terraform (HCL) editor, or open a .tf, .hcl or .txt file up to 500 KB. Convert one file or one module at a time. Sample loads an SNS topic, an email subscription and a CloudWatch alarm.
Let the secret check runVariable defaults and tfvars values sometimes hold real credentials. The page checks for AWS keys, private keys and common tokens before anything is sent.
Convert to PulumiClick Convert to Pulumi or press Ctrl + Enter. The TypeScript streams into the right-hand editor, and you can stop it at any time. One conversion takes up to 60,000 characters.
Drop it into a projectDownload the result as index.ts into a Pulumi TypeScript project (for example one created with pulumi new aws-typescript). Set each config key the code reads, such as pulumi config set alert_email you@example.com, and check any TODO: comments.
Import, then previewImport resources that already exist so Pulumi manages them instead of creating copies, then run pulumi preview. It should show no creates or replacements for resources you imported.
Set config and import existing resources before the first pulumi up.
Why move from Terraform to Pulumi TypeScript
Pulumi lets you define the same AWS infrastructure in a general-purpose language. With TypeScript you get real loops and functions instead of count and for_each workarounds, type checking in your editor, npm packages for shared components, and unit tests in the test framework you already use.
The resources don't change much. Pulumi's AWS provider is built from the Terraform AWS provider, so resource types and arguments line up closely: aws_sns_topic is aws.sns.Topic, alarm_actions is alarmActions. That's what makes a line-by-line conversion like this one practical, and why most of the review effort goes into the parts that differ: config, outputs and resource options.
Use this page for single files, modules and snippets, or to see how a Terraform pattern looks in Pulumi before you commit to a migration. For a whole repository, Pulumi's own pulumi convert --from terraform converts a project in one command. Coming from CloudFormation instead? Convert the template with the CloudFormation YAML to Terraform converter first, then bring the HCL here.
The day-to-day workflow maps closely, so the switch is mostly about the code.
How Terraform maps to a Pulumi program
Most resources map one to one. resource "aws_sns_topic" "alerts" becomes new aws.sns.Topic("alerts", { ... }), where the Terraform name becomes the Pulumi logical name. Arguments become camelCase properties, variable blocks become pulumi.Config reads, locals become ordinary constants, and output blocks become export const statements.
The main new concept is Output<T>. Values such as topic.arn aren't known until deployment, so you pass them straight into other resources, and build strings from them with pulumi.interpolate or .apply(). The converter uses these where Terraform used interpolation.
Terraform's lifecycle settings and meta-arguments become resource options, passed as a third argument: depends_on becomes dependsOn, prevent_destroy becomes protect, ignore_changes becomes ignoreChanges, and a provider alias becomes an explicit aws.Provider.
Converting the code doesn't adopt your deployed resources. Import them into the Pulumi stack first, or pulumi up will try to create duplicates.
Resource type, name, arguments and references all have a direct counterpart.
Terraform vs Pulumi TypeScript: quick reference
The Terraform constructs you'll convert most often and their Pulumi equivalents.
Terraform HCL constructs and their Pulumi TypeScript equivalents
Concept
Terraform
Pulumi TypeScript
Resource
resource "aws_sns_topic" "alerts"
new aws.sns.Topic("alerts", { … })
Argument names
alarm_actions
alarmActions
Input variable
variable "alert_email"
config.require("alert_email") or config.get()
Sensitive variable
sensitive = true
config.requireSecret() and pulumi config set --secret
Locals
locals { … }
Plain const values
Output
output "topic_arn"
export const topicArn = topic.arn
Data source
data "aws_caller_identity" "current"
aws.getCallerIdentityOutput()
String with references
"arn-${aws_sns_topic.alerts.arn}"
pulumi.interpolate`arn-${alerts.arn}`
count / for_each
for_each = toset(var.names)
An if statement or a for…of loop
depends_on
depends_on = [aws_iam_role.x]
{ dependsOn: [role] }
prevent_destroy
lifecycle { prevent_destroy = true }
{ protect: true }
ignore_changes
lifecycle { ignore_changes = [tags] }
{ ignoreChanges: ["tags"] }
Provider alias
provider = aws.use1
{ provider: new aws.Provider("use1", { region }) }
Module
module "vpc" { … }
A ComponentResource class or a function
State
Backend (S3, Terraform Cloud)
Stack state (Pulumi Cloud or a self-managed backend such as S3)
Key features
Resources become @pulumi/aws classesEach resource block becomes a new aws.* class with camelCase properties, and explicit names such as name = "ops-alerts" are kept.
Variables and outputs carried overvariable blocks become pulumi.Config reads and output blocks become export const statements.
References stay typedAttributes such as .arn pass straight between resources as Output values, with pulumi.interpolate where Terraform built strings.
Loops and conditions in TypeScriptcount and for_each become ordinary if statements and loops.
Lifecycle rules become resource optionsdepends_on, prevent_destroy, ignore_changes and provider aliases map to dependsOn, protect, ignoreChanges and provider.
Gaps flagged, not guessedProvisioners, backend settings and anything else without a Pulumi equivalent get a TODO: comment.
Save as index.tsCopy the program or download it as index.ts. The HCL is checked for keys and tokens in your browser before it's sent.
Examples: Terraform converted to Pulumi TypeScript
Three Terraform patterns and the Pulumi TypeScript they should become. Choose Try this example to load the HCL into the converter; your output may be formatted differently.
for_each becomes a loop
A list variable becomes a config value with the same default, and for_each becomes a plain for…of loop. Each queue needs a unique logical name.
import * as aws from "@pulumi/aws";
const use1 = new aws.Provider("use1", { region: "us-east-1" });
const site = new aws.acm.Certificate("site", {
domainName: "example.com",
validationMethod: "DNS",
}, { provider: use1 });
Tips for better results
Set names explicitly if they matter. Pulumi adds a random suffix to physical names by default. Keep a fixed name, such as name: "ops-alerts", when other systems depend on it, and always when you import an existing resource.
Use the config keys the code reads. The converted program may keep Terraform-style names like alert_email. Set exactly those keys with pulumi config set, or rename them in the code first.
Set the region once. A default provider "aws" region becomes stack config: pulumi config set aws:region us-east-1.
Preview before deploying. Run pulumi preview and check it only creates or imports what you expect.
Convert one module at a time. A Terraform module maps naturally to a Pulumi ComponentResource.
Common mistakes to avoid
Running pulumi up before importing. Pulumi doesn't know about resources Terraform created, so it creates new ones, or fails on names that already exist.
Letting both tools manage a resource. After importing into Pulumi, remove the resource from Terraform with terraform state rm so a later terraform apply doesn't fight Pulumi.
Building strings with plain template literals.`arn-${topic.arn}` gives you a warning message instead of the ARN. Use pulumi.interpolate or .apply().
Dropping a name to "let Pulumi handle it". Without an explicit name, Pulumi generates one with a random suffix, so an imported resource no longer matches and gets replaced.
Storing secrets as plain config. Use pulumi config set --secret and config.requireSecret() for anything that was a sensitive variable.
Reusing a logical name in a loop. Every Pulumi resource needs a unique logical name, so include the loop key, as in `work-${name}`.
Frequently asked questions
How do I convert Terraform to Pulumi TypeScript?
Paste your HCL into the converter at the top of this page and press Convert to Pulumi. Resource blocks become @pulumi/aws classes, variables become pulumi.Config values and outputs become exports. Save the result as index.ts in a Pulumi TypeScript project, set your config, import existing resources and run pulumi preview.
Does Pulumi have an official Terraform converter?
Yes. Pulumi's CLI includes pulumi convert --from terraform, which converts whole Terraform projects. This tool is a quick alternative for individual files, modules and snippets.
Will my Terraform-managed resources move to Pulumi?
Not by converting code. Import them into your Pulumi stack with pulumi import or the import resource option, as described in Pulumi's Terraform migration guide, then remove them from Terraform state so only one tool manages each resource. The guide to migrate a Terraform stack to Pulumi TypeScript covers imports and the state handoff.
What is Output<T> and why does the code use it?
Resource attributes such as ARNs are only known after deployment, so Pulumi wraps them in Output values. You can pass them directly to other resources, and use pulumi.interpolate or .apply() when you need to build a string from them.
What happens to count and for_each?
They become TypeScript: an if statement for a conditional resource, and a for…of loop or map() for repeated ones. Each resource created in a loop needs its own logical name.
How are variables and secrets handled?
Variables become reads from pulumi.Config, such as config.require("alert_email"), and you set values per stack with pulumi config set. Sensitive values should use pulumi config set --secret and config.requireSecret().
Why do my resource names have random suffixes?
Pulumi auto-names resources by adding a random suffix, which lets it replace resources without name clashes. Set an explicit name property when you need an exact physical name.
Is it free?
Yes. Each conversion takes up to 60,000 characters. Without an account you can run 6 conversions a minute and 60 a day; a free account raises that to 12 a minute and 200 a day. Your code is sent to an AI model hosted on NVIDIA's API only to convert it, and ChatWithCloud doesn't store it. For the full picture, see whether it's safe to paste AWS code into an AI converter. The guide to ChatWithCloud free converter limits shows how far they stretch for a real migration.