<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Thomas Gummerer</title><link>https://www.pulumi.com/blog/author/thomas-gummerer/</link><description>Pulumi blog posts: Thomas Gummerer.</description><language>en-us</language><pubDate>Wed, 01 Apr 2026 00:00:00 +0000</pubDate><item><title>Introducing OTel Tracing in the Pulumi CLI</title><link>https://www.pulumi.com/blog/introducing-otel-tracing-in-the-pulumi-cli/</link><pubDate>Wed, 01 Apr 2026 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/introducing-otel-tracing-in-the-pulumi-cli/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/introducing-otel-tracing-in-the-pulumi-cli/index.png" /&gt;
&lt;p&gt;Tracing is an important part of our CLI observability story. So far we&amp;rsquo;ve relied on (the now deprecated) &lt;a href="https://opentracing.io/"&gt;OpenTracing&lt;/a&gt; for this. We have now added OTel tracing to the CLI, which is more future-proof, and should in most cases give you a better view over what the CLI is doing.&lt;/p&gt;
&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;We introduced tracing using &lt;a href="https://opentracing.io"&gt;OpenTracing&lt;/a&gt; &lt;a href="https://github.com/pulumi/pulumi/pull/521"&gt;all the way back in 2017&lt;/a&gt;, before &lt;a href="https://opentelemetry.io/"&gt;OpenTelemetry&lt;/a&gt; was a thing. This served us well over the years, but as OpenTracing was deprecated, and OTel emerged as the new and maintained thing, it got harder and harder to justify further investment in a tracing infrastructure that was deprecated. Last year we started focusing more on performance, and it became more and more clear that we&amp;rsquo;d either have to enhance our current OpenTracing setup, or do the work to switch to OTel.&lt;/p&gt;
&lt;p&gt;In the end it was a relatively easy decision to move to the more modern and fully supported OTel, especially as more and more tooling around it starts emerging.&lt;/p&gt;
&lt;h2 id="enter-otel"&gt;Enter OTel&lt;/h2&gt;
&lt;p&gt;With the decision to move to OTel made, the only thing left to decide was how to implement it. There were a couple of constraints we faced here. First, we wanted to make sure that traces are easily shareable. This means ideally a text file in whatever format, that can be shared easily. Second, pulumi&amp;rsquo;s plugin system works by spawning a new process per plugin. We want to get traces from each of these plugins to make sure we have as much coverage as possible. And third, ideally we also want to get traces from plugins that only implement OpenTracing, but not OTel yet, since someone can upgrade the CLI, but not the plugins for example.&lt;/p&gt;
&lt;p&gt;Given these constraints, we decided to implement an OTel collector in the CLI, that could then forward the traces to whatever output format we want. This means that plugins only ever need to send traces back to the CLI over &lt;a href="https://grpc.io/"&gt;gRPC&lt;/a&gt;, and the CLI will do any further processing. This means only one process will write to the file, if requested.&lt;/p&gt;
&lt;p&gt;For plugins we always request both OpenTracing and OTel traces. If both are requested and OTel is supported by the plugin, the plugin is expected to only send the OTel version of the traces. For OpenTracing traces, we collect them in the collector in the CLI, and then translate them internally to OTel traces. This way we can still get the traces from older plugins, without them needing to change anything.&lt;/p&gt;
&lt;h2 id="try-it-out"&gt;Try it out&lt;/h2&gt;
&lt;p&gt;Currently the OTel exporter supports both exporting the traces directly via gRPC to a collector, or to a file, where the traces are JSON encoded. This file can then be shared and imported into a trace viewer at a later time. To do this, you can use the &lt;code&gt;--otel-traces &amp;lt;file://&amp;lt;filename&amp;gt;|grpc://&amp;lt;exporter-address&amp;gt;&amp;gt;&lt;/code&gt; flag, using pulumi version v3.226 or newer. For further documentation see &lt;a href="https://www.pulumi.com/docs/support/debugging/performance-tracing/#opentelemetry-tracing"&gt;our performance tracing docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To view the traces, you can use one of the various exporters that exist. Popular options include &lt;a href="https://www.jaegertracing.io/"&gt;Jaeger&lt;/a&gt;, &lt;a href="https://github.com/CtrlSpice/otel-desktop-viewer"&gt;OTel Desktop Viewer&lt;/a&gt;, or &lt;a href="https://github.com/ymtdzzz/otel-tui"&gt;OTel TUI&lt;/a&gt; if you prefer not leaving your terminal. Once you&amp;rsquo;ve ingested the logs there either by uploading the trace file, or sending them directly by giving pulumi the exporter address, look for the &lt;code&gt;pulumi-cli: pulumi&lt;/code&gt; root span.&lt;/p&gt;
&lt;p&gt;All further spans will be parented to that root span, and you should thus be able to see a nice flow diagram in the viewer of your choice.&lt;/p&gt;
&lt;p&gt;As always, we would love any feedback either in the &lt;a href="https://slack.pulumi.com/"&gt;Community Slack&lt;/a&gt;, or through a &lt;a href="https://github.com/pulumi/pulumi/issues"&gt;GitHub issue&lt;/a&gt;.&lt;/p&gt;</description><author>Thomas Gummerer</author><category>features</category><category>pulumi-cli</category></item><item><title>Now GA: Up to 20x Faster Pulumi Operations for Everyone</title><link>https://www.pulumi.com/blog/journaling-ga/</link><pubDate>Thu, 05 Mar 2026 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/journaling-ga/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/journaling-ga/index.png" /&gt;
&lt;p&gt;In January, we &lt;a href="https://www.pulumi.com/blog/journaling/"&gt;introduced a major performance enhancement for Pulumi Cloud&lt;/a&gt; through a fundamental change to how Pulumi manages state that speeds up operations by up to 20x. After a staged rollout across many organizations, &lt;strong&gt;it is now enabled by default for every Pulumi Cloud operation&lt;/strong&gt;. No opt-in required—just use Pulumi CLI v3.225.0+ with Pulumi Cloud. The improvement applies to &lt;code&gt;pulumi up&lt;/code&gt;, &lt;code&gt;pulumi destroy&lt;/code&gt;, and &lt;code&gt;pulumi refresh&lt;/code&gt;; &lt;code&gt;pulumi preview&lt;/code&gt; does not modify state, so it is unchanged.&lt;/p&gt;
&lt;h2 id="what-this-means-for-you"&gt;What this means for you&lt;/h2&gt;
&lt;p&gt;First and foremost, nothing about how you work with &lt;code&gt;pulumi&lt;/code&gt; needs to change. Your updates now benefit from better parallelism and should thus complete faster. Before this change, &lt;code&gt;pulumi&lt;/code&gt; always saved a full snapshot to the cloud, so the current state could always be recovered if something goes wrong. With journaling, we now only send the state of each operation, which allows us to send these updates in parallel, as long as resources are not related to each other. For the full deep dive, see the blog post linked above.&lt;/p&gt;
&lt;h2 id="production-results"&gt;Production results&lt;/h2&gt;
&lt;p&gt;Since January, we&amp;rsquo;ve had many early adopters of journaling. This helped us shake out one final bug on the server side, and journaling has been stable since then. With that we feel confident in rolling this out to all our users.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve also gathered some real-world data on how journaling is performing. The data from the preview period shows some significant improvements for update times. For stacks with fewer than 100 resources, the median improvement is 25.3%, while the p90 improvement is 75.2%, and we&amp;rsquo;ve seen a p99 improvement of up to 92.6% Meanwhile, for larger stacks, the median improvement is 60.2%. We need more data for stacks with more than 100 resources, we will update this blog once that comes in.&lt;/p&gt;
&lt;p&gt;This data already shows the expected significant improvement in update times, especially for larger stacks, though the improvements strongly depend on the shape and type of resources that are being set up. Stacks with many resources, that are quick to update benefit more than smaller stacks with slower to set up resources. For more numbers see also the &lt;a href="https://www.pulumi.com/blog/journaling/#benchmarks"&gt;Benchmarks section in the previous blog post&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="what-you-need-to-do"&gt;What you need to do&lt;/h2&gt;
&lt;p&gt;While this was an opt-in process using the &lt;code&gt;PULUMI_ENABLE_JOURNALING&lt;/code&gt; environment variable, this opt-in is no longer required. Just upgrade your Pulumi CLI to v3.225.0+ and use the Pulumi Cloud backend, and journaling will automatically speed up your updates.&lt;/p&gt;
&lt;p&gt;If you encounter any issues, reach out on the &lt;a href="https://slack.pulumi.com/"&gt;Pulumi Community Slack&lt;/a&gt; or through &lt;a href="https://support.pulumi.com/hc/en-us"&gt;Pulumi Support&lt;/a&gt;. You can also set the &lt;code&gt;PULUMI_DISABLE_JOURNALING=true&lt;/code&gt; env variable to opt out of journaling.&lt;/p&gt;</description><author>Thomas Gummerer</author><author>Pat Gavlin</author><category>performance</category><category>pulumi-cloud</category><category>features</category><category>releases</category></item><item><title>Speeding up Pulumi Operations by up to 20x</title><link>https://www.pulumi.com/blog/journaling/</link><pubDate>Mon, 12 Jan 2026 18:57:55 +0200</pubDate><guid>https://www.pulumi.com/blog/journaling/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/journaling/index.png" /&gt;
&lt;p&gt;Today we&amp;rsquo;re introducing an improvement that can speed up operations by up to 20x. At every operation, and at every step within an operation, &lt;code&gt;pulumi&lt;/code&gt; saves a snapshot of your cloud infrastructure. This gives &lt;code&gt;pulumi&lt;/code&gt; a current view of state even if something fails mid-operation, but it comes with a performance penalty for large stacks. Here&amp;rsquo;s how we fixed it.&lt;/p&gt;
&lt;h2 id="benchmarks"&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;Before getting into the more technical details, here are a number of benchmarks demonstrating what this new experience looks like. To run the benchmarks we picked a couple of Pulumi projects: one that can be set up massively parallel, which is the worst case scenario for the old snapshot system, and another that looks a little more like a real world example. Note that we conducted all of these benchmarks in Europe, connecting to Pulumi Cloud, which runs in AWS&amp;rsquo;s &lt;code&gt;us-west-2&lt;/code&gt; region, so exact numbers may vary based on your location and internet connection. This should however give a good indication of the performance improvements.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re benchmarking two somewhat large stacks, both of which are or were used at Pulumi. The first program sets up a website using AWS bucket objects. We&amp;rsquo;re using the &lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-static-website"&gt;aws-ts-static-website&lt;/a&gt; example here with a small subset of the fraction from our docs site. This means we&amp;rsquo;re setting up more than 3000 bucket objects, with 3222 resources in total.&lt;/p&gt;
&lt;p&gt;The benchmarks were measured using the &lt;code&gt;time&lt;/code&gt; built-in command and using the best time in a best-of-three benchmark. The network traffic was measured using &lt;code&gt;tcpdump&lt;/code&gt;, limiting the measured traffic to only the IP addresses for Pulumi Cloud. Finally, &lt;code&gt;tshark&lt;/code&gt; was used to process the packet captures and count the bytes sent.&lt;/p&gt;
&lt;p&gt;All the benchmarks are run with journaling off (the default experience) and with journaling on (the new experience). To begin with, let&amp;rsquo;s look at the results when creating our stack from scratch:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Bytes sent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Without journaling&lt;/td&gt;
&lt;td&gt;58m26s&lt;/td&gt;
&lt;td&gt;16.5MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;With journaling&lt;/td&gt;
&lt;td&gt;02m50s&lt;/td&gt;
&lt;td&gt;2.3MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Now let&amp;rsquo;s have a look at what this looks like if we only change half the resources, but the remaining ones remain unchanged:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Bytes sent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Without journaling&lt;/td&gt;
&lt;td&gt;34m49s&lt;/td&gt;
&lt;td&gt;13.8MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;With journaling&lt;/td&gt;
&lt;td&gt;01m45s&lt;/td&gt;
&lt;td&gt;2.3MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The second example is setting up an instance of the Pulumi app and API. Here we&amp;rsquo;ll have an example that&amp;rsquo;s a bit more dominated by the cost of setting up the actual infrastructure in the cloud, but we still have a very noticeable improvement in the time it takes to set up the stack.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Bytes sent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Without journaling&lt;/td&gt;
&lt;td&gt;17m52s&lt;/td&gt;
&lt;td&gt;18.5MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;With journaling&lt;/td&gt;
&lt;td&gt;9m12s&lt;/td&gt;
&lt;td&gt;5.9MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src="time.png" alt="Comparison chart of the timings shown in the tables above"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="size.png" alt="Comparison chart of the bytes sent shown in the tables above"&gt;&lt;/p&gt;
&lt;div class="note note-tip"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-lightbulb-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;To use this feature, you need a &lt;code&gt;pulumi&lt;/code&gt; version newer than v3.211.0, and set the &lt;code&gt;PULUMI_ENABLE_JOURNALING&lt;/code&gt; environment variable to &lt;code&gt;true&lt;/code&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you are interested in the more technical details read on!&lt;/p&gt;
&lt;h2 id="introduction-to-snapshotting"&gt;Introduction to snapshotting&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pulumi&lt;/code&gt; keeps track of all resources in a stack in a snapshot. This snapshot is stored in the stack&amp;rsquo;s configured backend, which is either the Pulumi Cloud or a DIY backend. Future operations on the stack then use this snapshot to figure out which resources need to be created, updated or deleted.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pulumi&lt;/code&gt; creates a new snapshot at the beginning and at the end of each resource operation to minimize the possibility of untracked changes even if a deployment is aborted unexpectedly (for example due to network issues, power outages, or bugs).&lt;/p&gt;
&lt;p&gt;At the beginning of the operation, &lt;code&gt;pulumi&lt;/code&gt; adds a new &amp;ldquo;pending operation&amp;rdquo; to the snapshot. Pending operations declare the intent to mutate a resource. If a pending operation is left in the snapshot (in other words the operation started, but &lt;code&gt;pulumi&lt;/code&gt; couldn&amp;rsquo;t record the end of it), the next operation will try to resolve this. If we have an ID for the resource already, for example on partial updates/deletes, &lt;code&gt;pulumi&lt;/code&gt; will try to read the resource state from the cloud and resolve it internally. If there is no ID yet, &lt;code&gt;pulumi&lt;/code&gt; will ask the user to check the actual state of the resource. Depending on the user&amp;rsquo;s response, &lt;code&gt;pulumi&lt;/code&gt; will either remove the operation from the snapshot or import the resource. This is because it is possible that the resource has been set up correctly or that the resource creation failed. If &lt;code&gt;pulumi&lt;/code&gt; aborted midway through the operation, it&amp;rsquo;s impossible to know which state the resource is in.&lt;/p&gt;
&lt;p&gt;Once an operation finishes, the pending operation is removed and the resource&amp;rsquo;s final state is recorded in the snapshot.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also some additional metadata that is stored in the snapshot that is only updated infrequently.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how the &lt;a href="https://github.com/pulumi/pulumi/blob/76588836f542c95e8f43ed785cc1828c40369ada/pkg/resource/deploy/snapshot.go#L34"&gt;snapshot looks in code&lt;/a&gt;. This snapshot is serialized and sent to the backend. &lt;code&gt;Resources&lt;/code&gt; holds the list of known resource states and is updated after each operation finishes, and &lt;code&gt;PendingOperations&lt;/code&gt; is the list of pending operations described above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Snapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Manifest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Manifest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// a deployment manifest of versions, checksums, and so on.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SecretsManager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Manager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// the secrets manager to use when serializing this snapshot.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Resources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// all resources and their associated states.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PendingOperations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Operation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// all currently pending resource operations.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SnapshotMetadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// metadata associated with the snapshot.&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="p"&gt;}&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;Before we dive in deeper, we also need to understand a little bit about how the &lt;code&gt;pulumi&lt;/code&gt; engine works internally. Whenever a &lt;code&gt;pulumi&lt;/code&gt; operation is run (e.g. &lt;code&gt;pulumi up&lt;/code&gt;, &lt;code&gt;pulumi destroy&lt;/code&gt;, &lt;code&gt;pulumi refresh&lt;/code&gt; etc.), the engine internally generates and executes a series of steps, to create, update, delete etc. resources. To maintain correct relationships between resources, the steps need to be executed in a partial order such that no step is executed until all of the steps it depends on have executed successfully. Steps may otherwise execute concurrently.&lt;/p&gt;
&lt;p&gt;As each step is responsible for updating a single resource, we can generate a snapshot of the state before each step starts, and after it completes. Before each step starts, we create a pending operation, and add it to the &lt;code&gt;PendingOperations&lt;/code&gt; list. After that step completes, we remove the pending operation from that list, and update the &lt;code&gt;Resources&lt;/code&gt; list, either adding a resource, removing it, or updating it, depending on the kind of operation we just executed.&lt;/p&gt;
&lt;p&gt;After this introduction, we can dive into what&amp;rsquo;s slow, how we fixed it, and some benchmarks.&lt;/p&gt;
&lt;h2 id="why-is-it-slow"&gt;Why is it slow?&lt;/h2&gt;
&lt;p&gt;To make sure the state is always as up-to-date as possible, even if there are any network hiccups/power outages etc., a step won&amp;rsquo;t start until the snapshot that includes the pending operation is confirmed to be stored in the backend. Similarly an operation won&amp;rsquo;t be considered finished until the snapshot with an updated resources list is confirmed to be stored in the backend.&lt;/p&gt;
&lt;p&gt;To send the current state to the backend, we simply serialize it as a JSON file, and send it to the backend. However, as mentioned above, steps can be executed in parallel. If we uploaded the snapshot at the beginning and end of every step with no serialization, there would be a risk that we overwrite a new snapshot with an older one, leading to incorrect data.&lt;/p&gt;
&lt;p&gt;Our workaround for that is to serialize the snapshot uploads, uploading one snapshot at a time. This gives us the data integrity properties we want, however it can slow step execution down, especially on internet connections with lower bandwidth, and/or high latency.&lt;/p&gt;
&lt;p&gt;This impacts performance especially for large stacks, as we upload the whole snapshot every time, which can take some time if the snapshot is getting big. For the Pulumi Cloud backend we improved on this a little &lt;a href="https://github.com/pulumi/pulumi/pull/10788"&gt;at the end of 2022&lt;/a&gt;. We implemented a diff based protocol, which is especially helpful for large snapshots, as we only need to send the diff between the old and the new snapshot, and Pulumi Cloud can then reconstruct the full snapshot based on that. This reduces the amount of data that needs to be transferred, thus improving performance.&lt;/p&gt;
&lt;p&gt;However, the snapshotting is still a major bottleneck for large &lt;code&gt;pulumi&lt;/code&gt; operations. Having to serially upload the snapshot twice for each step does still have a big impact on performance, especially if many resources are modified in parallel. Furthermore, the time spent performing textual diffs between snapshots scales in proportion to the size of the data being processed, which adds additional execution time to each operation.&lt;/p&gt;
&lt;h2 id="fast-but-lacking-data-integrity"&gt;Fast, but lacking data integrity?&lt;/h2&gt;
&lt;p&gt;As long as &lt;code&gt;pulumi&lt;/code&gt; can complete its operation, there&amp;rsquo;s no need for the intermediate checkpoints. We could allow &lt;code&gt;pulumi&lt;/code&gt; operations to skip uploading the intermittent checkpoints to the backend. This, of course, avoids the single serialization point we have sending the snapshots to the backend, and thus makes the operation much more performant.&lt;/p&gt;
&lt;p&gt;However, it also has the serious disadvantage of compromising some of the data integrity guarantees &lt;code&gt;pulumi&lt;/code&gt; gives you. If anything goes wrong during the update, &lt;code&gt;pulumi&lt;/code&gt; has no notion of what happened until then, potentially leaving orphaned resources in the provider, or leaving resources in the state that no longer exist.&lt;/p&gt;
&lt;p&gt;Neither of these solutions is very satisfying, as the tradeoff is either performance or data integrity. We would like to have our cake and eat it too, and that&amp;rsquo;s exactly what we&amp;rsquo;re doing with journaling.&lt;/p&gt;
&lt;h2 id="enter-journaling"&gt;Enter journaling&lt;/h2&gt;
&lt;p&gt;To achieve this, we went back to the drawing board, and asked ourselves, &amp;ldquo;What would a solution look like that&amp;rsquo;s both performant &lt;em&gt;and&lt;/em&gt; preserves data integrity throughout the update?&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Making that happen is possible because of three facts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We always start with the same snapshot on the backend and the CLI.&lt;/li&gt;
&lt;li&gt;Every step the engine executes affects only one resource.&lt;/li&gt;
&lt;li&gt;We have a service that can reconstruct a snapshot from what is given to it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(The third point here already hints at it, but this feature is only available and made possible by Pulumi Cloud, not on the DIY backend).&lt;/p&gt;
&lt;p&gt;What if instead of sending the whole snapshot, or a diff of the snapshot, we could send the individual changes to the base snapshot to the service, which could then apply it, and reconstruct a full snapshot from it? This is exactly what we are doing here, in the form of what we call journal entries. Each journal entry has the following form:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKindBegin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKindSuccess&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKindFailure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKindRefreshSuccess&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKindOutputs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKindWrite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKindSecretsManager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKindRebuiltBaseState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&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="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="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Version of the journal entry format.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;version&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Kind of journal entry.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JournalEntryKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;kind&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Sequence ID of the operation.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SequenceID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;sequenceID&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ID of the operation this journal entry is associated with.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;OperationID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;operationID&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ID for the delete Operation that this journal entry is associated with.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RemoveOld&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;removeOld&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ID for the delete Operation that this journal entry is associated with.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RemoveNew&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;removeNew&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// PendingReplacementOld is the index of the resource that&amp;#39;s to be marked as pending replacement&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PendingReplacementOld&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;pendingReplacementOld,omitempty&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// PendingReplacementNew is the operation ID of the new resource to be marked as pending replacement&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PendingReplacementNew&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;pendingReplacementNew,omitempty&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// DeleteOld is the index of the resource that&amp;#39;s to be marked as deleted.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DeleteOld&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;deleteOld,omitempty&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// DeleteNew is the operation ID of the new resource to be marked as deleted.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DeleteNew&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;deleteNew,omitempty&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// The resource state associated with this journal entry.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ResourceV3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;state,omitempty&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// The operation associated with this journal entry, if any.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Operation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;OperationV2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;operation,omitempty&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// If true, this journal entry is part of a refresh operation.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RebuildDependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;isRefresh,omitempty&amp;#34;`&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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// The secrets manager associated with this journal entry, if any.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SecretsProvider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;SecretsProvidersV1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;secretsProvider,omitempty&amp;#34;`&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="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// NewSnapshot is the new snapshot that this journal entry is associated with.&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NewSnapshot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;DeploymentV3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;newSnapshot,omitempty&amp;#34;`&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="p"&gt;}&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;These journal entries encode all the information needed to reconstruct the snapshot from them. Each journal entry can be sent in parallel from the engine, and the snapshot will still be fully valid. All journal entries have a Sequence ID attached to them, and they need to be replayed in that order on the service side to make sure we get a valid snapshot. It is however okay to replay without journal entries that have not yet been received by the service, and whose sequence ID is thus missing. This is safe because the engine only sends entries in parallel for resources whose parents/dependencies have been fully created and confirmed by the service.&lt;/p&gt;
&lt;p&gt;This way we make sure that the resources list is always in the correct partial order that is required by the engine to function correctly, and for the snapshot to be considered valid.&lt;/p&gt;
&lt;p&gt;The algorithm looks as follows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Apply snapshot writes. This replaces the full snapshot we have on the service.
# We do this if default providers change, because we don&amp;#39;t emit steps for that, as
# we do for the rest of the operations.
snapshot = find_write_journal_entry_or_use_base(base, journal)
# Track changes
deletes, snapshot_deletes, mark_deleted, mark_pending = set(), set(), set(), set()
operation_id_to_resource_index = {}
# Process journal entries. This is the main algorithm, that adds new resources
# to the snapshot, removes existing ones, deals with refreshes, and operations
# that update outputs.
incomplete_ops = {}
has_refresh = false
index = 0
for entry in journal:
match entry.type:
case BEGIN:
incomplete_ops[entry.op_id] = entry
case SUCCESS:
del incomplete_ops[entry.op_id]
if entry.state and entry.op_id:
resources.append(entry.state)
operation_id_to_resource_index.add(entry.op_id, index)
index++
if entry.remove_old:
snapshot_deletes.add(entry.remove_old)
if entry.remove_new:
deletes[remove_new] = true
if entry.pending_replacement:
mark_pending(entry.pending_replacement)
if entry.delete:
mark_deleted(entry.delete)
has_refresh |= entry.is_refresh
case REFRESH_SUCCESS:
del incomplete_ops[entry.op_id]
has_refresh = true
if entry.remove_old:
if entry.state:
snapshot_replacements[entry.remove_old] = entry.state
else:
snapshot_deletes.add(entry.remove_old)
if entry.remove_new:
if entry.state:
deletes[entry.remove_new] = true
else:
resources.replace(operation_id_to_resource_index(entry.remove_new), entry.state)
case FAILURE:
del incomplete_ops[entry.op_id]
case OUTPUTS:
if entry.state and entry.remove_old:
snapshot_replacements[entry.remove_old] = entry.state
if entry.state and entry.remove_new:
resources.replace(operation_id_to_resource_index(entry.remove_new), entry.state)
deletes = deletes.map(|i| =&amp;gt; operation_id_to_resource_index[i])
# Now that we have marked all the operations, and created a new list of resources, we can
# go through them, and merge the list of new resources and old resources from the snapshot
# that remain together.
for i, res in resources:
if i in deletes:
remove_from_resources(resources, i)
# Merge snapshot resources. These resources have not been touched by the update, and will
# thus be appended to the end of the resource list. We also need to mark existing resources as
# `Delete` and `PendingReplacement` here.
for i, res in enumerate(snapshot.resources):
if i not in snapshot_deletes:
if i in snapshot_replacements:
resources.append(snapshot_replacements[i])
else:
if i in mark_deleted:
res.delete = true
if i in mark_pending:
res.pending_replacement = true
resources.append(res)
# Collect pending operations. These are stored separately from the resources list
# in the snapshot.
pending_ops = [op.operation for op in incomplete_ops.values() if op.operation]
pending_ops.extend([op for op in snapshot.pending_ops if op.type == CREATE])
# Rebuild dependencies if necessary. Refreshes can delete parents or dependencies
# of resources, without affecting the resource itself directly. We need to now remove
# these relationships to make sure the snapshot remains valid.
if has_refresh:
rebuild_dependencies(resources)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The full documentation of the algorithm can be found in our &lt;a href="https://pulumi-developer-docs.readthedocs.io/latest/docs/architecture/deployment-execution/state.html#snapshot-journaling"&gt;developer docs&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="rollout"&gt;Rollout&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pulumi&lt;/code&gt; state is a very central part of &lt;code&gt;pulumi&lt;/code&gt;, so we wanted to be extra careful with the rollout to make sure we don&amp;rsquo;t break anything. We did this in a few stages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We implemented the replay interface inside the &lt;code&gt;pulumi&lt;/code&gt; CLI, and ran it in parallel with the current snapshotting implementation in our tests. The snapshots were then compared automatically, and tests made to fail when the result didn&amp;rsquo;t match.&lt;/li&gt;
&lt;li&gt;Since tests can&amp;rsquo;t cover all possible edge cases, the next step was to run the journaler in parallel with the current snapshotting implementation internally. This was still without sending the results to the service. However we would compare the snapshot, and send an error event to the service if the snapshot didn&amp;rsquo;t match. In our data warehouse we could then inspect any mismatches, and fix them. Since this does involve the service in a minor way, we would only do this if the user is using the Cloud backend.&lt;/li&gt;
&lt;li&gt;Next up was adding a feature flag for the service, so journaling could be turned on selectively for some orgs. At the same time we implemented an opt-in environment variable in the CLI (&lt;code&gt;PULUMI_ENABLE_JOURNALING&lt;/code&gt;), so the feature could be selectively turned on by users, if both the feature flag is enabled and the user sets the environment variable. This way we could slowly start enabling this in our repos, e.g. first in the integration tests for &lt;code&gt;pulumi/pulumi&lt;/code&gt;, then in the tests for &lt;code&gt;pulumi/examples&lt;/code&gt; and &lt;code&gt;pulumi/templates&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Allow users to start opting in. If you want to opt-in with your org, please reach out to us, either on the &lt;a href="https://slack.pulumi.com/"&gt;Community Slack&lt;/a&gt;, or through our &lt;a href="https://support.pulumi.com/hc/en-us"&gt;Support channels&lt;/a&gt;, and we&amp;rsquo;ll opt your org into the feature flag. Then you can begin seeing the performance improvements by setting the &lt;code&gt;PULUMI_ENABLE_JOURNALING&lt;/code&gt; env variable to true.&lt;/li&gt;
&lt;li&gt;Turn on the feature flag for everyone, but still require the &lt;code&gt;PULUMI_ENABLE_JOURNALING&lt;/code&gt; env variable to be set to true. (We are here right now).&lt;/li&gt;
&lt;li&gt;Flip the feature on by default, but still allow users to opt out using a &lt;code&gt;PULUMI_DISABLE_JOURNALING&lt;/code&gt; env variable.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;While these performance improvements hopefully make your day to day use of &lt;code&gt;pulumi&lt;/code&gt; quicker and more enjoyable, we&amp;rsquo;re not quite done here. We&amp;rsquo;re looking at some other performance improvements, that will hopefully speed up your workflows even more.&lt;/p&gt;</description><author>Thomas Gummerer</author><category>journaling</category><category>performance</category><category>data-integrity</category><category>features</category></item><item><title>Introducing `pulumi state move`: Move Resources Between Stacks or Projects</title><link>https://www.pulumi.com/blog/move-resources-between-stacks/</link><pubDate>Thu, 25 Jul 2024 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/move-resources-between-stacks/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/move-resources-between-stacks/index.png" /&gt;
&lt;p&gt;Today we&amp;rsquo;re announcing the &lt;a href="https://www.pulumi.com/docs/iac/cli/commands/pulumi_state_move/"&gt;&lt;code&gt;pulumi state move&lt;/code&gt;&lt;/a&gt; command, which can be used to move resources that are managed by Pulumi between different stacks and/or projects. With the &lt;code&gt;pulumi state move&lt;/code&gt; command, you can refactor your Pulumi Infrastructure as Code without any disruption to your deployed cloud infrastructure, enabling you to evolve and scale with confidence.&lt;/p&gt;
&lt;p&gt;When refactoring a Pulumi project from a &lt;a href="https://www.pulumi.com/docs/using-pulumi/organizing-projects-stacks/#monolithic"&gt;monolithic&lt;/a&gt; structure to &lt;a href="https://www.pulumi.com/docs/using-pulumi/organizing-projects-stacks/#micro-stacks"&gt;micro stacks&lt;/a&gt;, you might need to move resources between different projects or stacks, without recreating them. While this has been possible with some significant manual effort to hand-modify Pulumi state files (our users came up with &lt;a href="https://github.com/pulumi/pulumi/issues/3389#issuecomment-679020482"&gt;automated solutions&lt;/a&gt; for this!), doing so can be error prone and time consuming. Having a simpler, integrated solution for this problem was a &lt;a href="https://github.com/pulumi/pulumi/issues/3389"&gt;much requested feature&lt;/a&gt;. The new &lt;code&gt;pulumi state move&lt;/code&gt; command, combined with the &lt;a href="https://www.pulumi.com/docs/concepts/options/aliases/"&gt;&lt;code&gt;aliases&lt;/code&gt;&lt;/a&gt; command for refactoring resources within stacks, offers the richest collection of tools for refactoring IaC at scale available in the industry today.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;pulumi state move&lt;/code&gt; command works as follows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ pulumi state move --help
Move resources from one stack to another
This command can be used to move resources from one stack to another. This can be useful when
splitting a stack into multiple stacks or when merging multiple stacks into one.
Usage:
pulumi state move [flags] &amp;lt;urn&amp;gt;...
Flags:
--dest string The name of the stack to move resources to
-h, --help help for move
--include-parents Include all the parents of the moved resources as well
--source string The name of the stack to move resources from
-y, --yes Automatically approve and perform the move
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Both &lt;code&gt;dest&lt;/code&gt; and &lt;code&gt;source&lt;/code&gt; can be either stacks in the current project, or stacks in a different project, using the fully qualified stack names. Note that this works only for stacks within the same backend, it is currently not possible to move a resource between different backends (though you can &lt;a href="https://www.pulumi.com/docs/iac/concepts/state-and-backends/#migrating-between-state-backends"&gt;move stacks between backends&lt;/a&gt; using other existing tools).&lt;/p&gt;
&lt;p&gt;The resources being moved have to be specified by their full &lt;a href="https://www.pulumi.com/docs/concepts/resources/names/#urns"&gt;URN&lt;/a&gt;, and multiple URNs can be passed at once. For each resource being moved, all the children of that resource will also be moved, and the relationships between all resources being moved is preserved. Resources with other types of dependencies will however not be moved to the target stack by default. The easiest way to get the full URN of the resources is to use &lt;code&gt;pulumi stack --show-urns&lt;/code&gt;. Note that URNs can contain characters that get interpreted by the shell, so it is always a good idea to wrap them in single quotes (&lt;code&gt;'&lt;/code&gt;) when passing them as arguments.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;pulumi state move&lt;/code&gt; command won&amp;rsquo;t modify the code of your program directly, it will only modify the state of the source and destination stacks. So following running the command, you should ensure that you modify both programs to match the changes you have made. This can typically be accomplished by copy/pasting source code for the resources and/or components between the two codebases. Inputs and outputs of resources that were moved may need to be adjusted as part of this process. This can be done either by using &lt;a href="https://www.pulumi.com/docs/concepts/stack/#stackreferences"&gt;stack references&lt;/a&gt;, or recreating the inputs in the program.&lt;/p&gt;
&lt;h2 id="example"&gt;Example&lt;/h2&gt;
&lt;p&gt;To demonstrate how the command works in practice, let&amp;rsquo;s go through an example. Let&amp;rsquo;s assume we have the following 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;const&lt;/span&gt; &lt;span class="nx"&gt;randomPet&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;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RandomPet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a-random-pet&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;b&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;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&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;index&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;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index.html&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;bucket&lt;/span&gt;: &lt;span class="kt"&gt;b.bucket&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;content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Thanks for using Pulumi!&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="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;parent&lt;/span&gt;: &lt;span class="kt"&gt;b&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;const&lt;/span&gt; &lt;span class="nx"&gt;randomSite&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;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;random.html&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;bucket&lt;/span&gt;: &lt;span class="kt"&gt;b.bucket&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;content&lt;/span&gt;: &lt;span class="kt"&gt;randomPet.id&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 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;parent&lt;/span&gt;: &lt;span class="kt"&gt;b&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We create a bucket, and two bucket objects, both of them having the bucket as their parent. One of the objects has a random pet name as its content. At some point we decide we&amp;rsquo;d rather group all the AWS resources in a separate program, because this one grew too large. We can do that using the &lt;code&gt;pulumi state move&lt;/code&gt; command, and then adjusting the code.&lt;/p&gt;
&lt;p&gt;First we can find out which stacks already exist and we can move the resources to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi stack ls --all
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME LAST UPDATE RESOURCE COUNT URL
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;v-thomas-pulumi-corp/pulumi-demo-move-aws/dev &lt;span class="m"&gt;23&lt;/span&gt; minutes ago &lt;span class="m"&gt;0&lt;/span&gt; &amp;lt;url&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;v-thomas-pulumi-corp/pulumi-demo-move/dev &lt;span class="m"&gt;6&lt;/span&gt; minutes ago &lt;span class="m"&gt;7&lt;/span&gt; &amp;lt;url&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we can find the URN of the resources using &lt;code&gt;pulumi stack --show-urns&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi stack --show-urns
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Current stack is dev:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Owner: v-thomas-pulumi-corp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Last updated: &lt;span class="m"&gt;13&lt;/span&gt; seconds ago &lt;span class="o"&gt;(&lt;/span&gt;2024-07-23 13:52:17.58884965 +0200 CEST&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Pulumi version used: 3.125.1-dev.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Current stack resources &lt;span class="o"&gt;(&lt;/span&gt;7&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; TYPE NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; pulumi:pulumi:Stack pulumi-demo-move-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ URN: urn:pulumi:dev::pulumi-demo-move::pulumi:pulumi:Stack::pulumi-demo-move-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ random:index/randomPet:RandomPet a-random-pet
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ URN: urn:pulumi:dev::pulumi-demo-move::random:index/randomPet:RandomPet::a-random-pet
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ aws:s3/bucket:Bucket b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ URN: urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket::b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├─ aws:s3/bucketObject:BucketObject random.html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ URN: urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket&lt;span class="nv"&gt;$aws&lt;/span&gt;:s3/bucketObject:BucketObject::random.html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └─ aws:s3/bucketObject:BucketObject index.html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ URN: urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket&lt;span class="nv"&gt;$aws&lt;/span&gt;:s3/bucketObject:BucketObject::index.html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ pulumi:providers:random default_4_16_3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ URN: urn:pulumi:dev::pulumi-demo-move::pulumi:providers:random::default_4_16_3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─ pulumi:providers:aws default_6_45_0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; URN: urn:pulumi:dev::pulumi-demo-move::pulumi:providers:aws::default_6_45_0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we can actually move the resources. Since we&amp;rsquo;re moving the resources from the currently selected stack, we can omit the &lt;code&gt;--source&lt;/code&gt; argument:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi state move --dest v-thomas-pulumi-corp/pulumi-demo-move-aws/dev &lt;span class="s1"&gt;&amp;#39;urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket::b&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Planning to move the following resources from v-thomas-pulumi-corp/pulumi-demo-move/dev to v-thomas-pulumi-corp/pulumi-demo-move-aws/dev:
&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; - urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket::b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket&lt;span class="nv"&gt;$aws&lt;/span&gt;:s3/bucketObject:BucketObject::random.html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket&lt;span class="nv"&gt;$aws&lt;/span&gt;:s3/bucketObject:BucketObject::index.html
&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;The following resources being moved to v-thomas-pulumi-corp/pulumi-demo-move-aws/dev have dependencies on resources in v-thomas-pulumi-corp/pulumi-demo-move/dev:
&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; - urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket&lt;span class="nv"&gt;$aws&lt;/span&gt;:s3/bucketObject:BucketObject::random.html has a dependency on urn:pulumi:dev::pulumi-demo-move::random:index/randomPet:RandomPet::a-random-pet
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - urn:pulumi:dev::pulumi-demo-move::aws:s3/bucket:Bucket&lt;span class="nv"&gt;$aws&lt;/span&gt;:s3/bucketObject:BucketObject::random.html &lt;span class="o"&gt;(&lt;/span&gt;content&lt;span class="o"&gt;)&lt;/span&gt; has a property dependency on urn:pulumi:dev::pulumi-demo-move::random:index/randomPet:RandomPet::a-random-pet
&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;If you go ahead with moving these dependencies, it will be necessary to create the appropriate inputs and outputs in the program &lt;span class="k"&gt;for&lt;/span&gt; the stack the resources are moved to.
&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;Do you want to perform this move? yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Successfully moved resources from v-thomas-pulumi-corp/pulumi-demo-move/dev to v-thomas-pulumi-corp/pulumi-demo-move-aws/dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s a few things worth explaining above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We&amp;rsquo;re moving the resources to a different project, so we need to use the fully qualified stack name. If the resources are being moved between different stacks in the same project, the argument can be only the stack name.&lt;/li&gt;
&lt;li&gt;Before actually moving the resources, the command gives a list of resources that is being moved. Since the bucket objects are children of the bucket, they will be included in the move. However the random pet is not a child resource, so it will not be moved.&lt;/li&gt;
&lt;li&gt;The command also warns us about dependencies that will not be transferred to the destination stack. Since we didn&amp;rsquo;t want to include the pet, but the bucket has a dependency on it, pulumi will warn us about that.&lt;/li&gt;
&lt;li&gt;Before going ahead with the move, we get prompted to confirm whether we want to go ahead with it. In this example we do want to go ahead with the move, so we confirm the move, and the resources will be moved for us.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that the resources are moved, we need to adjust the program to take advantage of that. The source program will now look as follows:&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;const&lt;/span&gt; &lt;span class="nx"&gt;randomPet&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;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RandomPet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a-random-pet&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="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;randomPetName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;randomPet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;Only the random pet remains here. We also export its name, so we can continue using it in the destination program. Meanwhile the destination program is mostly just a copy of the original AWS resources, but there is an important change:&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;const&lt;/span&gt; &lt;span class="nx"&gt;stackRef&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;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`v-thomas-pulumi-com/pulumi-demo-move/dev`&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;b&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;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&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;index&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;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index.html&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;bucket&lt;/span&gt;: &lt;span class="kt"&gt;b.bucket&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;content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Thanks for using Pulumi!&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="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;parent&lt;/span&gt;: &lt;span class="kt"&gt;b&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;const&lt;/span&gt; &lt;span class="nx"&gt;randomSite&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;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;random.html&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;bucket&lt;/span&gt;: &lt;span class="kt"&gt;b.bucket&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;content&lt;/span&gt;: &lt;span class="kt"&gt;stackRef.getOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;randomPetName&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="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;parent&lt;/span&gt;: &lt;span class="kt"&gt;b&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note how we now need to specify the content of &lt;code&gt;random.html&lt;/code&gt;, since we no longer have the random pet in the same program. In this case we use a &lt;a href="https://www.pulumi.com/learn/building-with-pulumi/stack-references/"&gt;stack reference&lt;/a&gt; to reference the output from the source program. It is of course up to the user how to re-create the output. It could also come from config, or be hardcoded depending on the use-case.&lt;/p&gt;
&lt;div class="rounded-lg bg-violet-50 p-6 my-8"&gt;
&lt;p class="heading-4 m-0 mb-3 flex items-center gap-1.5"&gt;Refactor your infrastructure safely&lt;/p&gt;
&lt;div class="body-base m-0 text-gray-950"&gt;Use &lt;code&gt;pulumi state move&lt;/code&gt; to split a growing project into focused stacks without recreating your cloud resources. Organize your infrastructure as code and manage every stack in Pulumi Cloud.&lt;/div&gt;
&lt;a href="https://www.pulumi.com/tutorials/move-resources-between-stacks/" data-track="blog-body-cta" class="btn btn-primary mt-4"&gt;
Learn more
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--regular size-4" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-arrow-right-regular"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;As always, we would love to hear your feedback in the &lt;a href="https://www.pulumi.com/community/"&gt;community slack&lt;/a&gt;. If you encounter any issues with the command, please open an &lt;a href="https://github.com/pulumi/pulumi/issues"&gt;issue&lt;/a&gt;.&lt;/p&gt;</description><author>Thomas Gummerer</author><category>announcements</category><category>cli</category></item><item><title>Continue on Error in the Pulumi CLI</title><link>https://www.pulumi.com/blog/continue-on-error/</link><pubDate>Thu, 09 May 2024 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/continue-on-error/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/continue-on-error/index.png" /&gt;
&lt;p&gt;When managing many resources with Pulumi, a &lt;code&gt;pulumi up&lt;/code&gt; or &lt;code&gt;pulumi destroy&lt;/code&gt; can lead to a complex graph of resource operations being performed as concurrently as possible relative to the dependencies in your program. If one of those operations fails, you may have noticed that Pulumi takes the most conservative approach, letting already in-flight operations complete, but not starting any new operations. Most often, that&amp;rsquo;s what you want &amp;ndash; there might be no point in bringing up more infrastructure if a resource fails.&lt;/p&gt;
&lt;p&gt;However, in some cases it can be useful to keep going to try to bring resources that are independent from the failed one into the requested state, be that resources being created or destroyed. For example, when doing a &lt;code&gt;pulumi destroy&lt;/code&gt;, you may want to have Pulumi destroy as many resources as it can, without stopping when the first error occurs.&lt;/p&gt;
&lt;p&gt;You can now do exactly that with the new &lt;code&gt;--continue-on-error&lt;/code&gt; flag for &lt;code&gt;pulumi up&lt;/code&gt; and &lt;code&gt;pulumi destroy&lt;/code&gt;.&lt;/p&gt;
&lt;div class="my-4"&gt;
&lt;video class="flex outline-none rounded-lg w-full" title="Using pulumi new with the new AI option"
autoplay muted playsinline
loop &gt;
&lt;source src="coe.mp4" /&gt;
&lt;/video&gt;
&lt;/div&gt;
&lt;p&gt;Using &lt;code&gt;--continue-on-error&lt;/code&gt; means that resources that are not in the same dependency tree as the failed resource will still continue to be updated or destroyed, as they would normally. To make sure dependencies are still correctly respected, resources that depend on a successful update or destroy of the failed resource will not continue to be updated. This means that this flag is always safe to use, as Pulumi will continue to manage the failed resources, and they can be updated or destroyed in subsequent runs of Pulumi.&lt;/p&gt;
&lt;p&gt;When the execution finishes, Pulumi will report the resource failures as you would currently expect from a failure, and exit with a non-zero exit code. This indicates that even though we continued to update resources independent from the failed one, there was an error during the deployment.&lt;/p&gt;
&lt;h2 id="what-do-i-need-to-do"&gt;What do I need to do?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pulumi destroy --continue-on-error&lt;/code&gt; was introduced in Pulumi v3.112.0. After upgrading to this version or later of the Pulumi CLI you can go ahead and use this feature.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pulumi up --continue-on-error&lt;/code&gt; was introduced in Pulumi v3.114.0. However, it also requires updated language SDKs to work best. Since we now have resources that fail to create, but the Pulumi program continues to execute, the SDK has to deal with the outputs of these resources. Currently during &lt;code&gt;pulumi up&lt;/code&gt; there are no &amp;ldquo;unknown&amp;rdquo; values expected by the SDK. Since we may now have a failed resource, the outputs for that resource might still be unknown, and the SDK will have to deal with that. This means that to fully support this feature some SDK changes were necessary. So in addition to upgrading the Pulumi CLI to v3.114.0, you will also have to upgrade the Pulumi SDK to v3.114.0 or later for Go, Python and Node.js, and 3.63.0 or later for .NET. For Pulumi YAML programs, you&amp;rsquo;ll need to use v3.115.0 or later of the Pulumi CLI.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;--continue-on-error&lt;/code&gt; is used with older SDK versions, the Pulumi engine will return an error to your program, which then will need to be handled, or the rest of the program might not be executed, and thus some resources may not be updated.&lt;/p&gt;
&lt;p&gt;This feature was a &lt;a href="https://github.com/pulumi/pulumi/issues/3304"&gt;highly requested feature&lt;/a&gt; in the Pulumi open source project, and we&amp;rsquo;re constantly working to address these highly requested features across the open source projects, so jump in and give features you&amp;rsquo;d like to see us add a 👍.&lt;/p&gt;
&lt;p&gt;Give &lt;code&gt;--continue-on-error&lt;/code&gt; a try and &lt;a href="https://slack.pulumi.com"&gt;let us know&lt;/a&gt; what you think!&lt;/p&gt;</description><author>Thomas Gummerer</author><category>error-handling</category><category>announcements</category></item><item><title>Introducing Dev Releases for the Pulumi CLI and SDKs</title><link>https://www.pulumi.com/blog/introducing-dev-releases/</link><pubDate>Tue, 02 Apr 2024 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/introducing-dev-releases/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/introducing-dev-releases/index.png" /&gt;
&lt;p&gt;At Pulumi, the stability of our CLI and SDK releases is critically important. A lot of infrastructure is built and deployed using Pulumi, and any bugs in how that infrastructure is deployed can cause outages. While we put a lot of emphasis on unit and integration testing, we are also constantly investing in new ways to provide additional safeguards to ensure we always deliver stable releases. To that end, we are introducing new dev releases of our CLI and SDKs, which will enable users who want to work with the very latest bits to try them out before they are deployed widely to the entire Pulumi user base. This blog post will walk you through how we are using them internally, and how you can too!&lt;/p&gt;
&lt;h2 id="what-are-dev-releases"&gt;What are dev releases?&lt;/h2&gt;
&lt;p&gt;On every push to the main branch, in other words every time a pull request is merged, we publish a new dev release for both the CLI and the Pulumi core SDKs. For the CLI, we publish the binaries for all supported platforms to S3, while for the SDKs we create pre-release versions in whatever form the various package managers support.&lt;/p&gt;
&lt;p&gt;This means these dev releases always include all the latest features and bug fixes, that will be in the next stable release. And while it was always possible to get the latest CLI or SDKs with some moderate effort depending on the platform and language, having these be proper releases makes for a much more standardized experience on how to install them.&lt;/p&gt;
&lt;p&gt;Dev releases of the CLI can be &lt;a href="https://www.pulumi.com/docs/install/#installing-dev-releases"&gt;installed&lt;/a&gt; on Linux, macOS, and Windows. For example, on Linux or macOS:&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;$ curl -fsSL https://get.pulumi.com &lt;span class="p"&gt;|&lt;/span&gt; sh -s -- --version dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dev SDKs are currently available for &lt;a href="https://www.pulumi.com/docs/languages-sdks/javascript/#dev-versions"&gt;Node.js&lt;/a&gt;, &lt;a href="https://www.pulumi.com/docs/languages-sdks/python/#dev-versions"&gt;Python&lt;/a&gt;, &lt;a href="https://www.pulumi.com/docs/languages-sdks/dotnet/#dev-versions"&gt;.NET&lt;/a&gt;, and &lt;a href="https://www.pulumi.com/docs/languages-sdks/go/#dev-versions"&gt;Go&lt;/a&gt;. For example, installing the dev release of the Node.js SDK with &lt;code&gt;npm&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;$ npm add @pulumi/pulumi@dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="how-are-we-using-them-internally-at-pulumi"&gt;How are we using them internally at Pulumi?&lt;/h2&gt;
&lt;p&gt;At Pulumi we are not only developers of our product, but we also love using it internally. All deploys for Pulumi Cloud are orchestrated by Pulumi. In addition we use Pulumi for managing other things such as this blog, and even access management to services we are using is partly done with Pulumi. All of these are a great way for us to test our software in real world use cases, before releasing it.&lt;/p&gt;
&lt;p&gt;In addition to that, we also use the dev releases for testing both our &lt;a href="https://github.com/pulumi/templates/"&gt;templates&lt;/a&gt; and &lt;a href="https://github.com/pulumi/examples/"&gt;examples&lt;/a&gt;. This is a huge set of Pulumi programs, that can help figure out problems early.&lt;/p&gt;
&lt;h2 id="how-you-can-leverage-dev-releases"&gt;How you can leverage dev releases&lt;/h2&gt;
&lt;p&gt;In addition to making it easier for us to test new code that gets merged, and making sure we can release the most stable software possible, we also wanted to make it easier for users to use these dev releases. There&amp;rsquo;s a couple reasons why these could help your organization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A bug fix has been merged, but not yet released? You can get it a few minutes after it is merged via the dev release channel&lt;/li&gt;
&lt;li&gt;You want to make sure your production stacks work well with the latest version of Pulumi? Use dev releases with your dev stacks for some early testing. We&amp;rsquo;d be delighted with any bugs that are reported before they make it into a release.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-to-get-started"&gt;How to get started&lt;/h2&gt;
&lt;p&gt;For instructions on how to use the CLI dev releases see the &lt;a href="https://www.pulumi.com/docs/install/#installing-dev-releases"&gt;Install Docs&lt;/a&gt;, and for pre-release versions of dev SDKs see the docs for &lt;a href="https://www.pulumi.com/docs/languages-sdks/javascript/#dev-versions"&gt;Node.js&lt;/a&gt;, &lt;a href="https://www.pulumi.com/docs/languages-sdks/python/#dev-versions"&gt;Python&lt;/a&gt;, &lt;a href="https://www.pulumi.com/docs/languages-sdks/dotnet/#dev-versions"&gt;.NET&lt;/a&gt;, and &lt;a href="https://www.pulumi.com/docs/languages-sdks/go/#dev-versions"&gt;Go&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy building!&lt;/p&gt;</description><author>Thomas Gummerer</author><category>cli</category><category>sdk</category><category>pre-release</category></item></channel></rss>