<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Modules</title><link>https://www.pulumi.com/blog/tag/modules/</link><description>Pulumi blog posts: Modules.</description><language>en-us</language><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><item><title>A guided tour of Terraform state, hosted modules, and HCL in Pulumi</title><link>https://www.pulumi.com/blog/terraform-to-pulumi-cloud-hands-on/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/terraform-to-pulumi-cloud-hands-on/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/terraform-to-pulumi-cloud-hands-on/index.png" /&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/releases/terraform-state-backend-modules-hcl/"&gt;Today&amp;rsquo;s big release&lt;/a&gt; contains a whole new set of features designed for seamless interoperability with the Terraform and OpenTofu ecosystems, and there&amp;rsquo;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:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Support for Pulumi Cloud as a Terraform state backend&lt;/strong&gt;, including remote execution with human approvals&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Terraform module registry&lt;/strong&gt; in Pulumi Cloud that lets you publish, document, and share your modules even across language boundaries&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First-class support for HCL&lt;/strong&gt; as an authoring language in the Pulumi engine&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To make this release a little easier to appreciate holistically, I&amp;rsquo;ve put together a quick end-to-end walkthrough that doesn&amp;rsquo;t quite cover &lt;em&gt;everything&lt;/em&gt;, but does cover the big stuff, and should give you a sense of how it all comes together. We&amp;rsquo;ll start with a simple Terraform project that you&amp;rsquo;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&amp;rsquo;ll take a bit, but all you&amp;rsquo;ll need are a free Pulumi account and the ability to deploy an S3 bucket to AWS.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve got a bunch to cover, so let&amp;rsquo;s jump right in.&lt;/p&gt;
&lt;h2 id="start-with-a-terraform-project"&gt;Start with a Terraform project&lt;/h2&gt;
&lt;p&gt;Our tour begins with a tiny Terraform project that provisions a single Amazon S3 bucket using a locally defined module that we&amp;rsquo;ll publish later. The project is &lt;a href="https://github.com/cnunciato/simple-tf-template"&gt;available on GitHub&lt;/a&gt; as a template, and the easiest way to use it is with the GitHub CLI:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ gh repo create my-tf-project &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --template cnunciato/simple-tf-template &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --public &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --clone &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; my-tf-project
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll use the local Terraform backend to start. Set your AWS credentials (preferably with &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html"&gt;environment variables&lt;/a&gt;), then deploy the project with Terraform or OpenTofu. (This walkthrough uses the &lt;code&gt;terraform&lt;/code&gt; CLI, but you can swap in &lt;code&gt;tofu&lt;/code&gt; if that&amp;rsquo;s your preference.)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ terraform init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; terraform apply
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Apply complete! Resources: &lt;span class="m"&gt;2&lt;/span&gt; added, &lt;span class="m"&gt;0&lt;/span&gt; changed, &lt;span class="m"&gt;0&lt;/span&gt; destroyed.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Outputs:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;bucket_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;arn:aws:s3:::my-tf-project-bucket-14d19ece&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-tf-project-bucket-14d19ece&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s see how to move this project into Pulumi Cloud.&lt;/p&gt;
&lt;h2 id="migrate-the-state-to-pulumi-cloud"&gt;Migrate the state to Pulumi Cloud&lt;/h2&gt;
&lt;p&gt;First, &lt;a href="https://app.pulumi.com/signup"&gt;create a Pulumi Cloud account&lt;/a&gt; if you don&amp;rsquo;t already have one (it&amp;rsquo;s free for individuals) and sign in to the Pulumi console. Then, add a &lt;code&gt;backend&lt;/code&gt; block to &lt;code&gt;main.tf&lt;/code&gt;, swapping &lt;code&gt;&amp;lt;your-org&amp;gt;&lt;/code&gt; for your own Pulumi Cloud account or organization name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; {&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;remote&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tf.pulumi.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; organization&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;your-org&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;workspaces&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-tf-project_dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The workspace &lt;code&gt;name&lt;/code&gt; is an underscore-delimited string that expresses the name of the &lt;a href="https://www.pulumi.com/docs/iac/concepts/projects/"&gt;project&lt;/a&gt; you&amp;rsquo;d like to use (here, &lt;code&gt;my-tf-project&lt;/code&gt;) and the &lt;a href="https://www.pulumi.com/docs/iac/concepts/stacks/"&gt;stack&lt;/a&gt; (&lt;code&gt;dev&lt;/code&gt;). A single Pulumi project can have as many stacks as you like.&lt;/p&gt;
&lt;p&gt;Next, sign in to Pulumi Cloud with the Terraform CLI:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ terraform login tf.pulumi.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Choose &lt;code&gt;yes&lt;/code&gt; when prompted, and you&amp;rsquo;ll be taken to Pulumi Cloud to create a &lt;a href="https://www.pulumi.com/docs/administration/access-identity/access-tokens/#personal-access-tokens"&gt;personal access token&lt;/a&gt;, which you can paste into the prompt to authenticate:&lt;/p&gt;
&lt;p&gt;&lt;img src="./token.png" alt="Creating a personal access token in the Pulumi Cloud console"&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Success! Logged in to Terraform Enterprise (tf.pulumi.com)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With the &lt;code&gt;backend&lt;/code&gt; block in place and your &lt;code&gt;terraform&lt;/code&gt; CLI signed in to Pulumi Cloud, you&amp;rsquo;re ready to complete the migration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ terraform init -migrate-state
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Terraform should detect the &lt;code&gt;backend&lt;/code&gt; change and offer to copy your existing state:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous &amp;#34;local&amp;#34; backend
to the newly configured &amp;#34;remote&amp;#34; backend. [...] Enter &amp;#34;yes&amp;#34; to copy and
&amp;#34;no&amp;#34; to start with an empty state.
Enter a value: yes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Choose &lt;code&gt;yes&lt;/code&gt;, and you&amp;rsquo;re done:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Successfully configured the backend &amp;#34;remote&amp;#34;! Terraform will automatically
use this backend unless the backend configuration changes.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;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&amp;rsquo;re moving from S3, Azure, Google Cloud, or HCP Terraform or Terraform Enterprise. See &lt;a href="https://www.pulumi.com/docs/iac/get-started/terraform/terraform-state-backend/"&gt;Store Terraform state in Pulumi Cloud&lt;/a&gt; for details.&lt;/p&gt;
&lt;p&gt;Now hop over to the Pulumi Cloud console, choose &lt;strong&gt;Stacks&lt;/strong&gt;, and you&amp;rsquo;ll see your new stack in the list, along with its first update:&lt;/p&gt;
&lt;p&gt;&lt;img src="./new-tf-stack.png" alt="The migrated Terraform stack and its first update in the Pulumi Cloud console"&gt;&lt;/p&gt;
&lt;h3 id="runs-happen-remotely-by-default"&gt;Runs happen remotely by default&lt;/h3&gt;
&lt;p&gt;Another thing to note is that Terraform stacks backed by Pulumi Cloud run &lt;em&gt;remotely&lt;/em&gt; 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 &lt;code&gt;main.tf&lt;/code&gt; — adding a tag, say — and run &lt;code&gt;terraform apply&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tags = {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Environment = &amp;#34;dev&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ Owner = &amp;#34;me&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ terraform apply
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Running apply in the remote backend. Output will stream here.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;To view this run in a browser, visit:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://tf.pulumi.com/app/cnunciato/my-tf-project_dev/runs/run-8a635542-334f-4367-b188-8b4b442b2550
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Confirm with another &lt;code&gt;yes&lt;/code&gt;, and you&amp;rsquo;ll notice the &lt;code&gt;apply&lt;/code&gt; &lt;em&gt;fails&lt;/em&gt; — which makes sense, considering the runner doesn&amp;rsquo;t have your AWS credentials yet. That&amp;rsquo;s where &lt;a href="https://www.pulumi.com/docs/esc/"&gt;Pulumi ESC&lt;/a&gt; comes in.&lt;/p&gt;
&lt;h3 id="configure-cloud-credentials-with-esc"&gt;Configure cloud credentials with ESC&lt;/h3&gt;
&lt;p&gt;When you create a new Terraform stack, Pulumi Cloud automatically creates a linked &lt;a href="https://www.pulumi.com/docs/esc/concepts/environments/"&gt;ESC environment&lt;/a&gt; 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.&lt;/p&gt;
&lt;p&gt;In the stack&amp;rsquo;s &lt;strong&gt;Overview&lt;/strong&gt; tab, click the linked &lt;code&gt;my-tf-project/dev&lt;/code&gt; environment to open the environment editor, where you can either set your AWS credentials directly or wire up a new &lt;a href="https://www.pulumi.com/docs/esc/guides/configuring-oidc/"&gt;connection&lt;/a&gt; by choosing &lt;strong&gt;Add new&lt;/strong&gt; → &lt;strong&gt;Login provider/configuration&lt;/strong&gt; and following the steps for AWS.&lt;/p&gt;
&lt;p&gt;Since I&amp;rsquo;ve already configured an environment for sharing my AWS credentials across all of my stacks (which I&amp;rsquo;ve named &lt;code&gt;default/personal&lt;/code&gt;), I can just &lt;a href="https://www.pulumi.com/docs/esc/concepts/imports/"&gt;import that environment&lt;/a&gt; into this one to use it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;default/personal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Try this yourself, then run the &lt;code&gt;apply&lt;/code&gt; again, and see that it succeeds:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ terraform apply -auto-approve
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;OpenTofu will perform the following actions:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# module.s3-bucket.aws_s3_bucket.this will be updated in-place&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ~ resource &lt;span class="s2"&gt;&amp;#34;aws_s3_bucket&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;this&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-tf-project-bucket-b64b8e37&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ~ &lt;span class="nv"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Environment&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + &lt;span class="s2"&gt;&amp;#34;Owner&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;me&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ~ &lt;span class="nv"&gt;tags_all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + &lt;span class="s2"&gt;&amp;#34;Owner&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;me&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Plan: &lt;span class="m"&gt;0&lt;/span&gt; to add, &lt;span class="m"&gt;1&lt;/span&gt; to change, &lt;span class="m"&gt;0&lt;/span&gt; to destroy.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Apply complete! Resources: &lt;span class="m"&gt;0&lt;/span&gt; added, &lt;span class="m"&gt;1&lt;/span&gt; changed, &lt;span class="m"&gt;0&lt;/span&gt; destroyed.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Outputs:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;bucket_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;arn:aws:s3:::my-tf-project-bucket-b64b8e37&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-tf-project-bucket-b64b8e37&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Remote execution is powered by &lt;a href="https://www.pulumi.com/docs/deployments/"&gt;Pulumi Deployments&lt;/a&gt;, 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:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Under &lt;strong&gt;Management → Version Control&lt;/strong&gt; in the Pulumi console, configure your VCS provider and add the &lt;code&gt;my-tf-project&lt;/code&gt; repository.&lt;/li&gt;
&lt;li&gt;In your stack&amp;rsquo;s &lt;strong&gt;Settings&lt;/strong&gt; tab, choose &lt;strong&gt;Deploy&lt;/strong&gt;, configure the repository and the base branch to build from (e.g., &lt;code&gt;main&lt;/code&gt;), and save.&lt;/li&gt;
&lt;li&gt;Push a commit to that branch.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When you do that, you&amp;rsquo;ll see a new plan in the &lt;strong&gt;Deployments&lt;/strong&gt; tab, and once that finishes, you&amp;rsquo;ll be prompted to approve or decline the run:&lt;/p&gt;
&lt;p&gt;&lt;img src="./confirm.png" alt="A deployment run awaiting confirmation in the Pulumi Cloud console"&gt;&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Confirm&lt;/strong&gt;, and you&amp;rsquo;re off and running.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it! Your Terraform stacks are now first-class citizens in Pulumi Cloud, with &lt;a href="https://www.pulumi.com/docs/administration/access-identity/rbac/"&gt;access control&lt;/a&gt;, &lt;a href="https://www.pulumi.com/docs/ai/neo/code-reviews/"&gt;Neo code reviews&lt;/a&gt;, and &lt;a href="https://www.pulumi.com/docs/insights/policy/"&gt;Pulumi Policies&lt;/a&gt; all available to them. &lt;a href="https://www.pulumi.com/docs/iac/get-started/terraform/terraform-state-backend/#audit-policies"&gt;Audit policies&lt;/a&gt; are runnable on any Terraform stack, and preventative policies can be used to block non-compliant changes before they happen.&lt;/p&gt;
&lt;p&gt;Next up: modules.&lt;/p&gt;
&lt;h2 id="publish-a-terraform-module"&gt;Publish a Terraform module&lt;/h2&gt;
&lt;p&gt;If your team&amp;rsquo;s been using Terraform for a while, chances are you&amp;rsquo;ve also written some Terraform modules, so you&amp;rsquo;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 &lt;a href="https://www.pulumi.com/docs/idp/concepts/terraform-modules/"&gt;private registry&lt;/a&gt; that can host your Terraform modules and make them available across your organization.&lt;/p&gt;
&lt;p&gt;Module publishing requires an &lt;a href="https://www.pulumi.com/pricing/"&gt;Enterprise or Business Critical&lt;/a&gt; plan, so you&amp;rsquo;ll need an organization with one of those for this part — but you can easily &lt;a href="https://app.pulumi.com"&gt;create one&lt;/a&gt; with a free trial if you don&amp;rsquo;t have one already. In the Pulumi console, open the organization menu, choose &lt;strong&gt;Create organization&lt;/strong&gt;, give it a name, and you&amp;rsquo;re good to go.&lt;/p&gt;
&lt;p&gt;Pulumi Cloud&amp;rsquo;s Terraform registry API is wire-compatible with HCP Terraform&amp;rsquo;s, which means the tools you already use for publishing — e.g., the &lt;a href="https://github.com/hashicorp/go-tfe"&gt;&lt;code&gt;go-tfe&lt;/code&gt;&lt;/a&gt; library or the &lt;a href="https://registry.terraform.io/providers/hashicorp/tfe/latest/docs"&gt;&lt;code&gt;hashicorp/tfe&lt;/code&gt;&lt;/a&gt; Terraform provider — need only be pointed at &lt;code&gt;tf.pulumi.com&lt;/code&gt;. To make this easy, our project includes a small Go program that uses &lt;code&gt;go-tfe&lt;/code&gt; to do just that. Set a Pulumi access token in your terminal (you may need to &lt;a href="https://app.pulumi.com/user/settings/tokens"&gt;obtain a new one&lt;/a&gt;), then run it from the project root, passing your organization name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PULUMI_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-access-token&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install Go at https://go.dev/doc/install if you don&amp;#39;t have it already.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ go -C upload-module run . &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -org &amp;lt;your-org&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -provider aws &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -version 0.1.0 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -path ./modules/s3-bucket
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should see a confirmation:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;creating module veridian/s3-bucket/aws
uploaded veridian/s3-bucket/aws@0.1.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, in the Pulumi console, navigate to &lt;strong&gt;Platform → Private components&lt;/strong&gt;, 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:&lt;/p&gt;
&lt;p&gt;&lt;img src="./module.png" alt="The published Terraform module&amp;rsquo;s detail page in the Pulumi Cloud registry"&gt;&lt;/p&gt;
&lt;p&gt;With the module now hosted in Pulumi Cloud, you can update your project to use the hosted version instead. Delete the local &lt;code&gt;./modules&lt;/code&gt; folder entirely, and then in &lt;code&gt;main.tf&lt;/code&gt;, update the &lt;code&gt;source&lt;/code&gt; and add a &lt;code&gt;version&lt;/code&gt;, replacing &lt;code&gt;&amp;lt;your-org&amp;gt;&lt;/code&gt; with your chosen org name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s3-bucket&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tf.pulumi.com/&amp;lt;your-org&amp;gt;/s3-bucket/aws&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.1.0&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now run &lt;code&gt;terraform plan&lt;/code&gt; and see that the module is downloaded and the plan, as expected, produces no changes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ terraform plan
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Initializing modules...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Downloading tf.pulumi.com/veridian/s3-bucket/aws 0.1.0 &lt;span class="k"&gt;for&lt;/span&gt; s3-bucket...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;No changes. Your infrastructure matches the configuration.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s have a look at how you can use this module in other ways.&lt;/p&gt;
&lt;h2 id="consume-the-module-from-a-pulumi-program"&gt;Consume the module from a Pulumi program&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see how this looks with a TypeScript project. If you haven&amp;rsquo;t already, &lt;a href="https://www.pulumi.com/docs/install/"&gt;install Pulumi&lt;/a&gt;, then run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ mkdir ../my-typescript-project &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi new typescript
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Follow the prompts, accepting the defaults. Then, add the Terraform module to your TypeScript project with &lt;code&gt;pulumi package add&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi package add s3-bucket-aws
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This fairly magical command generates a local TypeScript SDK at &lt;code&gt;./sdks&lt;/code&gt; that wraps the module as a &lt;a href="https://www.pulumi.com/docs/iac/concepts/components/"&gt;Pulumi component&lt;/a&gt;. To use it, open up &lt;code&gt;index.ts&lt;/code&gt; and replace its contents with the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./sdks/s3-bucket&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;my-module&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;bucketPrefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-new-bucket&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bucketArn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Configure your AWS credentials locally as before — or alternatively, &lt;a href="https://www.pulumi.com/docs/esc/concepts/environments/#using-environments-with-pulumi-iac"&gt;use the ESC environment you created earlier&lt;/a&gt; if you set one up — then run &lt;code&gt;pulumi up&lt;/code&gt; to deploy in the usual way:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi up
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Updating &lt;span class="o"&gt;(&lt;/span&gt;dev&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Type Name Status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + pulumi:pulumi:Stack my-typescript-project-dev created &lt;span class="o"&gt;(&lt;/span&gt;8s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + └─ s3-bucket:index:Module my-module created &lt;span class="o"&gt;(&lt;/span&gt;5s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ├─ random:index:Id my-module-suffix created &lt;span class="o"&gt;(&lt;/span&gt;0.21s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + └─ aws:index:S3Bucket my-module-this created &lt;span class="o"&gt;(&lt;/span&gt;2s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Outputs:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; bucketArn : &lt;span class="s2"&gt;&amp;#34;arn:aws:s3:::my-new-bucket-bbfa52db&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; bucketName: &lt;span class="s2"&gt;&amp;#34;my-new-bucket-bbfa52db&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Resources:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + &lt;span class="m"&gt;4&lt;/span&gt; created
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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&amp;rsquo;ll prefer a simpler or more dynamic option. In these situations, you can instead choose to load the module at runtime.&lt;/p&gt;
&lt;p&gt;In TypeScript, you&amp;rsquo;d do that with the &lt;code&gt;@pulumi/hcl&lt;/code&gt; module. Try that now by installing it in your project:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ npm install @pulumi/hcl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; and using it in your program:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;hcl&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/hcl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;hcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;my-module&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tf.pulumi.com/&amp;lt;your-org&amp;gt;/s3-bucket/aws&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.1.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;bucket_prefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-new-bucket&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bucket_arn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because this approach uses untyped references, you&amp;rsquo;ll trade a little type safety for flexibility — but either way, you&amp;rsquo;re able to keep using the modules you&amp;rsquo;ve already built, while migrating to &lt;em&gt;new&lt;/em&gt; infrastructure in general-purpose languages on whatever schedule works best for your team.&lt;/p&gt;
&lt;h2 id="write-and-run-hcl-natively"&gt;Write and run HCL, natively&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;The easiest way to get a feel for it is to create a new project from a template:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ mkdir ../my-hcl-project &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi new aws-hcl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As before, step through the prompts, then open the generated Pulumi project&amp;rsquo;s &lt;code&gt;main.tf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;required_providers&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; aws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;pulumi/aws&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create an AWS resource (S3 Bucket)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;aws_s3_bucket&amp;#34; &amp;#34;my-bucket&amp;#34;&lt;/span&gt; {}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Export the name of the bucket
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can deploy this out of the box (after setting your AWS credentials again, of course) to see the magic happen:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi up
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Updating &lt;span class="o"&gt;(&lt;/span&gt;dev&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Type Name Status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + pulumi:pulumi:Stack my-hcl-project-dev created &lt;span class="o"&gt;(&lt;/span&gt;6s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + └─ aws:s3:Bucket my-bucket created &lt;span class="o"&gt;(&lt;/span&gt;3s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Outputs:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; bucket_name: &lt;span class="s2"&gt;&amp;#34;my-bucket-7282e67&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Resources:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + &lt;span class="m"&gt;2&lt;/span&gt; created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Duration: 7s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This template happens to use the &lt;code&gt;pulumi/aws&lt;/code&gt; provider, but you&amp;rsquo;re free to pull in any others as well: &lt;a href="https://www.pulumi.com/docs/iac/concepts/providers/"&gt;native Pulumi providers&lt;/a&gt;, official &lt;code&gt;hashicorp/*&lt;/code&gt; providers, community-supported providers, and more. In fact, you can try that now by replacing the code in &lt;code&gt;main.tf&lt;/code&gt; with the same code you used in the Terraform program you left off with earlier — only without the explicit &lt;code&gt;terraform &amp;gt; backend&lt;/code&gt; block, as it&amp;rsquo;s no longer needed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s3-bucket&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tf.pulumi.com/&amp;lt;your-org&amp;gt;/s3-bucket/aws&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.1.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; bucket_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-tf-project-bucket&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;s3&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;bucket_name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bucket_arn&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;s3&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;bucket_arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run &lt;code&gt;pulumi up&lt;/code&gt; again, and you&amp;rsquo;ll see the module get picked up and used, just as before:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi up
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Updating &lt;span class="o"&gt;(&lt;/span&gt;dev&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Type Name Status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + pulumi:pulumi:Stack my-hcl-project-dev created &lt;span class="o"&gt;(&lt;/span&gt;10s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + └─ components:index:C619d883588c0366 s3-bucket created &lt;span class="o"&gt;(&lt;/span&gt;8s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ├─ random:index:Id s3-bucket.suffix created &lt;span class="o"&gt;(&lt;/span&gt;0.22s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + └─ aws:index:S3Bucket s3-bucket.this created &lt;span class="o"&gt;(&lt;/span&gt;3s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Outputs:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; bucket_arn : &lt;span class="s2"&gt;&amp;#34;arn:aws:s3:::my-tf-project-bucket-f61cea35&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; bucket_name: &lt;span class="s2"&gt;&amp;#34;my-tf-project-bucket-f61cea35&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Resources:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + &lt;span class="m"&gt;4&lt;/span&gt; created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Duration: 12s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And with that, our tour is complete. Be sure to clean up both projects with a &lt;code&gt;terraform destroy&lt;/code&gt; and &lt;code&gt;pulumi destroy&lt;/code&gt; when you&amp;rsquo;re done.&lt;/p&gt;
&lt;h2 id="where-to-go-next"&gt;Where to go next&lt;/h2&gt;
&lt;p&gt;Now you&amp;rsquo;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&amp;rsquo;ve already built.&lt;/p&gt;
&lt;p&gt;A few next steps to keep the learning going:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explore our &lt;a href="https://www.pulumi.com/templates/"&gt;architecture templates&lt;/a&gt; to bootstrap new HCL projects easily&lt;/li&gt;
&lt;li&gt;Read up on &lt;a href="https://www.pulumi.com/docs/iac/get-started/terraform/terraform-state-backend/"&gt;Terraform state&lt;/a&gt;, &lt;a href="https://www.pulumi.com/docs/iac/get-started/terraform/terraform-remote-execution/"&gt;remote execution&lt;/a&gt;, and &lt;a href="https://www.pulumi.com/docs/idp/concepts/terraform-modules/"&gt;Terraform modules&lt;/a&gt; in Pulumi Cloud&lt;/li&gt;
&lt;li&gt;Dive into &lt;a href="https://www.pulumi.com/docs/iac/languages-sdks/hcl/"&gt;Pulumi and HCL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Check out the &lt;a href="https://www.pulumi.com/releases/terraform-state-backend-modules-hcl/"&gt;full set of features&lt;/a&gt; in this release&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;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&amp;rsquo;t behave as you&amp;rsquo;d expect, &lt;a href="https://github.com/pulumi/pulumi/issues"&gt;open an issue&lt;/a&gt;, let us know in the &lt;a href="https://slack.pulumi.com/"&gt;Pulumi Community Slack&lt;/a&gt;, or &lt;a href="https://www.pulumi.com/contact/?form=sales"&gt;reach out&lt;/a&gt; if you&amp;rsquo;d like to learn more.&lt;/p&gt;
&lt;p&gt;Happy building!&lt;/p&gt;</description><author>Christian Nunciato</author><category>terraform</category><category>hcl</category><category>pulumi-cloud</category><category>infrastructure-as-code</category><category>modules</category></item></channel></rss>