<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Community</title><link>https://www.pulumi.com/blog/tag/community/</link><description>Pulumi blog posts: Community.</description><language>en-us</language><pubDate>Thu, 19 Jun 2025 14:46:07 +0300</pubDate><item><title>Adding PostgreSQL State Backend Support to Pulumi: A Community Contribution Journey</title><link>https://www.pulumi.com/blog/postgres-diy-backend/</link><pubDate>Thu, 19 Jun 2025 14:46:07 +0300</pubDate><guid>https://www.pulumi.com/blog/postgres-diy-backend/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/postgres-diy-backend/index.png" /&gt;
&lt;p&gt;When managing infrastructure as code at scale, reliable state storage is essential. Pulumi Cloud provides a fully managed, secure, and scalable solution out of the box. For teams that choose to build and maintain their own backend, Pulumi now offers support for PostgreSQL as a DIY state storage option—though this requires additional operational overhead and careful consideration around performance, security, and maintenance.&lt;/p&gt;
&lt;h2 id="the-need-for-database-backed-state-storage"&gt;The Need for Database-Backed State Storage&lt;/h2&gt;
&lt;p&gt;Traditional DIY backends in Pulumi have relied on object storage systems like AWS S3, Google Cloud Storage, or Azure Blob Storage. While these work well for many use cases, they have limitations when it comes to handling very large state files, complex locking mechanisms, and transactional guarantees that some enterprise environments require.&lt;/p&gt;
&lt;p&gt;PostgreSQL stood out as an excellent candidate for state storage due to its:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Large object support&lt;/strong&gt;: Ability to handle substantial state files without size constraints&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ACID compliance&lt;/strong&gt;: Robust transactional guarantees for state consistency&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mature ecosystem&lt;/strong&gt;: Well-established tooling and operational practices&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability options&lt;/strong&gt;: From single instances to complex replication setups&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security features&lt;/strong&gt;: Comprehensive authentication and authorization capabilities&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-community-contribution-process"&gt;The Community Contribution Process&lt;/h2&gt;
&lt;p&gt;This feature came to life through &lt;a href="https://github.com/pulumi/pulumi/pull/19581/files"&gt;PR #19581&lt;/a&gt;, which addressed a long-standing community request tracked in &lt;a href="https://github.com/pulumi/pulumi/issues/5632"&gt;issue #5632&lt;/a&gt;. The development process showcased the collaborative nature of open-source development, with multiple rounds of feedback, testing, and refinement.&lt;/p&gt;
&lt;h3 id="key-technical-challenges"&gt;Key Technical Challenges&lt;/h3&gt;
&lt;p&gt;The implementation required several technical considerations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Integration with Go Cloud Development Kit (CDK)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The PostgreSQL backend needed to integrate seamlessly with Pulumi&amp;rsquo;s existing blob storage abstraction layer. This was achieved by implementing the &lt;code&gt;blob.BucketURLOpener&lt;/code&gt; interface:&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="c1"&gt;// URLHandler is a URL opener for PostgreSQL URLs.&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="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;URLHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// OpenBucketURL implements blob.BucketURLOpener.&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="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;URLHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;OpenBucketURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;)&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;blob&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="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&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;pg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;NewPostgresBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;u&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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;&lt;strong&gt;2. Database Schema Design&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The implementation uses a simple but effective schema for storing state data:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pulumi_state&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="k"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&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="k"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&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="n"&gt;created_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TIME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ZONE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NOW&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="n"&gt;updated_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TIME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ZONE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NOW&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="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;This design allows for efficient key-based lookups while leveraging PostgreSQL&amp;rsquo;s native JSON support for flexible state data storage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Cross-Platform Testing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One of the significant challenges was ensuring the implementation worked across different operating systems and CI environments. The solution involved using &lt;a href="https://golang.testcontainers.org/modules/postgres/"&gt;testcontainers-go&lt;/a&gt; to spin up PostgreSQL instances during testing, with appropriate skip conditions for environments where Docker isn&amp;rsquo;t available:&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;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;skipIfDockerNotAvailable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;windows&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;darwin&amp;#34;&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Skipping test: Docker not available on this platform in CI&amp;#34;&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="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="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Additional Docker availability checks...&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;h2 id="performance-and-limitations"&gt;Performance and Limitations&lt;/h2&gt;
&lt;h3 id="postgresql-wins-over-a-plain-s3-bucket"&gt;PostgreSQL wins over a plain S3 bucket&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ACID transactions with row-level locking –&lt;/strong&gt; A postgres backend means strong consistency guarantees.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faster for smaller state files –&lt;/strong&gt; Community benchmarks show improvements on S3 backend speed for smaller state files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Point-in-time restore –&lt;/strong&gt; WAL-based backups allow for easier rollback.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deep observability –&lt;/strong&gt; plug pg-metrics into Grafana/APM for real-time insight.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="gaps-youll-still-close-only-with-pulumi-cloud"&gt;Gaps you&amp;rsquo;ll still close only with Pulumi Cloud&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Server-side policy guardrails (CrossGuard) –&lt;/strong&gt; automatically enforced policies on every preview or deploy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always-on drift detection –&lt;/strong&gt; automatic scans catch out-of-band changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managed Deployments –&lt;/strong&gt; fully managed runners, PR previews, and dependent stack updates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Org-wide RBAC, SSO/SAML, immutable audit logs&lt;/strong&gt; – turnkey for teams and auditors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search &amp;amp; analytics across &lt;em&gt;all&lt;/em&gt; cloud resources (Insights)&lt;/strong&gt; – not just those managed by Pulumi.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enterprise Secrets &amp;amp; Configuration (ESC)&lt;/strong&gt; - encrypted secrets and config with RBAC, versioning and audit trail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SOC 2, high availability, and painless upgrades&lt;/strong&gt; – Pulumi operates the control plane so you don&amp;rsquo;t have to.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Using the PostgreSQL backend is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Login to your PostgreSQL backend:&lt;/strong&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 login postgres://pulumi_user:secure_password@localhost:5432/pulumi_state?sslmode&lt;span class="o"&gt;=&lt;/span&gt;require
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Pulumi normally:&lt;/strong&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 up
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Your state is now stored in PostgreSQL!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="community-impact"&gt;Community Impact&lt;/h2&gt;
&lt;p&gt;This contribution demonstrates the power of community-driven development in open source projects. It addresses a real need expressed by Pulumi users while maintaining the high quality standards expected from the project.&lt;/p&gt;
&lt;p&gt;The implementation follows Pulumi&amp;rsquo;s architectural patterns and coding standards, making it a seamless addition to the existing codebase. The comprehensive documentation and testing ensure that future maintainers can easily understand and modify the code.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s Next?&lt;/h2&gt;
&lt;p&gt;The PostgreSQL backend opens up several possibilities for future enhancements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Availability&lt;/strong&gt;: Support for PostgreSQL clustering and replication&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance Optimizations&lt;/strong&gt;: Caching strategies and connection pooling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced Features&lt;/strong&gt;: Custom backup strategies and state analytics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-tenant Support&lt;/strong&gt;: Isolation patterns for multiple teams or environments&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The addition of PostgreSQL state backend support to Pulumi represents more than just a new feature—it&amp;rsquo;s a testament to the collaborative nature of open source development and the power of community contributions. By providing enterprise-grade state storage options while maintaining the simplicity that makes Pulumi great, this feature enables organizations to adopt infrastructure as code with confidence.&lt;/p&gt;
&lt;p&gt;Whether you&amp;rsquo;re running a small startup or managing infrastructure for a large enterprise, having robust, reliable state storage options is crucial. The PostgreSQL backend provides an improved approach to Do-It-Yourself state management.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in contributing to Pulumi or have ideas for new features, the community welcomes your contributions. This PR serves as an excellent example of how community members can make meaningful improvements to the project while learning from experienced maintainers and following established best practices.&lt;/p&gt;</description><author>Matan Baruch</author><category>features</category><category>diy-backend</category><category>postgresql</category><category>state-management</category><category>community</category></item><item><title>Introducing the Pulumi Puluminaries 2.0 Program</title><link>https://www.pulumi.com/blog/puluminaries-program/</link><pubDate>Mon, 10 Feb 2025 12:15:00 -0800</pubDate><guid>https://www.pulumi.com/blog/puluminaries-program/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/puluminaries-program/index.png" /&gt;
&lt;p&gt;We are excited to announce the Pulumi Puluminaries 2.0 Program. This is a fresh and revitalized way to celebrate and support Pulumi’s most passionate community members. Pulumi Puluminaries are individuals who demonstrate leadership in the Pulumi ecosystem by sharing best practices, creating valuable content, and helping fellow practitioners succeed.&lt;/p&gt;
&lt;p&gt;Before we dive into what is new, we want to recognize and applaud the incredible achievements of our existing Pulumi Puluminaries. You can check out the great folks currently making a difference in our community on the &lt;a href="https://www.pulumi.com/community/puluminaries/"&gt;Pulumi Puluminaries page&lt;/a&gt;. Their hard work and dedication have laid a strong foundation for what is next.&lt;/p&gt;
&lt;h4 id="what-is-the-pulumi-puluminaries-20-program"&gt;What is the Pulumi Puluminaries 2.0 Program?&lt;/h4&gt;
&lt;p&gt;The Pulumi Puluminaries 2.0 Program is designed to recognize and empower developers, architects, and technologists who are committed to driving innovation with Pulumi. This program offers exclusive opportunities to collaborate with Pulumi engineers, gain early access to new features, and share insights with the community.&lt;/p&gt;
&lt;p&gt;Similar to how other ambassador programs bring together dedicated advocates around a specific focus, the Pulumi Puluminaries 2.0 Program unites a community of individuals who champion Pulumi’s approach to cloud engineering. Pulumi Puluminaries lead conversations, help new users adopt Pulumi effectively, and showcase best practices and use cases that highlight the power of Infrastructure as Code. They are also key participants in shaping Pulumi’s future by providing feedback to the product team and taking part in special initiatives.&lt;/p&gt;
&lt;h4 id="what-does-it-mean-to-be-a-pulumi-puluminary"&gt;What does it mean to be a Pulumi Puluminary?&lt;/h4&gt;
&lt;p&gt;Pulumi Puluminaries serve as champions of cloud engineering. They help users navigate the complexities of building, deploying, and managing applications with Pulumi. They also promote the benefits of Infrastructure as Code in modern organizations by creating thought leadership content, hosting or co-hosting events, and offering guidance for those just getting started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Responsibilities for Pulumi Puluminaries:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As a Pulumi Puluminary, you will be expected to engage with the community at least once a quarter. Here are some ways you can contribute:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Community Engagement&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Actively share Pulumi-related articles, tutorials, and project stories.&lt;/li&gt;
&lt;li&gt;Present at conferences, meetups, or webinars on topics that highlight Pulumi’s value.&lt;/li&gt;
&lt;li&gt;Mentor new users and help them get started with Pulumi.&lt;/li&gt;
&lt;li&gt;Participate in Pulumi’s community forums, Slack channels, and social media discussions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Thought Leadership&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write blog posts (like &lt;a href="https://www.pulumi.com/blog/author/simen-a-w-olsen/"&gt;Simen A. W. Olsen&lt;/a&gt; did in his latest post &lt;a href="https://www.pulumi.com/blog/your-perfect-infrastructure/"&gt;Your Perfect Infrastructure May Not Be So Perfect&lt;/a&gt; or &lt;a href="https://www.pulumi.com/blog/author/tyler-mulligan/"&gt;Tyler Mulligan&lt;/a&gt;) or create &lt;a href="https://www.youtube.com/playlist?list=PLyy8Vx2ZoWlqxDJjRRhgLGu1_Oct0VVhN"&gt;videos showcasing&lt;/a&gt; innovative ways to use Pulumi.&lt;/li&gt;
&lt;li&gt;Contribute to discussions around best practices and patterns for cloud engineering with Pulumi.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Product Feedback&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Offer beta-testing support and provide product feedback directly to the Pulumi team.&lt;/li&gt;
&lt;li&gt;Participate in early-access programs and help guide product roadmap decisions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="benefits-of-joining-the-program"&gt;Benefits of Joining the Program&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Early Access&lt;/strong&gt;: Get sneak peeks at new Pulumi features and releases before they are publicly announced.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Direct Collaboration&lt;/strong&gt;: Collaborate one-on-one with Pulumi engineers and product managers, influencing future capabilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recognition and Swag&lt;/strong&gt;: Receive official Pulumi Puluminaries 2.0 digital badges, exclusive swag, and spotlight opportunities on Pulumi’s blog and at events.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Networking and Growth&lt;/strong&gt;: Build relationships with peers in the Pulumi ecosystem, deepen your technical expertise, and grow your professional network.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="how-to-get-involved"&gt;How to Get Involved&lt;/h4&gt;
&lt;p&gt;We are looking for Pulumi enthusiasts who have shown dedication and passion for empowering others in the Pulumi community. Whether you have built cutting-edge projects, delivered insightful conference talks, or helped users overcome technical hurdles, we would love to hear your story.&lt;/p&gt;
&lt;p&gt;Whether you want to apply for yourself or nominate someone else, we’d love to hear from you!&lt;/p&gt;
&lt;a
href="https://docs.google.com/forms/d/e/1FAIpQLScdi58QR5SYDYoXhceFumlrjsVxHOOCgCICNw_t3V3qSK3K6w/viewform"
class="btn btn-primary"
&gt;
Apply or Nominate Now
&lt;/a&gt;</description><author>Engin Diri</author><category>puluminaries</category><category>community</category><category>program</category></item><item><title>2024 Year in Review: Growth, Innovation and Appreciation</title><link>https://www.pulumi.com/blog/pulumi-year-in-review/</link><pubDate>Fri, 20 Dec 2024 06:11:09 +0000</pubDate><guid>https://www.pulumi.com/blog/pulumi-year-in-review/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/pulumi-year-in-review/index.png" /&gt;
&lt;div class="note note-info"&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.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;Note: This post discusses Pulumi Copilot, which Pulumi Neo has replaced. &lt;a href="https://www.pulumi.com/docs/ai/"&gt;Learn about Neo →&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;rsquo;s the end of 2024, and like everyone, we&amp;rsquo;re counting down until 2025 while looking back at our year. We&amp;rsquo;ve had a very exciting year, from unveiling a bold new vision for Pulumi to delivering cutting-edge updates across our Pulumi Cloud.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a look at how Pulumi has grown, evolved, and continued empowering teams worldwide.&lt;/p&gt;
&lt;h2 id="a-unified-vision-the-pulumi-platform"&gt;A Unified Vision: The Pulumi Platform&lt;/h2&gt;
&lt;p&gt;&lt;img src="pulumi-platform-vision.png" alt="Placeholder Image"&gt;&lt;/p&gt;
&lt;p&gt;This year, Pulumi unveiled a bold new vision for Pulumi—&lt;a href="https://www.pulumi.com/blog/pulumi-up-2024/"&gt;a comprehensive product suite&lt;/a&gt; that extends far beyond infrastructure as code. The Pulumi platform now consists of three core products:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/docs/iac/"&gt;Pulumi IaC&lt;/a&gt;&lt;/strong&gt;: Open source infrastructure as code in any programming language. With over 100M downloads, 167% growth in contributions, and industry-leading innovations, Pulumi IaC continues to redefine cloud automation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/docs/esc/"&gt;Pulumi ESC&lt;/a&gt;&lt;/strong&gt;: Centralized secrets management and orchestration that scales. General availability this year brought features like SDKs, versioning, tagging, and integrations with 1Password and Kubernetes secrets operators.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/docs/pulumi-cloud/insights/"&gt;Pulumi Insights&lt;/a&gt;&lt;/strong&gt;: Visibility, intelligence, and control over all infrastructure resources. Insights 2.0 expanded support for resources created outside Pulumi IaC, delivering advanced graph visualizations and policy enforcement powered by Pulumi CrossGuard.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/AepHQaXeNX0?rel=0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Three core capabilities unite these products:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/docs/pulumi-cloud/copilot/"&gt;Pulumi Copilot&lt;/a&gt;&lt;/strong&gt;: Generative AI is used to manage cloud resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/docs/iac/using-pulumi/crossguard/"&gt;Pulumi CrossGuard&lt;/a&gt;&lt;/strong&gt;: A policy-as-code engine for compliance and best practices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/docs/pulumi-cloud/deployments/"&gt;Pulumi Deployments&lt;/a&gt;&lt;/strong&gt;: Infrastructure task orchestration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Over 3,000 customers, including Nvidia, BMW, Unity Games, and Tivity Health, leveraged these solutions to enhance velocity, save costs, and secure their infrastructure.&lt;/p&gt;
&lt;h2 id="highlights-of-releases-and-enhancements"&gt;Highlights of Releases and Enhancements&lt;/h2&gt;
&lt;p&gt;&lt;img src="any-language-any-cloud.png" alt="Placeholder Image"&gt;&lt;/p&gt;
&lt;p&gt;Pulumi&amp;rsquo;s product innovations this year have empowered teams to automate, secure, and manage cloud infrastructure with confidence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/pulumi-esc-ga/"&gt;Pulumi ESC General Availability&lt;/a&gt;&lt;/strong&gt;, redefining secrets management with features like dynamic credentials and hierarchical environments. New capabilities include secrets syncing with external stores like AWS Secrets Manager and Kubernetes operators for runtime integration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/pulumi-insights-2/"&gt;Pulumi Insights 2.0&lt;/a&gt;&lt;/strong&gt; delivers AI-powered search, a comprehensive inventory of resources created by Pulumi IaC as well as other tools like Terraform and CloudFormation, and automated remediation capabilities powered by CrossGuard.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/product/copilot/"&gt;Pulumi Copilot&lt;/a&gt;&lt;/strong&gt; leverages Generative AI to transform how teams diagnose and resolve IaC issues, providing instant feedback and deployment recommendations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/pulumi-kubernetes-operator-2-0/"&gt;Pulumi Kubernetes Operator 2.0&lt;/a&gt;&lt;/strong&gt; introduced scalable, secure deployments with dedicated workspace pods, enhanced customization options, and improved stability under dynamic conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/drift-detection/"&gt;Drift Detection and Remediation&lt;/a&gt;&lt;/strong&gt; in Pulumi Cloud now automates drift detection for all 180+ supported providers, ensuring infrastructure consistency, enhanced security, and reduced operational risks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/azure-v6-release/"&gt;Azure Classic Provider v6.0.0&lt;/a&gt;&lt;/strong&gt; updates included the latest upstream changes and ensured compatibility with Pulumi Azure Native Provider.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/gcp-v8-release/"&gt;Google Cloud Provider v8.0.0&lt;/a&gt;&lt;/strong&gt; provides full resource coverage for the latest Google Cloud updates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/aws-cdk-on-pulumi-1.0/"&gt;AWS CDK 1.0&lt;/a&gt;&lt;/strong&gt; expands compatibility with AWS CDK features, bridging the CDK and Pulumi ecosystems and enabling integration with constructs from AWS&amp;rsquo;s construct hub.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/pulumi-vscode-extension/"&gt;Pulumi Visual Studio Code Extension&lt;/a&gt;&lt;/strong&gt; empowers developers with tools for debugging Pulumi programs, YAML language support, and direct ESC management within the IDE.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/docker-containers/"&gt;Pulumi Docker Containers&lt;/a&gt;&lt;/strong&gt; enhances CI/CD workflows with versioned images, pre-installed tools like Poetry and ppm, and runtime flexibility for Python, Node.js, and .NET.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="by-the-numbers"&gt;By the Numbers&lt;/h2&gt;
&lt;p&gt;&lt;img src="3000-customers.png" alt="Placeholder Image"&gt;&lt;/p&gt;
&lt;p&gt;Pulumi&amp;rsquo;s growth and impact this year have been amazing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;75,000+ Pull Requests&lt;/strong&gt;: Driven by 5,600+ contributors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3,000+ Customers&lt;/strong&gt;: Empowering industries like AI, gaming, finance, and healthcare.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;100M+ Downloads&lt;/strong&gt;: Pulumi IaC adoption soared globally.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;167% Contribution Growth&lt;/strong&gt;: Outpacing other IaC tools like Terraform and OpenTofu.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;71% Cloud Cost Savings&lt;/strong&gt;: Customers like &lt;a href="https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/"&gt;Tivity Health saved millions&lt;/a&gt; with Pulumi.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="community-achievements"&gt;Community Achievements&lt;/h2&gt;
&lt;p&gt;&lt;img src="community-highlights.png" alt="Placeholder Image"&gt;&lt;/p&gt;
&lt;p&gt;Pulumi&amp;rsquo;s vibrant community has been at the heart of everything we&amp;rsquo;ve accomplished:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Global &lt;a href="https://luma.com/pulumi"&gt;Pulumi User Groups&lt;/a&gt; (PUGs)&lt;/strong&gt;: With 20 groups across 9 countries, including new meetups in &lt;a href="https://luma.com/pulumi"&gt;Chicago&lt;/a&gt;, &lt;a href="https://luma.com/pulumi"&gt;Columbus&lt;/a&gt;, &lt;a href="https://luma.com/pulumi"&gt;São Paulo&lt;/a&gt;, and &lt;a href="https://luma.com/pulumi"&gt;Tel Aviv&lt;/a&gt;, our community continues to grow. These meetups collectively hosted over 6,396 members.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PulumiUP 2024&lt;/strong&gt;: Over 8,000 registrants globally, with nearly 1,000 for the first-ever PulumiUP Europe.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/resources/#upcoming"&gt;Workshops&lt;/a&gt;&lt;/strong&gt;: 72 workshops with 14,500+ registrations from engineers wanting to learn Pulumi.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;22,000 GitHub Stars&lt;/strong&gt;: A testament to our community&amp;rsquo;s passion and dedication.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="looking-ahead-to-2025"&gt;Looking Ahead to 2025&lt;/h2&gt;
&lt;p&gt;To our users, customers, partners, Puluminaries, and employees, &amp;ldquo;thank you!&amp;rdquo; Your support has made 2024 an amazing year, and we&amp;rsquo;re excited for what&amp;rsquo;s ahead in 2025. Together, we&amp;rsquo;ll continue to build the future of cloud infrastructure and security.&lt;/p&gt;
&lt;p&gt;Stay tuned for more innovations, community events, and collaboration opportunities. Here&amp;rsquo;s to another year of growth, transformation, and success!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/docs/"&gt;Explore Pulumi ➡️&lt;/a&gt;&lt;/p&gt;</description><author>Sara Huddleston</author><category>community</category><category>features</category></item><item><title>PulumiUP 2024: Dive Into the Future of Cloud, Platform Engineering, and AI/ML</title><link>https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/</link><pubDate>Wed, 11 Sep 2024 07:04:40 +0000</pubDate><guid>https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/index.png" /&gt;
&lt;p&gt;PulumiUP 2024 is just around the corner! It will be held on September 18th, starting at 8 AM PT | 15:00 UTC +0, and with over 5,500 engineers from all over the world already registered, this is shaping up to be the must-attend event for cloud professionals, platform engineers, and AI/ML enthusiasts alike. From entry-level engineers to tech executives, this event brings together professionals from companies of all sizes to explore the latest innovations and best practices in &lt;strong&gt;Cloud and IaC&lt;/strong&gt;, &lt;strong&gt;Platform Engineering &amp;amp; DevOps&lt;/strong&gt;, and &lt;strong&gt;AI/ML&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you haven’t registered yet, now’s the time! &lt;a href="https://conference.pulumi.com/schedule/?utm_source=PulumiUP&amp;amp;utm_medium=web&amp;amp;utm_campaign=FY2025Q1_Event_PulumiUP"&gt;Start building your schedule today&lt;/a&gt;, select the talks you want to watch live and on-demand and add them to your schedule.&lt;/p&gt;
&lt;h2 id="pulumiup-at-glance"&gt;PulumiUP at Glance&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/#main-track-keynote-cloud-culture-talks-and-more"&gt;Main Track: Keynote, Cloud Culture Talks, and More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/#aiml-track-highlights"&gt;AI/ML Track Highlights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/#platform-engineering-and-devops-track-highlights"&gt;Platform Engineering and DevOps Track Highlights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/#cloud-and-iac-track-highlights"&gt;Cloud and IaC Track Highlights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/#workshops-and-hands-on-learning"&gt;Workshops and Hands-On Learning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/#amazing-partners-and-sponsors"&gt;Amazing Partners and Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/#build-your-schedule-now"&gt;Build Your Schedule Now&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="main-track-keynote-cloud-culture-talks-and-more"&gt;Main Track: Keynote, Cloud Culture Talks, and More&lt;/h2&gt;
&lt;p&gt;In the Main Track, you’ll hear directly from the Pulumi leadership team about exciting product innovations. Don’t miss the Cloud Culture talks focused on the human side of technology and collaboration.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/events/pulumiup-2024-keynote/"&gt;Keynote and New Product Announcements&lt;/a&gt;&lt;/strong&gt; - Joe Duffy (Co-founder and CEO), Luke Hoban (CTO), and other key Pulumi leaders will unveil new products and innovations shaping the next wave of cloud development. Expect to see live demos showcasing how these tools can revolutionize your workflows, from cloud infrastructure to security automation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/9Xf6DJMvQ08"&gt;Why Care About Building a Cloud Center of Excellence?&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Alex Radu (VP Product &amp;amp; Marketing Manager, Public Cloud, J.P. Morgan) discusses why building a Cloud Center of Excellence is essential for long-term cloud strategy success.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/30wTCwfj3cc"&gt;What Is &amp;lsquo;HugOps&amp;rsquo; and Why Is It Important?&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Rees Pozzi (Senior Platform Engineer, Kainos) explores the meaning and importance of &amp;ldquo;HugOps,&amp;rdquo; a practice promoting empathy and collaboration in the tech world but also by all those impacted by it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="aiml-track-highlights"&gt;AI/ML Track Highlights&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://conference.pulumi.com/talks/panel-ai-cloud-development/"&gt;&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/pulumiup_2024-ai-cloud-development.png"
alt="Panelists of AI for Cloud Development" width="100%"&gt;&lt;figcaption&gt;
&lt;p&gt;Panel: AI for Cloud Development&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AI is transforming how we approach development and infrastructure, and PulumiUP offers an incredible lineup of talks and panels on the topic.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/4IWGNoNVbiM"&gt;Data Privacy Challenges with Large Language Models&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Aditi Godbole (Data Science, AI &amp;amp; ML Leader, SAP) will address the critical question of managing data privacy, addressing LLM basics, AI data privacy fundamentals, and specific privacy issues in LLMs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/CoZM9BCJcJ4"&gt;Pulumi Powered AI/ML on Kubernetes&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Jason Smith (App Modernization Specialist, Google Cloud) will show you how to serve an open-source LLM with a RAG on Kubernetes and set up a Kubernetes environment for AI/ML workloads with Pulumi.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/WoM8Bj76_Fw"&gt;AI Tools for Developers&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Jim Clark (Principal Software Engineer, Docker) will explore how Tools can enhance the capabilities of AI assistants and show how the combination of AI, Tools, and containerized runtimes, can enhance interactions between developers and their tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/2mlb2jEBkoo"&gt;The AI Governance Challenge&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Patty O&amp;rsquo;Callaghan (Technical Director, Charles River) will explore the complexities of AI governance and how to navigate them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/events/panel-ai-in-cloud-development/"&gt;Panel Discussion: &amp;ldquo;AI for Cloud Development&amp;rdquo;&lt;/a&gt;&lt;/strong&gt; - Luke Hoban (CTO, Pulumi), Phillip Carter (Principal Product Manager, Honeycomb), Giri Sreenivas (CPO, Docker), Clare Liguori (Senior Principal Software Engineer, AWS) and Meagan Cojocar (Principal Product Manager, Pulumi) will provide diverse perspectives on how AI is shaping cloud infrastructure.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="platform-engineering-and-devops-track-highlights"&gt;Platform Engineering and DevOps Track Highlights&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/pulumiup_2024-secrets-policies-automating-cybersecurity.png"
alt="Panelists of Secrets and Policies—Automating Cybersecurity" width="100%"&gt;&lt;figcaption&gt;
&lt;p&gt;Panel: Secrets and Policies—Automating Cybersecurity&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Platform Engineering &amp;amp; DevOps track at PulumiUP is perfect for anyone looking to automate infrastructure, secure their systems, and scale their environments with ease. This track features talks and panels on cutting-edge technologies and practices.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/WZD1z2ldweY"&gt;Moving Mountains: How IaC Unlocks Massive Refactoring Possibilities&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Shaun Verch (Infrastructure Engineer, Oso) will show you how infrastructure as code can transform large-scale refactoring projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/1Q3XPmenthg"&gt;Using Pulumi to Empower Kubernetes Fleet Management&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Blake Romano (Senior Software Engineer, Imagine Learning) will show you how Imagine Learning leverages Pulumi to manage a fleet of Kubernetes clusters and other platform resources quickly and reliably, and how it revolutionized their workflow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/f2r9rS9U2CA"&gt;Streamline Your Infrastructure Deployment with GitOps and Pulumi Operator&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Sam Cogan (Solutions Architect &amp;amp; Azure MVP, WTW) will highlight how you can define your infrastructure in Git, and have Pulumi automatically reconcile it into deployed infrastructure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/EQwpC02CQ9k"&gt;Is This a Platform? Platform Engineering Before PMF&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Jk Jensen (Team Lead, Staff Software Engineer, MystenLabs) will share how they built their service platform around Pulumi to help engineers and researchers move from prototypes to deployed environments, enabling their teams to iterate more quickly and develop an increasing number of systems without additional complexity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://www.pulumi.com/events/security-automation-faster-cheaper-better/"&gt;Security Automation—Faster. Cheaper. Better.&lt;/a&gt;&amp;quot;&lt;/strong&gt; - David Giambruno (VP of Engineering and DevOps, Tivity Health) and Joe Duffy (Founder/CEO, Pulumi) will discuss security automation and how these efforts impacted speed and agility, cost optimization and improved security and compliance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/CGE8VXgkjug"&gt;Meet Devs Where They Are: Why IaC Must Be Real Code&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Jeremy Adams (Head of Ecosystems, Dagger) will present why real code (that teams already use for applications) is essential for both IaC and CI/CD pipelines, and to meet modern DevOps best practices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/2a-wlUnBY1g"&gt;Security of IaC Pipelines and Infrastructure Governance With Policies-As-Code&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Marina Novikova (Senior Partner Solutions Architect, AWS) and Andy Taylor (Senior Networking Specialist/ Solutions Architect, AWS) will explore security best practices for IaC pipelines and infrastructure governance using policies-as-code, cover best practices for handling sensitive information like access keys, tokens and encryption keys, and scaling and automating your DevSecOps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/events/secrets-policies-automating-cybersecurity/"&gt;Panel Discussion: &amp;ldquo;Secrets and Policies—Automating Cybersecurity&amp;rdquo;&lt;/a&gt;&lt;/strong&gt; - Arun Loganathan (Senior Product Manager, Pulumi), Maya Kaczorowski (PM of Security), Jason Meller (VP, Product, 1Password), and Ofir Cohen (CTO, Container Security, Wiz) will explore the intersection of innovation and security, offering insights into how AI is reshaping the threat landscape and how organizations can stay ahead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="cloud-and-iac-track-highlights"&gt;Cloud and IaC Track Highlights&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/pulumiup-global-cloud-iac-platform-engineering-ai-conference/pulumiup_2024-infrastructure-as-code.png"
alt="Panelists of Infrastructure as Code - Can we do better?" width="100%"&gt;&lt;figcaption&gt;
&lt;p&gt;Panel: Infrastructure as Code - Can we do better?&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Cloud and IaC track is ideal for those wanting to deepen their understanding of infrastructure as code and its impact on modern cloud computing. Learn from industry pioneers and see where the future of IaC is headed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://www.pulumi.com/blog/from-cdk-pulumi-evolution-of-sst/"&gt;From CDK to Pulumi: Evolution of SST&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Dax Raad (Founding Engineer, SST) and Jay V (Founder, SST) will share insights into the early development of their product, initially built on AWS Cloud Development Kit (CDK), the challenges they encountered and the groundbreaking decision to transition to Pulumi.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/"&gt;Unified Software Development at BMW Software Factory with Pulumi&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Jan-Peter Alten (Expert Software Engineer, DevOps, BMW Group) will showcase how BMW uses Pulumi to unify their software development processes, resulting in an increase in developer productivity, streamlined infrastructure management, and enhanced scalability, security, and compliance across their complex software ecosystem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/E077QbXPOZE"&gt;Stacking Accounts: Multi-Account Deployments in a Single Stack for Monitoring, Management, and More&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Denis Willett (Software Engineer, North Carolina Institute for Climate Studies) will explain how they use Pulumi for managing 60+PB of environmental data across AWS, GCP, and Azure and leverage multi-account deployments in a single stack via the Pulumi Automation API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://youtu.be/C5ZuEVXesOk"&gt;Industrialize the Configuration of Your GitHub Repositories With IaC&lt;/a&gt;&amp;quot;&lt;/strong&gt; - Alexandre Nédélec (Software Engineer, Avanade) will live code using the GitHub, Azure native, and Azure AD Pulumi providers to create a GitHub repository and configure its pipeline to deploy to Azure using OpenID Connect.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/events/panel-infrastructure-as-code-can-we-do-better/"&gt;Panel Discussion: &amp;ldquo;Infrastructure as Code — Can We Do Better?&amp;rdquo;&lt;/a&gt;&lt;/strong&gt; - Joe Duffy (Co-Founder and CEO, Pulumi), Brian Grant (CTO, Stealth), Elad Ben-Israel (Co-Founder and CEO, Winglang), Adam Jacob (Co-Founder and CEO, System Initiative), and Luke Hoban (CTO, Pulumi) will discuss the past, the present, and the future of Infrastructure as Code and the overall infrastructure technologies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="workshops-and-hands-on-learning"&gt;Workshops and Hands-On Learning&lt;/h2&gt;
&lt;p&gt;Want to get hands-on experience? Our live workshops are designed to deepen your understanding of trending topics, Pulumi, and its applications.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://www.pulumi.com/resources/managing-team-secrets-with-1password-pulumi-esc/"&gt;Managing Team Secrets with 1Password &amp;amp; Pulumi ESC&lt;/a&gt;&amp;quot;&lt;/strong&gt; on September 25, led by Phil Johnston (1Password) and Diana Esteves (Pulumi).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://www.pulumi.com/resources/securing-iac-pipelines-in-regulated-industries/"&gt;Securing IaC Pipelines in Regulated Industries&lt;/a&gt;&amp;quot;&lt;/strong&gt; on September 26, led by Josh Kodroff (Pulumi) and Marina Novikova (AWS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://www.pulumi.com/resources/deploy-tailscale-infrastructure-with-pulumi/"&gt;Deploy Tailscale infrastructure in AWS with Pulumi&lt;/a&gt;&amp;quot;&lt;/strong&gt; on October 15, led by Diana Esteves (Pulumi) and Lee Briggs (Tailscale).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;&lt;a href="https://www.pulumi.com/resources/observability-as-code-for-ai-apps-new-relic/"&gt;Observability as Code for AI Apps with New Relic and Pulumi&lt;/a&gt;&amp;quot;&lt;/strong&gt; on October 30, led by Diana Esteves (Pulumi) and Harry Kimpel (New Relic).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="amazing-partners-and-sponsors"&gt;Amazing Partners and Sponsors&lt;/h2&gt;
&lt;p&gt;Thank you to our amazing customers, sponsors, and partners for supporting PulumiUP!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://aws.amazon.com/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;AWS&lt;/a&gt;&lt;/strong&gt; The world&amp;rsquo;s most comprehensive and broadly adopted cloud, offering over 200 fully featured services from data centers globally.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://cloud.google.com/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Google Cloud&lt;/a&gt;&lt;/strong&gt; Help developers build quickly, securely, and cost effectively with the next generation of modern infrastructure designed to meet specific workload and industry needs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://newrelic.com/instant-observability/pulumi/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;New Relic&lt;/a&gt;&lt;/strong&gt; Data for engineers to monitor, debug, and improve their entire stack.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://docker.com/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Docker&lt;/a&gt;&lt;/strong&gt; Accelerate how you build, share, and run applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://1password.com/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;1Password&lt;/a&gt;&lt;/strong&gt; Streamline how you manage SSH keys, API tokens, and other infrastructure secrets throughout the entire software development life cycle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://tailscale.com/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Tailscale&lt;/a&gt;&lt;/strong&gt; Make creating software-defined networks easy: securely connecting users, services, and devices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://pinecone.io/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Pinecone&lt;/a&gt;&lt;/strong&gt; Build remarkable GenAI applications fast, with lower cost, better performance, and greater ease of use at any scale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://wiz.io/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Wiz&lt;/a&gt;&lt;/strong&gt; Secure everything you build and run in the cloud.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://honeycomb.io/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Honeycomb&lt;/a&gt;&lt;/strong&gt; Observability that helps solve problems you couldn’t before.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://mystenlabs.com/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Mysten Labs&lt;/a&gt;&lt;/strong&gt; Building critical infrastructure to enable a more decentralized internet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://imaginelearning.com/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Imagine Learning&lt;/a&gt;&lt;/strong&gt; Empowering educators to inspire breakthrough moments in every student’s unique learning journey with digital-first, K–12 education solutions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://dagger.io/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Dagger&lt;/a&gt;&lt;/strong&gt; Powerful, programmable open source CI/CD engine that runs your pipelines in containers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://osohq.com/?utm_source=Pulumi.com&amp;amp;utm_medium=Website&amp;amp;utm_campaign=PulumiUP"&gt;Oso&lt;/a&gt;&lt;/strong&gt; Authorization as a service.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="build-your-schedule-now"&gt;Build Your Schedule Now&lt;/h2&gt;
&lt;p&gt;With so many compelling topics across AI/ML, Platform Engineering, DevOps, and Cloud and IaC, now is the time to &lt;a href="https://conference.pulumi.com/schedule/?utm_source=PulumiUP&amp;amp;utm_medium=web&amp;amp;utm_campaign=FY2025Q1_Event_PulumiUP"&gt;build your PulumiUP schedule&lt;/a&gt;. Select the sessions that match your goals and interests to maximize your experience. Plus, you&amp;rsquo;ll receive a link to watch your chosen panel discussions and tech talks on-demand!&lt;/p&gt;
&lt;p&gt;Haven’t registered yet? It’s not too late! Join 5,500 engineers worldwide in this groundbreaking event and gain the insights you need to drive innovation at your company. &lt;a href="https://www.pulumi.com/pulumi-up/"&gt;Register now-&amp;gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can’t wait to see you there!&lt;/p&gt;</description><author>Sara Huddleston</author><category>pulumi-events</category><category>announcements</category><category>platform-engineering</category><category>ai</category><category>cloud-computing</category><category>infrastructure-as-code</category><category>devsecops</category><category>community</category></item><item><title>Platform Engineering: Cloud-Native, Maturity Models, and Platforms as Products</title><link>https://www.pulumi.com/blog/platform-engineering-cncf-maturity-model/</link><pubDate>Fri, 26 Jul 2024 17:40:17 +0000</pubDate><guid>https://www.pulumi.com/blog/platform-engineering-cncf-maturity-model/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/platform-engineering-cncf-maturity-model/index.png" /&gt;
&lt;p&gt;The Platform Engineering &amp;amp; DevOps in-person series launched in Berlin with two great speakers. This blog article is an overview of Dominik Kress&amp;rsquo;s talk, “What the Heck is the CNCF Platform Working Group? Answers from a Member!” in which he discussed Cloud-Native Platforms, The Platform Maturity Model, and approaching Platforms as Products.&lt;/p&gt;
&lt;p&gt;In this article, you&amp;rsquo;ll learn more about &lt;a href="https://www.pulumi.com/what-is/what-is-platform-engineering/"&gt;platform engineering&lt;/a&gt; and how to get involved with the CNCF Platform Working Group. Make sure to check our &lt;a href="https://luma.com/pulumi"&gt;Pulumi User Groups (PUGs)&lt;/a&gt; to find a meetup near you.&lt;/p&gt;
&lt;h2 id="on-this-platform-engineering-article"&gt;On this platform engineering article:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/platform-engineering-cncf-maturity-model/#what-is-a-platform"&gt;What is a Platform?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/platform-engineering-cncf-maturity-model/#defining-cloud-native-platforms"&gt;Defining Cloud Native Platforms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/platform-engineering-cncf-maturity-model/#the-platform-maturity-model-charting-the-path-to-success"&gt;The Platform Maturity Model: Charting the Path to Success&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/platform-engineering-cncf-maturity-model/#platforms-as-products-driving-success"&gt;Platforms as Products: Driving Success&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/platform-engineering-cncf-maturity-model/#frequently-asked-questions"&gt;Frequently Asked Questions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-is-a-platform"&gt;What is a Platform?&lt;/h2&gt;
&lt;p&gt;According to CNCF, a platform in cloud-native computing is an integrated collection of capabilities designed to meet users&amp;rsquo; needs. It serves as a cross-cutting layer, ensuring a consistent experience integrating and managing services and capabilities for various applications and use cases. A good platform provides consistent user experiences through portals, project templates, and self-service APIs.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/case-studies/atlassian/"&gt;Atlassian&lt;/a&gt; describes platform teams as creators of capabilities used by multiple product teams, reducing their overhead and cognitive load. Martin Fowler and Evan Bottcher define a digital platform as a foundation of self-service APIs, tools, services, and support arranged as an internal product. These platforms enable autonomous teams to deliver features faster with less coordination.&lt;/p&gt;
&lt;p&gt;The specific capabilities of a platform are tailored to the needs of its stakeholders and users. While platform teams provide these capabilities, they do not always implement them directly. Managed service providers or dedicated internal teams can maintain the underlying implementations, ensuring consistency across the organization. Platforms are tailored to an enterprise’s internal users and are particularly relevant for cloud-native architectures, separating supporting capabilities from application-specific logic.&lt;/p&gt;
&lt;h2 id="defining-cloud-native-platforms"&gt;Defining Cloud Native Platforms&lt;/h2&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/NUPK5CCm6XA?rel=0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;The core of the discussion at the CNCF Platform Working Group is: &amp;ldquo;What exactly is a cloud-native platform, and why do organizations need one?&amp;rdquo;
A cloud-native platform is a strategic business initiative that enables organizations to deliver value more efficiently and effectively. It is characterized by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vendor-neutrality&lt;/strong&gt;: A cloud-native platform should be open and agnostic, allowing organizations to leverage various technologies and services without being locked into a single vendor.
&lt;strong&gt;Scalability and flexibility&lt;/strong&gt;: The platform should be designed to accommodate the organization&amp;rsquo;s growing and changing needs, with the ability to add or remove components as needed easily.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/software-developer-experience-devex-devx-devops-culture/"&gt;Developer-centricity&lt;/a&gt;&lt;/strong&gt;: The platform should empower developers to be more productive by providing self-service capabilities, streamlined workflows, and a consistent developer experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com/blog/developer-portal-platform-teams/"&gt;Operational efficiency&lt;/a&gt;&lt;/strong&gt;: The platform should simplify and automate the management and maintenance of the underlying infrastructure, allowing the organization to focus on delivering value to customers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous improvement&lt;/strong&gt;: The platform should be designed with a mindset of continuous evolution, with regular updates and enhancements to address emerging needs and technologies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By aligning with these principles, organizations can achieve faster time-to-market, improved developer productivity, and reduced operational overhead.&lt;/p&gt;
&lt;h2 id="the-platform-maturity-model-charting-the-path-to-success"&gt;The Platform Maturity Model: Charting the Path to Success&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/platform-engineering-cncf-maturity-model/dominik-platform-engineering-berlin.png"
alt="Dominik Kress at the Berlin Pulumi Group meetup explaining the platform maturity model" width="100%"&gt;&lt;figcaption&gt;
&lt;p&gt;Dominik Kress at the Berlin Pulumi Group meetup explaining the platform maturity model&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Platform Working Group has developed a Platform Maturity Model. This framework can help assess the current state of a cloud native platform and identify steps to enhance its maturity. It defines five levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ad-hoc&lt;/strong&gt;: At the lowest level, the platform is ad-hoc, with no dedicated team or budget and a reliance on individual efforts and workarounds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational&lt;/strong&gt;: The platform has a dedicated team and budget, which focus primarily on operational tasks and maintenance rather than strategic initiatives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalable&lt;/strong&gt;: The platform is now viewed as a product with a clear roadmap and a focus on delivering value to internal customers. The platform team has more autonomy and the ability to drive innovation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enabled&lt;/strong&gt;: The platform has become a strategic asset, with a thriving ecosystem of contributors and consumers. The platform team is empowered to make decisions and drive the platform&amp;rsquo;s evolution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimized&lt;/strong&gt;: The platform is continuously optimized, focusing on measuring and improving key performance indicators. The platform is deeply integrated into the organization&amp;rsquo;s technology strategy and decision-making processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By using the CNCF&amp;rsquo;s &lt;a href="https://tag-app-delivery.cncf.io/whitepapers/platform-eng-maturity-model/"&gt;Platform Maturity Model&lt;/a&gt; as a guide, organizations can assess their current level of platform maturity, identify areas for improvement, and develop a roadmap to reach a higher level of platform success. This model has proven to be a valuable tool for many organizations, helping them to align their platform initiatives with their broader business objectives.&lt;/p&gt;
&lt;h2 id="platforms-as-products-driving-success"&gt;Platforms as Products: Driving Success&lt;/h2&gt;
&lt;p&gt;One of the key initiatives currently underway within the Platform Working Group is the &amp;ldquo;&lt;a href="https://tag-app-delivery.cncf.io/blog/product-thinking-for-platforms/"&gt;Platforms as Products&lt;/a&gt;&amp;rdquo; research project. This effort aims to explore the parallels between traditional product management and the management of cloud-native platforms to identify best practices and patterns that can help organizations unlock their platforms&amp;rsquo; full potential.&lt;/p&gt;
&lt;p&gt;The core premise of the &amp;ldquo;Platforms as Products&amp;rdquo; initiative is that successful cloud-native platforms should be treated and managed like products rather than just technology projects. This means applying principles of product management, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Defining a clear vision and roadmap&lt;/strong&gt;: Establishing a long-term vision for the platform and a roadmap to get there, focusing on delivering value to internal customers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Measuring and optimizing for key metrics&lt;/strong&gt;: Identifying and tracking the right performance indicators to ensure the platform delivers the desired outcomes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fostering a thriving ecosystem&lt;/strong&gt;: Encouraging and enabling a community of contributors and consumers to drive the platform&amp;rsquo;s evolution and adoption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuously iterating and improving&lt;/strong&gt;: Embracing a continuous improvement mindset, with regular updates and enhancements to the platform based on user feedback and emerging needs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By adopting a product management approach to their cloud native platforms, organizations can better align their platform initiatives with their broader business goals and ensure that the platform delivers tangible value to the organization.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This article and Dominik Kress&amp;rsquo;s talk highlighted the importance of platform engineering in cloud-native computing. Key points included defining cloud-native platforms, assessing platform maturity, and managing platforms with product management principles.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in learning more about platform engineering and getting involved in CNCF&amp;rsquo;s initiatives, there are several ways to participate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Register for the &lt;a href="https://info.pulumi.com/platform-engineering-workshop-series"&gt;Platform Engineering workshop series course&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Join the bi-weekly &lt;a href="https://tag-app-delivery.cncf.io/wgs/platforms/#meetings"&gt;CNCF Platform Working Group meetings&lt;/a&gt; every other Tuesday at 5 PM Berlin time (11 AM ET).&lt;/li&gt;
&lt;li&gt;Learn how to &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/developer-portals/"&gt;build Developer Portals with Pulumi&lt;/a&gt;. You can get off the ground faster with organization templates, a new project wizard, or leveraging the backstage plugin.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="frequently-asked-questions"&gt;Frequently Asked Questions&lt;/h2&gt;
&lt;h3 id="what-is-the-cncf-platform-working-group"&gt;What is the CNCF Platform Working Group?&lt;/h3&gt;
&lt;p&gt;Within the &lt;a href="https://www.cncf.io/"&gt;CNCF&lt;/a&gt;, the Platform Working Group is a collaborative effort focused on the critical topic of cloud native platforms. As a working group member, you can share insights and experiences from our discussions.&lt;/p&gt;
&lt;p&gt;The Platform Working Group is part of the CNCF&amp;rsquo;s App Delivery Technical Advisory Group, which includes sustainability, runtime, observability, and security initiatives. The working group is open to anyone interested in contributing to the conversation around cloud native platforms, and its members come from diverse backgrounds, including product managers, engineers, and industry experts.&lt;/p&gt;
&lt;h3 id="who-is-dominik-kress"&gt;Who is Dominik Kress?&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/in/dominik-kress-33a540174/"&gt;Dominik Kress&lt;/a&gt; is a product manager at Giant Swarm and a CNCF Platforms Working Group member.&lt;/p&gt;
&lt;p&gt;He is on a mission to simplify developers&amp;rsquo; lives by delivering intuitive developer platforms. He has been in the IT industry for over seven years, starting his journey as a Full Stack Software Engineer, falling in love with DevOps and Product Organisations to become a Product Manager for Cloud Technology later.&lt;/p&gt;
&lt;p&gt;Dominik is passionate about Cloud Transformation, Product Management, and helping people. He loves contributing to the community with talks, articles, or publications.&lt;/p&gt;
&lt;h3 id="how-can-i-participate-in-the-platform-as-a-product-research"&gt;How can I participate in the Platform As A Product research?&lt;/h3&gt;
&lt;p&gt;The &amp;ldquo;Platforms as Products&amp;rdquo; research project is currently underway, with the Platform Working Group conducting interviews and gathering insights from organizations that have successfully implemented cloud native platforms. The goal is to distill these learnings into a set of best practices and patterns that can be shared with the broader community.&lt;/p&gt;
&lt;p&gt;To participate in this research, &lt;a href="https://bit.ly/platform-research"&gt;sign up here&lt;/a&gt;.&lt;/p&gt;</description><author>Sara Huddleston</author><category>platform-engineering</category><category>community</category><category>pulumi-events</category></item><item><title>Platform Engineering &amp; DevOps Series Kickoff Announcement</title><link>https://www.pulumi.com/blog/platform-engineering-devops-event-meetup-community/</link><pubDate>Mon, 15 Jul 2024 14:38:46 +0000</pubDate><guid>https://www.pulumi.com/blog/platform-engineering-devops-event-meetup-community/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/platform-engineering-devops-event-meetup-community/index.png" /&gt;
&lt;p&gt;We are excited to announce the kickoff of the Platform Engineering &amp;amp; DevOps Series, which will run from July 16 to October 31. This series will feature in-person events across various cities, including Berlin, London, Paris, Sydney, Boston, San Francisco, Seattle, Austin, Denver, and NYC. For those unable to attend an in-person event, we encourage you to participate in a virtual DevOps &amp;amp; Platform Engineering workshop.&lt;/p&gt;
&lt;h2 id="upcoming-in-person-events"&gt;Upcoming In-Person Events&lt;/h2&gt;
&lt;p&gt;Pulumi is hosting in-person meetups and virtual workshops for engineers interested in Platform Engineering and DevOps, covering topics from best practices to setup and implementation to advanced topics. Join us for tech talks, hands-on workshops, and opportunities to network with fellow engineers.&lt;/p&gt;
&lt;h3 id="first-platform-engineering--devops-in-person-event-berlin---july-16"&gt;First Platform Engineering &amp;amp; DevOps in-person event: Berlin - July 16&lt;/h3&gt;
&lt;p&gt;Join &lt;a href="https://www.linkedin.com/in/dominik-kress-33a540174/"&gt;Dominik Kress&lt;/a&gt; (Product Manager at Giant Swarm and member of the CNCF Platforms Working Group) and &lt;a href="https://www.linkedin.com/in/guy-menahem/"&gt;Guy Menahem&lt;/a&gt; (Solutions Architect at Komodor and community leader at The Platformers) at LOBE Block for an engaging discussion on platform engineering.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dominik Kress will present &amp;ldquo;What the Heck is the CNCF Platform Working Group? Answers from a Member!&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Guy Menahem will present &amp;ldquo;For This Backstage, You Don’t Need a Special Pass: Platform Engineering in Practice.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details and to RSVP, visit the &lt;a href="https://luma.com/pulumi"&gt;Berlin Meetup page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="second-in-person-event-boston-july-24"&gt;Second In-Person Event: Boston, July 24&lt;/h3&gt;
&lt;p&gt;Join Pulumi&amp;rsquo;s founder &amp;amp; CEO, &lt;a href="https://www.linkedin.com/in/joejduffy/"&gt;Joe Duffy&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/jbaldanza/"&gt;Jamie Baldanza&lt;/a&gt;, Director of DevOps at Relay Therapeutics, for the launch of our Boston meetup at The Foundry. This is a great opportunity for DevOps engineers, platform engineers, software engineers, and Cloud enthusiasts to connect and share insights.&lt;/p&gt;
&lt;p&gt;RSVP is required due to limited capacity. Secure your spot, and RSVP at the &lt;a href="https://luma.com/pulumi"&gt;Boston Meetup page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="more-coming-soon"&gt;More coming soon!&lt;/h3&gt;
&lt;p&gt;We expected Seattle, Paris, and London&amp;rsquo;s meetups to happen in early September.
Details for the other in-person events will be coming soon, so stay tuned for more updates at &lt;a href="https://info.pulumi.com/platform-engineering-devops-series"&gt;Platform Engineering &amp;amp; DevOps Series&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;No upcoming in-person events in your area? Attend one of our virtual workshops and keep up with the latest in DevOps &amp;amp; Platform Engineering!&lt;/p&gt;
&lt;h3 id="virtual-workshops"&gt;Virtual Workshops&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Getting Started with CI/CD for AWS using GitHub Actions&lt;/strong&gt;: July 17. Learn more and &lt;a href="https://www.pulumi.com/resources/getting-started-with-ci-cd-aws-pulumi-github-actions/"&gt;register here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;From Zero to Production in Kubernetes&lt;/strong&gt;: July 25. Learn more and &lt;a href="https://www.pulumi.com/resources/from-zero-to-production-in-kubernetes/"&gt;register here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced CI/CD for AWS using Pulumi and GitHub Actions&lt;/strong&gt;: August 14. Learn more and &lt;a href="https://www.pulumi.com/resources/advanced-cicd-aws-pulumi-github-actions/"&gt;register here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mastering Platform Engineering: From Setup to Scaling Success&lt;/strong&gt;: TBD.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Join us for tech talks, hands-on workshops, and opportunities to network with industry peers. We are excited to meet you in person or virtually.
See you there!&lt;/p&gt;</description><author>Sara Huddleston</author><category>developer-experience</category><category>devops</category><category>platform-engineering</category><category>community</category><category>pulumi-events</category></item><item><title>How AI is Transforming DevOps: AI Talks for DevOps Insights</title><link>https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/</link><pubDate>Mon, 13 Nov 2023 22:05:34 +0000</pubDate><guid>https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/index.png" /&gt;
&lt;p&gt;The integration of artificial intelligence (AI) with DevOps signals a new era in software development. DevOps possesses unique characteristics and needs that make it exceptionally compatible with AI augmentation. Given that code fundamentally relies on language, and large language models (LLMs) serve as the core of GPT functionality, these models are particularly well-suited for tasks such as code generation. This article unwraps the topics addressed during our “AI: Friends or Foe | AI Talks for DevOps” event in San Francisco.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/#how-will-ai-impact-the-future-of-devops"&gt;How will AI Impact the Future of DevOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/#what-is-devops-in-ai"&gt;What is DevOps in AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/#what-is-ai-in-devops"&gt;What is AI in DevOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/#how-ai-is-currently-used-in-devops"&gt;How AI is Currently Used in DevOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/#how-can-a-devops-team-take-advantage-of-artificial-intelligence"&gt;How Can a DevOps Team Take Advantage of Artificial Intelligence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/#top-5-skills-for-devops-engineers-in-the-ai-era"&gt;Top 5 Skills for DevOps Engineers in the AI era&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/#how-to-overcome-ai-for-devops-challenges"&gt;How to Overcome AI for DevOps Challenges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/#frequently-asked-questions"&gt;Frequently Asked Questions&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="how-will-ai-impact-the-future-of-devops"&gt;How will AI Impact the Future of DevOps&lt;/h2&gt;
&lt;p&gt;In the Fireside chat, Luke Hoban expressed his enthusiasm for incorporating AI into DevOps, emphasizing its potential utilization by both DevOps professionals and the development team. In contrast, Nana Janashia, &lt;a href="https://www.youtube.com/@TechWorldwithNana"&gt;TechWorld with Nana&lt;/a&gt;, adopted a more cautious stance, drawing attention to potential risks, particularly regarding reliability.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/i09F14yc0l4?rel=0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="what-is-devops-in-ai"&gt;What is DevOps in AI&lt;/h2&gt;
&lt;p&gt;When considering the adoption of AI and AI engineering, such as prompt engineering and other specific disciplines, AI engineers rely heavily on the cloud. Many AI-based systems are built around cloud-native tools, practices, and managed services.&lt;/p&gt;
&lt;p&gt;The extensive use of cloud-native primitives means that AI is fundamentally a cloud adoption story. Consequently, established DevOps and &lt;a href="https://www.pulumi.com/what-is/what-is-infrastructure-as-code/"&gt;infrastructure as code&lt;/a&gt; tooling and best practices play an important role in AI engineering, in what is being done, and in leveraging its use.&lt;/p&gt;
&lt;h2 id="what-is-ai-in-devops"&gt;What is AI in DevOps&lt;/h2&gt;
&lt;p&gt;Integrating AI in DevOps entails employing machine learning (ML) and other artificial intelligence technologies to automate and optimize various software development and delivery aspects, including tasks such as automating testing and deployment processes and refining resource management.&lt;/p&gt;
&lt;p&gt;The incorporation of AI in DevOps can result in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enhanced speed&lt;/li&gt;
&lt;li&gt;Improved resource management&lt;/li&gt;
&lt;li&gt;Increased reliability throughout the software development lifecycle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consequently, organizations may experience swifter deployments, reduced errors, and heightened overall productivity. By substituting manual processes with automation and AI-powered solutions, DevOps teams can elevate product quality and more efficiently oversee their systems.&lt;/p&gt;
&lt;h2 id="how-ai-is-currently-used-in-devops"&gt;How AI is Currently Used in DevOps&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/luke-nana-adora-discussing-devop-ai.png"
alt="Luke Hoban at the San Francisco Pulumi Group meetup discussing the usage of AI, during the AI Talks for DevOps" width="100%"&gt;&lt;figcaption&gt;
&lt;p&gt;Luke Hoban at the San Francisco Pulumi Group meetup discussing the usage of AI, during the AI Talks for DevOps&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;DevOps significantly impacts all stages of the development and operations lifecycle. It has helped break silos and optimize to increase developer velocity and productivity, creating a positive impact on the ops side.&lt;/p&gt;
&lt;p&gt;AI assists DevOps teams to refine their operations by detecting inefficiencies and triggering warnings as soon as issues appear. This technology also enables real-time observation of systems and applications, permitting prompt actions from operations personnel and minimizing downtime.&lt;/p&gt;
&lt;p&gt;AI-driven automation can also help the software development process with code generation. The operations teams from both departments quickly pinpoint any issues in their workflows while speeding up decision-making processes.&lt;/p&gt;
&lt;p&gt;In the long term, the AI may use cloud credentials to create and autonomously automate deployment and management and perform DevOps tasks on behalf of the DevOps or &lt;a href="https://www.pulumi.com/what-is/what-is-platform-engineering/"&gt;platform engineering&lt;/a&gt; team. In the near term, it is used to assist humans, from DevOps professionals to developers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;“In the near term, AI usage in DevOps is about how do you keep the human in the loop, how do you use it as a learning tool, as a code generation tool, and for auditing to provide actionable notifications, all of which are still very human-dependent. There is so much more it can be used for. Currently, it&amp;rsquo;s a tool serving humans. But it is fascinating to imagine it in the future when it can be trusted to be an autonomous agent.”&lt;/strong&gt;&lt;/em&gt; - &lt;em&gt;Luke Hoban, CTO of Pulumi and co-creator of TypeScript&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, how does this technology bring about these advancements? Two main ways are by streamlining development tasks and improving monitoring and security protocols.&lt;/p&gt;
&lt;h3 id="streamlining-development-workflows"&gt;Streamlining Development Workflows&lt;/h3&gt;
&lt;p&gt;AI has the potential to significantly change the software development life cycle by automating routine processes, using resources efficiently, and offering solutions for improved code. DevOps teams can leverage AI-driven tools to increase effectiveness when managing their assets while also improving code quality, leading to faster project deliveries.&lt;/p&gt;
&lt;p&gt;For instance, AI can identify any slowdowns during progress and recommend improvements, analyze root causes, and help make judicious decisions regarding resource allocation, ultimately resulting in efficient delivery of software products.&lt;/p&gt;
&lt;h3 id="enhancing-monitoring-and-security"&gt;Enhancing Monitoring and Security&lt;/h3&gt;
&lt;p&gt;The usage of AI significantly boosts DevOps monitoring and security. With the assistance of AI tools, operations teams can use machine learning algorithms to examine a massive amount of data to protect their applications from potential risks and enhance reliability. These algorithmic solutions provide valuable advice for development personnel, which helps strengthen protection measures while optimizing performance simultaneously.&lt;/p&gt;
&lt;h2 id="how-can-a-devops-team-take-advantage-of-artificial-intelligence"&gt;How Can a DevOps Team Take Advantage of Artificial Intelligence&lt;/h2&gt;
&lt;p&gt;A DevOps team can leverage AI in several ways to enhance efficiency, automation, and overall productivity. Here are some key strategies for a DevOps team to leverage AI:&lt;/p&gt;
&lt;h3 id="code-generation-and-learning"&gt;Code Generation and Learning&lt;/h3&gt;
&lt;p&gt;AI can be a powerful tool to make the development process much smoother. It can help engineers focus on what matters instead of searching for documentation on websites, developer forums, or search engines for answers to help solve specific problems. The perfect code snippet is often hard to find or may not even exist.&lt;/p&gt;
&lt;p&gt;Generative AI, powered by LLMs, can help developers and DevOps engineers quickly discover, learn, and, for example, use new cloud infrastructure APIs.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-ai/run-metabase-in-azure.png"
alt="Pulumi AI response to Run metabase in Azure, using TypeScript" width="90%"&gt;&lt;figcaption&gt;
&lt;p&gt;Pulumi AI response to Run metabase in Azure, using TypeScript&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can see it in practice &lt;a href="https://www.pulumi.com/blog/pulumi-ai/"&gt;using Pulumi AI&lt;/a&gt;. You can use natural language to ask for a specific cloud infrastructure goal and obtain a corresponding Pulumi program in your preferred programming language. Moreover, Pulumi AI facilitates the iterative refinement of your cloud infrastructure, incorporating new features, enhancements to security and performance, resolution of correctness issues, and clarification of requirements. &lt;a href="https://www.pulumi.com/product/neo/"&gt;Try Pulumi Neo&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="intelligent-resource-management"&gt;Intelligent Resource Management&lt;/h3&gt;
&lt;p&gt;Platform and developer teams are spinning up resources across many cloud accounts within multiple cloud providers. It can be almost impossible to aggregate and search your infrastructure across these accounts since every cloud and every account is (by design!) a walled garden.&lt;/p&gt;
&lt;p&gt;Often, to answer key questions about their business, such as “Which team has the largest cloud footprint?”, teams need to combine data across various clouds, cloud accounts, and SaaS tools. This prevents organizations from getting quick answers to operational questions and barriers to building aggregations to derive insights. AI can provide actionable insights over infrastructure with natural language processing (NLP) queries.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;“I can get developers using IaC immediately with Pulumi Deployments and its GitHub integration, while Pulumi Insights [Resource Search] makes it really easy to find idle developer environments that need to be shut down, which reduces our cloud costs”&lt;/strong&gt;&lt;/em&gt; - &lt;em&gt;Santosh Dornal, Head of Software Test &amp;amp; DevOps, Alkira.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When using Pulumi Cloud, you can leverage &lt;a href="https://www.pulumi.com/blog/ai-assist-improvements/"&gt;AI Assist&lt;/a&gt;. You can provide a plain English (Spanish, French, or Japanese) request to express queries where you might not know the exact syntax, type tokens, or package names. AI Assist makes it easier by providing answers to queries like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;show me all s3 buckets not tagged in production&lt;/li&gt;
&lt;li&gt;show azure and azure native security groups&lt;/li&gt;
&lt;li&gt;show all AWS VPCs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="remediation-tool-continuous-and-automatic-compliance"&gt;Remediation Tool: Continuous and Automatic Compliance&lt;/h3&gt;
&lt;p&gt;Defining and managing security rules, criteria, and conditions are key to enabling security at scale. Policy as Code, or Compliance as Code, has the ability to verify and spot problems before deploying the infrastructure. When policies are written with code, you can apply software development practices such as testing, automated deployment, and version control.&lt;/p&gt;
&lt;p&gt;AI can be valuable not just in anomaly detection but also in remediation, which doesn&amp;rsquo;t just warn or error but can change the infrastructure directly to remediate compliance gaps. With Pulumi, &lt;a href="https://www.pulumi.com/blog/remediation-policies/"&gt;Remediation policies&lt;/a&gt; can be configured and applied across any subset of stacks within an organization.&lt;/p&gt;
&lt;p&gt;Consider the &lt;a href="https://techcrunch.com/2017/02/01/gitlab-suffers-major-backup-failure-after-data-deletion-incident/"&gt;unfortunate incident at GitLab&lt;/a&gt;, marked by a disastrous sequence of events resulting in the loss of segments of production databases and substantial user data.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/tLdRBsuvVKc?si=brUXETRY4Rew7954?rel=0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;In this scenario, AI could have swiftly pinpointed the root cause and proposed a rapid solution, potentially mitigating the extent of damage before it escalated.&lt;/p&gt;
&lt;p&gt;While the benefits of writing policies as code are evident for DevOps engineers, &lt;a href="https://www.pulumi.com/blog/benefits-of-policy-as-code/"&gt;the organizational benefits are even more significant&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="enhance-collaboration-and-communication"&gt;Enhance Collaboration and Communication&lt;/h3&gt;
&lt;p&gt;Nowadays, many professionals with DevOps in their titles are held accountable for the multiple aspects of Dev + Ops. At its core, DevOps is a cultural movement that removes barriers among teams and promotes collaboration and shared responsibility between dev and ops.&lt;/p&gt;
&lt;p&gt;DevOps teams have seen great benefits from AI-driven tools, particularly in improving transparency, encouraging feedback, providing quick answers to common queries, and automating routine tasks. Many major companies, like Microsoft, use AI to build efficient workflows that make better decisions, leading to increased business outcomes. This includes smoother application delivery, a prime function of DevOps initiatives.&lt;/p&gt;
&lt;h3 id="improved-product-delivery"&gt;Improved Product Delivery&lt;/h3&gt;
&lt;p&gt;Companies incorporating AI into their DevOps strategy reap the rewards of accelerated product delivery. This has been made possible through improved workflows, automated testing, and strengthened security measures, leading to greater efficiency in software development by allowing development and operations teams more scope for dealing with complex tasks using machine learning models. They can make better decisions, resulting in faster product delivery and enhanced business outcomes.&lt;/p&gt;
&lt;p&gt;By streamlining existing systems within this context, customers can benefit from superior products delivered faster than before while companies provide and reinforce their business value.&lt;/p&gt;
&lt;h3 id="planning-and-cost-optimization"&gt;Planning and Cost Optimization&lt;/h3&gt;
&lt;p&gt;Implementing AI algorithms or AI tools that analyze usage patterns can assist in predicting future resource needs and optimize cloud infrastructure costs. This also ties in with intelligent resource management mentioned before, which can enhance cost efficiency, optimize resource allocation, and prevent unnecessary expenses.&lt;/p&gt;
&lt;h2 id="top-5-skills-for-devops-engineers-in-the-ai-era"&gt;Top 5 Skills for DevOps Engineers in the AI Era&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/devops-ai-developer-future--pulumi-user-group-tech-talks/adora-nana-talking-angle.png"
alt="Adora Nwodo with Nana discussing skills that DevOps engineers will need to learn to make AI work" width="100%"&gt;&lt;figcaption&gt;
&lt;p&gt;Adora Nwodo with Nana discussing skills that DevOps engineers will need to learn to make AI work&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;DevOps engineers must open new skills, mainly in cloud services, data, and AI/ML.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Programming Languages&lt;/strong&gt;: Although DevOps don&amp;rsquo;t usually need to know general programming languages in-depth, they should know what is most used by application teams. It&amp;rsquo;s a nice-to-have to find errors in CI/CD pipelines and fix them themselves as needed, but it is a must-have if the goal is to work MLOps.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure as Code&lt;/strong&gt;: Automation is important for DevOps and critical for MLOps. IaC, using familiar languages, has been used since the early days of MLOps and is a key component to solving the cloud orchestration challenge present in AI/ML. Dive into more detail in &lt;a href="https://www.pulumi.com/blog/mlops-the-ai-challenge-is-cloud-not-code/"&gt;The Real AI Challenge is Cloud, not Code!&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Continuous Integration and Delivery&lt;/strong&gt;: CI/CD is mandatory knowledge, and it must be understood in detail to predict data preparation code, AI/ML code, CI/CD code, and infrastructure-related code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom containers&lt;/strong&gt;: As a DevOps professional, you are expected to understand Docker and/or Kubernetes concepts and best practices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Orchestration&lt;/strong&gt;: This is the holy grail of Ops and your key to unlocking AI.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/UfQeX-8HRmI?rel=0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="how-to-overcome-ai-for-devops-challenges"&gt;How to Overcome AI for DevOps Challenges&lt;/h2&gt;
&lt;p&gt;The integration of AI in DevOps brings forth several challenges that organizations must navigate to leverage the full potential of these technologies. Here are some key challenges and potential strategies to overcome them:&lt;/p&gt;
&lt;h3 id="1-skills-and-expertise"&gt;1. Skills and Expertise&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Challenge&lt;/strong&gt;: AI &amp;amp; ML expertise is often scarce within DevOps teams, leading to a skill gap in implementing and managing AI-driven tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overcoming Strategy&lt;/strong&gt;: Organizations will need to hire professionals with AI and ML expertise, or existing team members will need training in machine learning, becoming proficient in MLOps combined to DevOps principles. Collaboration between DevOps and data science teams can also bridge the knowledge gap.&lt;/p&gt;
&lt;h3 id="2-integration-complexity"&gt;2. Integration Complexity&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Challenge&lt;/strong&gt;: Integrating AI seamlessly into existing DevOps workflows can be complex, given the diverse technologies and processes involved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overcoming Strategy&lt;/strong&gt;: Prioritize a phased integration approach, gradually introducing AI capabilities into specific stages of the DevOps pipeline. This allows teams to adapt without disrupting established processes.&lt;/p&gt;
&lt;h3 id="3-scalability"&gt;3. Scalability&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Challenge&lt;/strong&gt;: As AI applications within DevOps expand, ensuring scalability becomes crucial to accommodate growing workloads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overcoming Strategy&lt;/strong&gt;: Design AI solutions with scalability in mind, utilizing cloud resources and containerization. Regularly assess performance and adjust infrastructure to meet evolving demands.&lt;/p&gt;
&lt;h3 id="4-data-quality-and-availability"&gt;4. Data Quality and Availability&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Challenge&lt;/strong&gt;: AI models rely heavily on high-quality and readily available data. Inconsistent or inadequate data can lead to inaccurate predictions or suboptimal performance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overcoming Strategy&lt;/strong&gt;: Prioritize data quality and establish robust data pipelines. Conduct regular audits to ensure data accuracy and implement strategies for handling missing or incomplete data.&lt;/p&gt;
&lt;h3 id="5-cultural-resistance"&gt;5. Cultural Resistance&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Challenge&lt;/strong&gt;: Resistance to change or skepticism about the benefits of AI within DevOps and in software development can impede successful integration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overcoming Strategy&lt;/strong&gt;: Foster a culture of innovation and continuous improvement. Provide clear communication about the benefits of AI, involve team members in the decision-making process, and showcase success stories to build confidence.&lt;/p&gt;
&lt;p&gt;Navigating these challenges requires a thoughtful and strategic approach, focusing on collaboration, skill development, and the gradual incorporation of AI into existing DevOps practices. Regular evaluation and adaptation are essential to ensure a successful and sustainable integration of AI in the DevOps environment.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;AI in DevOps can transform software development by making workflows more efficient, monitoring and security stronger, and overall effectiveness increased. Organizations that embrace AI/ML aim for better collaboration between teams, automating and intelligently managing infrastructure, reducing time-to-market, and increasing developer productivity and cost savings due to streamlining processes within their development lifecycle. AI and ML knowledge will become necessary skills expected to be applied to enhance and benefit the development, security, and operation teams as a whole. Follow the Pulumi Python + AI/ML series to upskill and get ahead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/mlops-the-ai-challenge-is-cloud-not-code/"&gt;The Real AI challenge is Cloud, not Code!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/mlops-huggingface-llm-aws-sagemaker-python/"&gt;Deploy AI Models on Amazon SageMaker using Pulumi Python IaC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/deploy-ai-ml-vercel-app/"&gt;Deploying Your AI/ML Chatbot Frontend To Vercel Using Pulumi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="frequently-asked-questions"&gt;Frequently Asked Questions&lt;/h2&gt;
&lt;h3 id="can-ai-replace-devops"&gt;Can AI replace DevOps?&lt;/h3&gt;
&lt;p&gt;DevOps has not been taken over by AI yet. Rather, its use has made a difference in code quality and accelerated certain processes. Improvements to the quality of coding have been seen as one of the main benefits brought on through integration with artificial intelligence.&lt;/p&gt;
&lt;h3 id="what-is-an-example-of-ai-in-devops"&gt;What is an example of AI in DevOps?&lt;/h3&gt;
&lt;p&gt;AI can be utilized to make DevOps processes more efficient by creating test cases and pipelines, assessing infrastructure configurations for security concerns, and deploying applications. This technology includes machine learning algorithms, natural language processing systems, computer vision capabilities, chatbot programs, and virtual assistants. All are designed with one goal in mind – optimization of operations within development teams.&lt;/p&gt;
&lt;h3 id="what-is-the-future-of-devops"&gt;What is the future of DevOps?&lt;/h3&gt;
&lt;p&gt;Operations teams collaborating with developers to create quality products quickly and effectively is the goal for DevOps moving forward. The vision of this process involves both sides working together, resulting in an improved system and increased efficiency.&lt;/p&gt;</description><author>Sara Huddleston</author><author>Scott Lowe</author><category>devops</category><category>pulumi-events</category><category>ai</category><category>ml</category><category>community</category><category>platform-engineering</category></item><item><title>LangChain for DevOps: Learn LLM &amp; GenAI for Dev, Sec &amp; Ops</title><link>https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/</link><pubDate>Thu, 26 Oct 2023 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/generative-ai-apps-devops-talks-pulumi-user-group/index.png" /&gt;
&lt;p&gt;The emergence of DevOps revolutionized software development. Now, with AI powered tools like LangChain, these transformations are being accelerated. Unsurprisingly, our distinguished speaker at the launch of Pulumi&amp;rsquo;s in-person AI Talks, Patrick Debois, who coined the term &amp;ldquo;DevOps,&amp;rdquo; has recently tuned into LLM and GenAI Ops using the Langchain framework.&lt;/p&gt;
&lt;p&gt;This article unwraps the topics addressed during our &amp;ldquo;Generative AI, Apps, and DevOps | AI/ML Talks&amp;rdquo; event in Seattle, focusing on Langchain and GenAI for DevOps. You&amp;rsquo;ll be able to &lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#how-to-use-langchain-to-learn-llms-and-genai-for-devsecops"&gt;watch Patrick Debois teach LangChain to learn LLMs and GenAI&lt;/a&gt;, with code examples and lessons that are easily understood by traditional software engineering.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#what-is-langchain-used-for"&gt;What is LangChain used for?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#why-is-langchain-becoming-popular"&gt;Why is LangChain becoming popular?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#langchain-accelerating-devops"&gt;LangChain Accelerating DevOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#how-to-use-langchain-to-learn-llms-and-genai-for-devsecops"&gt;How to Use LangChain to Learn LLMs and GenAI for Dev(Sec)Ops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#real-world-applications-of-langchain-ai-in-devops"&gt;Real-World Applications of LangChain AI in DevOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#monitoring-and-anomaly-detection-with-langchain-ai"&gt;Monitoring and Anomaly Detection with LangChain AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#future-ai-apps-and-devops"&gt;Future: AI, Apps, and DevOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/#frequently-asked-questions"&gt;Frequently Asked Questions&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="what-is-langchain-used-for"&gt;What is LangChain used for?&lt;/h2&gt;
&lt;p&gt;LangChain is an open-source Python framework designed to empower developers to construct robust generative AI applications. It facilitates the integration of advanced AI models such as OpenAI&amp;rsquo;s GPT, &lt;a href="https://gemini.google.com"&gt;Google&amp;rsquo;s Gemini&lt;/a&gt;, and &lt;a href="https://ai.meta.com/llama"&gt;Meta&amp;rsquo;s LLaMA&lt;/a&gt;. This versatile tool finds application in developing retrieval-augmented chatbots and other personalized assistants, utilizing technologies like ChatGPT. Furthermore, it enables tasks such as question-answering (GQA), summarization, code comprehension, API interactions, data cleansing, and much more.&lt;/p&gt;
&lt;h2 id="why-is-langchain-becoming-popular"&gt;Why is LangChain becoming popular?&lt;/h2&gt;
&lt;p&gt;The landscape of AI development is rapidly evolving, and LangChain is packed with incredible features for building LLM applications and tools. Developers with a wide range of expertise use LangChain for tasks such as managing interactions with language models, establishing seamless connections between diverse components, and integrating external resources like APIs and databases effortlessly.&lt;/p&gt;
&lt;p&gt;The LangChain platform hosts a diverse collection of APIs seamlessly integrated into applications. This enables developers to incorporate advanced language processing functionalities without the need for laborious construction from scratch.&lt;/p&gt;
&lt;h2 id="langchain-accelerating-devops"&gt;LangChain Accelerating DevOps&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/patrick-debois-langchain-ai-workshop.png"
alt="Patrick Debois at the Seattle Pulumi User Group, with a monitor showing OpenAI LangChain and code in Python" width="100%"&gt;&lt;figcaption&gt;
&lt;p&gt;Patrick Debois presenting the workshop Dev, Sec &amp;amp; Ops meet LangChain&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;LangChain can streamline the integration of large language models and data sources. By helping bring in AI capabilities, DevOps can revolutionize software development life cycles by making it easier for teams to handle challenges while optimizing resource allocation and usage through their cutting-edge, AI-powered solutions. This makes the &lt;a href="https://www.pulumi.com/docs/using-pulumi/automation-api/"&gt;automation of manual processes easier&lt;/a&gt; and the development of new resources far more accessible with faster utilization rates by leveraging artificial intelligence. Significantly important as &amp;ldquo;&lt;a href="https://www.pulumi.com/product/internal-developer-platforms/"&gt;Developer Velocity and Productivity&lt;/a&gt;&amp;rdquo; become business performance metrics across various industries.&lt;/p&gt;
&lt;p&gt;The features included in LangChain include chain interface memory functions for scalability within apps, control over dataflow, as storage – making code quality more reliable, plus seamless machine learning models integration, which allows them to build custom automated AI programs quickly. Thus providing users with an advanced but easy way of creating sophisticated solutions by combining their knowledge of coding and machine training models together.&lt;/p&gt;
&lt;h2 id="how-to-use-langchain-to-learn-llms-and-genai-for-devsecops"&gt;How to Use LangChain to Learn LLMs and GenAI for Dev(Sec)Ops&lt;/h2&gt;
&lt;p&gt;Patrick teaches in the way he would like to be taught. The workshop mainly uses the Langchain framework and basic Python knowledge. OpenAI will also need to be installed. You&amp;rsquo;ll learn essential skills in multiple use cases, applying Langchain for LLM application Development.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/iIl1bQnVwEs?rel=0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;You can see the breakdown of the lessons per role below, and find the code examples in the &lt;a href="https://github.com/jedi4ever/learning-llms-and-genai-for-dev-sec-ops/tree/main"&gt;Learning LLMs and GenAI for DevSecOps repo&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="developer"&gt;Developer&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Calling a simple LLM using OpenAI&lt;/li&gt;
&lt;li&gt;Looking at debugging in Langchain&lt;/li&gt;
&lt;li&gt;Chatting with OpenAI as a model&lt;/li&gt;
&lt;li&gt;Using prompt templates&lt;/li&gt;
&lt;li&gt;Use Docloader to read your local files and prepare them for the LLM&lt;/li&gt;
&lt;li&gt;Explain the calculation and use of embeddings&lt;/li&gt;
&lt;li&gt;Understand how splitting and chunking are important&lt;/li&gt;
&lt;li&gt;Loading embeddings and documents in a vector database&lt;/li&gt;
&lt;li&gt;Use a chain for Questions and Answers to implement the RAG pattern (Retrieval Augmented Generation)&lt;/li&gt;
&lt;li&gt;Show the use of OpenAI documentation to have the llm generate calls to find real-time information&lt;/li&gt;
&lt;li&gt;Implement an Agent and provide it with tools to get more real-time information&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="operations"&gt;Operations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Find out how many tokens you are using and the cost&lt;/li&gt;
&lt;li&gt;How to cache your calls to an LLM using exact matching or embeddings&lt;/li&gt;
&lt;li&gt;How to cache the calculation of embeddings and run the calculation locally&lt;/li&gt;
&lt;li&gt;Run your own local LLM (using Ollama)&lt;/li&gt;
&lt;li&gt;Track your calls and log them to a file (using a callback handler)&lt;/li&gt;
&lt;li&gt;Impose output structure (as JSON) and have the LLM retry if it&amp;rsquo;s not correct&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="security"&gt;Security&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Explain the OWASP top 10 for LLMS&lt;/li&gt;
&lt;li&gt;Show how simple prompt injection works and some mitigation strategies&lt;/li&gt;
&lt;li&gt;How to detect prompt injection using a 3rd party model from Hugging Face&lt;/li&gt;
&lt;li&gt;Detect project injection by using a prompt&lt;/li&gt;
&lt;li&gt;Check the answer LLMs provide and reflect if it is ok&lt;/li&gt;
&lt;li&gt;Use a Hugging Face model to detect if an LLM output was toxic&lt;/li&gt;
&lt;li&gt;Show a simple prompt for asking the LLM&amp;rsquo;s opinion on Kubernetes and Trivy vulnerabilities&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="real-world-applications-of-langchain-ai-in-devops"&gt;Real-World Applications of LangChain AI in DevOps&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/langchain-vector-similarity-search-in-a-rag-application.png"
alt="Vector similarity search in a RAG application" width="90%"&gt;&lt;figcaption&gt;
&lt;p&gt;Vector similarity search in a RAG application. Credit to LangChain and Neo4J team&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;LangChain AI has practical applications in DevOps, particularly related to question-answering using documents as context, extraction, evaluation, and natural language processing tasks, all powered by AI tools. An interesting scenario is implementing &lt;a href="https://blog.langchain.dev/using-a-knowledge-graph-to-implement-a-devops-rag-application/"&gt;a knowledge graph based RAG application with LangChain&lt;/a&gt; to support the DevOps team.&lt;/p&gt;
&lt;p&gt;ChatGPT based on LLMs enabled with these AI tools is one of the foremost implementations at present. Tracking capabilities through LangChain enables developers to determine which prompts are more effective, thus supporting what artificial intelligence offers when integrated into development operations.&lt;/p&gt;
&lt;h2 id="monitoring-and-anomaly-detection-with-langchain-ai"&gt;Monitoring and Anomaly Detection with LangChain AI&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/monitoring-llm-langchain-whylabs.png"
alt="Monitoring LLM performance with LangChain and LangKit" width="90%"&gt;&lt;figcaption&gt;
&lt;p&gt;Monitoring LLM performance with LangChain and LangKit. Credit to WhyLabs&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;LangChain AI provides an efficient approach to DevOps through continuous monitoring, and anomaly detection. LangChain&amp;rsquo;s integration in DevOps allows for intelligent monitoring of various system parameters, application performance metrics, and log files. It can process large volumes of data generated by applications and infrastructure components. Through natural language processing (NLP) techniques, LangChain can interpret logs, error messages, and system alerts in real-time.&lt;/p&gt;
&lt;p&gt;LangChain&amp;rsquo;s AI-driven anomaly detection capabilities are pivotal in identifying deviations from expected behavior in the monitored data. By analyzing historical data patterns, LangChain can learn the normal behavior of the system and its components. When the system encounters an unusual event or behavior, LangChain can promptly detect it as an anomaly. These anomalies could be security threats, performance bottlenecks, code quality, or any irregularities within the system.&lt;/p&gt;
&lt;p&gt;Keep in mind that monitoring LLM performance is important to ensure you can rely on the model&amp;rsquo;s accuracy and relevance in real-world applications. Our customer &lt;a href="https://www.pulumi.com/case-studies/whylabs/"&gt;WhyLabs&lt;/a&gt; has a great article showing the significance of &lt;a href="https://whylabs.ai/blog/posts/monitoring-llm-performance-with-langchain-and-langkit"&gt;monitoring LLMs performance with LangChain and how to get started&lt;/a&gt; with monitoring.&lt;/p&gt;
&lt;h2 id="future-ai-apps-and-devops"&gt;Future: AI, Apps, and DevOps&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/what-is/what-is-platform-engineering/"&gt;Platform engineering&lt;/a&gt;, DevOps, or MLOps may be the next steps in the AI-driven software development future. AI tools are the gateway to giving developers access to big language models that allow them to create highly sophisticated solutions.&lt;/p&gt;
&lt;h3 id="accelerating-development-with-ai-powered-tools"&gt;Accelerating Development with AI-Powered Tools&lt;/h3&gt;
&lt;p&gt;AI-powered tools are revolutionizing the development process, enabling DevOps teams to access intelligent recommendations and improve code quality or obtain suggestions on new ways to accomplish goals. An example is the usage of &lt;a href="https://www.pulumi.com/product/neo/"&gt;Pulumi Neo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Tools like LangChain, Amazon CodeGuru, and Pulumi AI provide actionable insights for efficient resource utilization while enforcing policy and compliance best practices. An &lt;a href="https://www.pulumi.com/blog/pulumi-insights-ai-cli/"&gt;IaC that leverages AI empowers teams&lt;/a&gt; to automate and more effectively manage infrastructure.&lt;/p&gt;
&lt;p&gt;Developers can reap multiple benefits from higher quality assessment of code lines, instantaneous detection of policy and security issues, useful cues on how to resolve problems, and improving code quality, productivity, and time management.&lt;/p&gt;
&lt;p&gt;AI has greatly impacted optimization during the review stage, providing developers with better insights into performance issues, code effectiveness, and &lt;a href="https://www.pulumi.com/blog/property-search/"&gt;resource utilization&lt;/a&gt; without any extra effort.&lt;/p&gt;
&lt;h3 id="security-and-vulnerability-management-with-ai"&gt;Security and Vulnerability Management with AI&lt;/h3&gt;
&lt;p&gt;The marriage of AI, machine learning, and DevOps/DevSecOps practices revolutionizes security and vulnerability management in software engineering. AI-enabled tools technologies not only strengthen the defenses of software systems but also enable organizations to stay one step ahead in ensuring successful deployments by removing manual processes through automation, using single sources of truth, enforcing least-privileged access through role-based access controls, and &lt;a href="https://www.pulumi.com/blog/remediation-policies/"&gt;automated remediation to correct configuration violations&lt;/a&gt; like auto-tagging, removing Internet access, and enabling storage encryption.&lt;/p&gt;
&lt;p&gt;DevSecOps can foster collaboration between developers, security, and operations and still enhance security and compliance best practices using Artificial Intelligence (AI) and Machine Learning techniques.&lt;/p&gt;
&lt;h3 id="leveraging-machine-learning-in-devops"&gt;Leveraging Machine Learning in DevOps&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/generative-ai-apps-devops-talks-pulumi-user-group/ml-dev-ops-cycle.png" alt="MLOps (Machine Learning Operations) meets DevOps Infinity Loop as part of the software development lifecycle" title="MLOps (Machine Learning Operations) meets DevOps Infinity Loop"&gt;&lt;/p&gt;
&lt;p&gt;Applying Machine Learning in DevOps is a game-changer. Not only does it automate and enhance various processes but also brings an intelligent, predictive, and adaptive dimension to development workflows. By embracing these technologies, development teams can achieve higher efficiency, improve code quality, optimize performance, and seamlessly integrate with artificial intelligence, thereby shaping the future of software engineering.&lt;/p&gt;
&lt;p&gt;ML models can evaluate past outcomes from compilation/builds plus performance metrics within an operation so that developers may leverage these insights when writing code accordingly with machine learning algorithms, allowing them to identify vulnerabilities or issues faster through root cause analysis while ameliorating system security against outside attacks like hackers or DDOS activity thus boosting overall system productivity thanks to AI implementation into existing processes.&lt;/p&gt;
&lt;h3 id="implementing-ai-models-into-devops-key-considerations"&gt;Implementing AI models into DevOps: Key Considerations&lt;/h3&gt;
&lt;p&gt;When considering AI, it&amp;rsquo;s essential to assess data quality, integration complexity, cost savings, and ethical/legal considerations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensuring the data used for training these AI tools are precise, exhaustive, and up-to-date will help gain more reliable results.&lt;/li&gt;
&lt;li&gt;An important aspect of the implementation process is integrating existing DevOps instruments and guaranteeing scalability, depending on the company&amp;rsquo;s necessities.&lt;/li&gt;
&lt;li&gt;Ensuring that all laws applicable when working with such technology.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;AI and ML are rapidly becoming a necessity, which can greatly enhance and benefit Development, Security, and Operation teams. At first glance, it may appear daunting or out of reach, but tools like Langchain and Pulumi can make it easier to get started. Follow the Pulumi Python + AI/ML series to upskill and get ahead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/mlops-the-ai-challenge-is-cloud-not-code/"&gt;The Real AI challenge is Cloud, not Code!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/mlops-huggingface-llm-aws-sagemaker-python/"&gt;Deploy AI Models on Amazon SageMaker using Pulumi Python IaC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/deploy-ai-ml-vercel-app/"&gt;Deploying Your AI/ML Chatbot Frontend To Vercel Using Pulumi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="frequently-asked-questions"&gt;Frequently Asked Questions&lt;/h2&gt;
&lt;h3 id="who-is-patrick-debois"&gt;Who is Patrick Debois?&lt;/h3&gt;
&lt;p&gt;Patrick Debois is a highly accomplished technologist whose expertise spans the domains of Development (Dev), Security (Sec), and Operations (Ops). Acknowledged as a respected confidant within the developer, security, and operations communities, Patrick is presently deeply engrossed in the realm of Artificial Intelligence and Machine Learning, continually pushing the boundaries of his technical acumen.&lt;/p&gt;
&lt;p&gt;He was the organizer of the inaugural DevOpsDays in 2009. He is credited with coining &amp;ldquo;DevOps&amp;rdquo; and co-authoring the renowned DevOps Handbook. In the past, Patrick has collaborated with esteemed technology organizations such as &lt;a href="https://www.pulumi.com/case-studies/atlassian/"&gt;Atlassian&lt;/a&gt; and Snyk. Currently, he wears dual hats as the Vice President of Engineering and a Distinguished Engineer at Showpad.&lt;/p&gt;
&lt;h3 id="is-langchain-free"&gt;Is LangChain Free?&lt;/h3&gt;
&lt;p&gt;Yes, LangChain is an open-source framework, free to use and modify.&lt;/p&gt;
&lt;h3 id="how-does-langchain-work"&gt;How Does LangChain Work?&lt;/h3&gt;
&lt;p&gt;LangChain is a meticulously designed open-source framework tailored to simplify the creation of applications powered by large language models (LLMs). First, you will need to have a language model or you can use a public language model or train your own.&lt;/p&gt;
&lt;p&gt;Once you have a language model ready, you can start building applications. The framework offers an array of tools and APIs, simplifying the process of connecting language models to diverse data sources, interacting with their environment, and constructing intricate applications.&lt;/p&gt;
&lt;p&gt;LangChain operates by linking together various components, referred to as links, to establish a workflow. Each link in the chain performs a task and is connected in a sequence, enabling the output of one link to serve as the input for the subsequent one. This sequential linking allows the chain to execute intricate tasks by combining simple tasks seamlessly.&lt;/p&gt;
&lt;p&gt;See &lt;a href="https://python.langchain.com/docs/get_started/introduction"&gt;LangChain&amp;rsquo;s get started&lt;/a&gt; docs for more information.&lt;/p&gt;
&lt;h3 id="does-chatgpt-use-langchain"&gt;Does ChatGPT use LangChain?&lt;/h3&gt;
&lt;p&gt;ChatGPT utilizes LangChain to create custom artificial intelligence (AI). This allows for an individualized experience.&lt;/p&gt;</description><author>Sara Huddleston</author><author>Kat Morgan</author><category>pulumi-events</category><category>community</category><category>ai</category><category>devops</category><category>langchain</category><category>ml</category></item><item><title>Converting Bicep code to Pulumi</title><link>https://www.pulumi.com/blog/converting-bicep-to-pulumi/</link><pubDate>Fri, 29 Sep 2023 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/converting-bicep-to-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/converting-bicep-to-pulumi/index.png" /&gt;
&lt;p&gt;Bicep is a DSL developed by Microsoft to simplify the authoring of &lt;a href="https://www.pulumi.com/docs/iac/adopting-pulumi/migrating-to-pulumi/from-arm/"&gt;ARM templates&lt;/a&gt; and deploy resources to Azure. Today I will be sharing with you a new Pulumi converter plugin that I have been working on that converts Bicep code to any of the supported Pulumi languages.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/VNj1EyQ7wug?rel=0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep"&gt;Bicep&lt;/a&gt; language is a domain-specific language for Infrastructure-As-Code built by Microsoft that uses declarative syntax to deploy Azure resources. It was made to address the shortcomings of authoring ARM templates that are written in JSON. Compared to ARM templates, Bicep is much more concise and readable. However, here at Pulumi we believe that using general-purpose programming languages that are familiar to developers is the best way to author Infrastructure-As-Code. Besides being able to use a general-purpose language, you and your team can benefit from using Pulumi Cloud as you scale to larger projects and teams with features such as &lt;a href="https://www.pulumi.com/blog/pulumi-deployments/"&gt;Pulumi Deployments&lt;/a&gt;, &lt;a href="https://www.pulumi.com/blog/organization-access-tokens/"&gt;Organization Access Tokens&lt;/a&gt;, &lt;a href="https://www.pulumi.com/blog/review-stacks/"&gt;Review Stacks&lt;/a&gt; and more.&lt;/p&gt;
&lt;p&gt;That is why I have been working on a new &lt;a href="https://github.com/pulumi/pulumi-converter-bicep"&gt;Pulumi converter plugin&lt;/a&gt; that converts Bicep code to any of the supported Pulumi languages using the Pulumi CLI via &lt;code&gt;pulumi convert&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="installing-and-using-the-plugin"&gt;Installing and using the plugin&lt;/h2&gt;
&lt;p&gt;First, install the &lt;em&gt;converter&lt;/em&gt; plugin using the Pulumi 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;pulumi plugin install converter bicep
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will install the plugin from the latest GitHub release of the &lt;a href="https://github.com/pulumi/pulumi-converter-bicep"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once installed, you can use it to convert Bicep code to Pulumi. For example, let&amp;rsquo;s say we have the following Bicep code in a file called &lt;code&gt;example.bicep&lt;/code&gt; which deploys a storage account:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bicep" data-lang="bicep"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;storage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Microsoft.Storage/storageAccounts@2022-09-01&amp;#39;&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="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="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;storageaccount&amp;#39;&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="nv"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;resourceGroup&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nv"&gt;location&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="nv"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;StorageV2&amp;#39;&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="nv"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;:&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="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Standard_LRS&amp;#39;&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="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="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;Navigate to the directory where the file is located and run the following command. For example to generate TypeScript code from the Bicep code above, 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;pulumi convert --from bicep --language typescript --out output -- --entry example.bicep
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will use the Bicep converter plugin we just installed to convert the code in &lt;code&gt;example.bicep&lt;/code&gt; to TypeScript and place the generated code in the &lt;code&gt;output&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;You can change &lt;code&gt;typescript&lt;/code&gt; to any of the supported languages such as &lt;code&gt;csharp&lt;/code&gt;, &lt;code&gt;python&lt;/code&gt;, &lt;code&gt;go&lt;/code&gt;, &lt;code&gt;java&lt;/code&gt; or &lt;code&gt;yaml&lt;/code&gt; and you are good to go.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t specify the &lt;code&gt;--entry&lt;/code&gt; flag, the plugin will look for the first file with the &lt;code&gt;.bicep&lt;/code&gt; extension in the current directory.&lt;/p&gt;
&lt;p&gt;Let us look at the generated code from the example above:&lt;/p&gt;
&lt;div&gt;
&lt;pulumi-chooser type="language" options="typescript,python,go,csharp,java,yaml" mode=""&gt;
&lt;div&gt;
&lt;pulumi-choosable type="language" values="typescript" mode=""&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;pulumi&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/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="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;azure_native&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/azure-native&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;config&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;Config&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="c1"&gt;// The name of the resource group to operate on
&lt;/span&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;resourceGroupName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resourceGroupName&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;const&lt;/span&gt; &lt;span class="nx"&gt;currentResourceGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azure_native&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getResourceGroupOutput&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;resourceGroupName&lt;/span&gt;: &lt;span class="kt"&gt;resourceGroupName&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storage&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;azure_native&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StorageAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;storageaccount&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;accountName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;storageaccount&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;kind&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;StorageV2&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;location&lt;/span&gt;: &lt;span class="kt"&gt;currentResourceGroup.apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentResourceGroup&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&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;resourceGroupName&lt;/span&gt;: &lt;span class="kt"&gt;currentResourceGroup.apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentResourceGroup&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;sku&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Standard_LRS&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&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;/pulumi-choosable&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pulumi-choosable type="language" values="python" mode=""&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pulumi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pulumi_azure_native&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;azure_native&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="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&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="c1"&gt;# The name of the resource group to operate on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;resource_group_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resourceGroupName&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="n"&gt;current_resource_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;azure_native&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_resource_group_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_group_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resource_group_name&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="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;azure_native&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StorageAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;storageaccount&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="n"&gt;account_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;storageaccount&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="n"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;StorageV2&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="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_resource_group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&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="n"&gt;resource_group_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_resource_group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&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="n"&gt;sku&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;azure_native&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SkuArgs&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="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Standard_LRS&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/pulumi-choosable&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pulumi-choosable type="language" values="go" mode=""&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="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&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="kn"&gt;import&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="s"&gt;&amp;#34;github.com/pulumi/pulumi-azure-native-sdk/v2/resources&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="s"&gt;&amp;#34;github.com/pulumi/pulumi-azure-native-sdk/v2/storage&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="s"&gt;&amp;#34;github.com/pulumi/pulumi/sdk/v3/go/pulumi&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="s"&gt;&amp;#34;github.com/pulumi/pulumi/sdk/v3/go/pulumi/config&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;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;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&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;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&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;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&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;cfg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&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;// The name of the resource group to operate 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;resourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;resourceGroupName&amp;#34;&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;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LookupResourceGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LookupResourceGroupArgs&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;ResourceGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resourceGroupName&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="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="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;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewStorageAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;storageaccount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StorageAccountArgs&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;AccountName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;storageaccount&amp;#34;&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;Kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;StorageV2&amp;#34;&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;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Location&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;ResourceGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&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;Sku&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SkuArgs&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;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Standard_LRS&amp;#34;&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="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="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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="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="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;/pulumi-choosable&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pulumi-choosable type="language" values="csharp" mode=""&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Linq&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pulumi&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AzureNative&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AzureNative&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Deployment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Config&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="c1"&gt;// The name of the resource group to operate on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resourceGroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;resourceGroupName&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;currentResourceGroup&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AzureNative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&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="n"&gt;ResourceGroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resourceGroupName&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AzureNative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StorageAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;storageaccount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&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="n"&gt;AccountName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;storageaccount&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="n"&gt;Kind&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;StorageV2&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="n"&gt;Location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getResourceGroupResult&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getResourceGroupResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Location&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="n"&gt;ResourceGroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getResourceGroupResult&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getResourceGroupResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&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="n"&gt;Sku&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AzureNative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SkuArgs&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="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Standard_LRS&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&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="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/pulumi-choosable&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pulumi-choosable type="language" values="java" mode=""&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;generated_program&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.pulumi.Context&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.pulumi.Pulumi&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.pulumi.core.Output&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.pulumi.azurenative.resources.ResourcesFunctions&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.pulumi.azurenative.resources.inputs.GetResourceGroupArgs&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.pulumi.azurenative.storage.StorageAccount&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.pulumi.azurenative.storage.StorageAccountArgs&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.pulumi.azurenative.storage.inputs.SkuArgs&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.List&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.ArrayList&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.Map&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.io.File&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.nio.file.Files&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="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.nio.file.Paths&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&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="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;Pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;stack&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="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="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&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="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;config&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="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;resourceGroupName&amp;#34;&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="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ResourcesFunctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResourceGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetResourceGroupArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&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&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="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StorageAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;storage&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StorageAccountArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;storageaccount&amp;#34;&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;StorageV2&amp;#34;&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;applyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getResourceGroupResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getResourceGroupResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentResourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;applyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getResourceGroupResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getResourceGroupResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SkuArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Standard_LRS&amp;#34;&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&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="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&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&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="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="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;/pulumi-choosable&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pulumi-choosable type="language" values="yaml" mode=""&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;configuration&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="nt"&gt;resourceGroupName&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="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;string&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="nt"&gt;resources&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="nt"&gt;storageaccount&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="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;azure-native:storage:StorageAccount&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="nt"&gt;properties&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="nt"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;storageaccount&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="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;StorageV2&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="nt"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${currentResourceGroup.location}&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="nt"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${currentResourceGroup.name}&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="nt"&gt;sku&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Standard_LRS&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="nt"&gt;variables&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="nt"&gt;currentResourceGroup&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="nt"&gt;fn::invoke&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="nt"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;azure-native:resources:getResourceGroup&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="nt"&gt;Arguments&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="nt"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${resourceGroupName}&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;/pulumi-choosable&gt;
&lt;/div&gt;
&lt;/pulumi-chooser&gt;
&lt;/div&gt;
&lt;p&gt;Notice how the in the original Bicep code, we reference the location of the &lt;em&gt;implicit&lt;/em&gt; resource group via &lt;code&gt;resourceGroup().location&lt;/code&gt;. When the converter generates code for the target language, it replaces this &lt;em&gt;implicit&lt;/em&gt; resource group with an explicit one by parameterizing the program by a resource group name and using that resource group when deploying the resources.&lt;/p&gt;
&lt;h2 id="early-days-for-the-converter"&gt;Early days for the converter&lt;/h2&gt;
&lt;p&gt;Currently the converter supports most of the features of Bicep such as parameters, variables, modules, resources, outputs and a number of built-in functions. That said, the converter plugin is still in its early days and it&amp;rsquo;s not yet tested against many real-world Bicep programs. I am actively working on improving it and making it support most of the Bicep language features. The source code is available on this &lt;a href="https://github.com/pulumi/pulumi-converter-bicep"&gt;GitHub repository&lt;/a&gt;. If you find any issues or have any feedback, please open an issue on the repository.&lt;/p&gt;
&lt;h2 id="how-does-it-work"&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;The converter plugin is a native binary built using .NET in &lt;a href="https://dotnet.microsoft.com/en-us/languages/fsharp"&gt;F#&lt;/a&gt; and new Pulumi Converter SDK. Using the core &lt;a href="https://www.nuget.org/packages/Pulumi"&gt;Pulumi&lt;/a&gt; nuget package, we shipped an experimental API that allows you to easily build converter plugins that can extend the functionality of &lt;code&gt;pulumi convert&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Building a language converter plugin involves taking the source language, in this case Bicep, and converting it to our internal Pulumi Configuration Language, or PCL for short. The converter implements a function of the following shape:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fsharp" data-lang="fsharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;convertProgram&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ConvertProgramRequest&lt;/span&gt;&lt;span class="o"&gt;):&lt;/span&gt; &lt;span class="n"&gt;ConvertProgramResponse&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="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sourceDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SourceDirectory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;targetDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TargetDirectory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// conversion logic here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In our case, we read the Bicep code from the source directory, convert it to PCL and write out the PCL code in the target directory. Then, Pulumi uses the built-in program generation facilities to take care of the rest of the work of generating the target language code from the PCL code so that we don&amp;rsquo;t have to worry about language-specific details. The converter only needs to know how to generate PCL.&lt;/p&gt;
&lt;p&gt;To build the actual transformation from Bicep to PCL, I made use the of the &lt;a href="https://www.nuget.org/packages/Azure.Bicep.Core"&gt;Azure.Bicep.Core&lt;/a&gt; package available for .NET which allowed me to parse the Bicep code and generate a typed Abstract Syntax Tree (AST) from it. Building an AST from the source language allows us to easily traverse the tree, analyze it and symbolically rewrite pieces of it. Working at the AST level also makes it easy to test source code transformation using structure rather than text. Once we have obtained the Bicep AST, we transform it into a Pulumi AST that represents a PCL program. Finally, we print out the Pulumi AST to a string and write it to the target directory.&lt;/p&gt;
&lt;p&gt;If you are curious about the specification of the AST that represents Bicep programs, head over to &lt;a href="https://github.com/Zaid-Ajaj/pulumi-converter-bicep/blob/master/src/Converter/BicepParser.fs"&gt;this file&lt;/a&gt; from the source code. You will find types such as &lt;code&gt;BicepProgram&lt;/code&gt; and &lt;code&gt;BicepSyntax&lt;/code&gt; that model almost every aspect of Bicep code. As for the AST of PCL programs, you can find the type definitions &lt;a href="https://github.com/Zaid-Ajaj/pulumi-converter-bicep/blob/master/src/Converter/PulumiTypes.fs"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The F# language is great for almost everything, but is especially amazing for writing language converters because of its powerful pattern-matching capabilities and the ability to write code in a functional style. Many of us working at Pulumi are big fans of F#!&lt;/p&gt;
&lt;p&gt;Here is a snippet of the transformation pipeline written in F# that takes the input Bicep program and converts it to the final PCL text&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fsharp" data-lang="fsharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;bicepProgram&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;BicepProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropResourceUnknowns&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;BicepProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduceScopeParameter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;BicepProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameterizeByTenantAndSubscriptionId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;BicepProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addResourceGroupNameParameterToModules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;BicepProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameterizeByResourceGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bicepProgramToPulumi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modifyComponentPaths&lt;/span&gt; &lt;span class="n"&gt;rewriteComponentPath&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Printer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printProgram&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can learn more on the implementation of these functions from the source code on GitHub. I&amp;rsquo;ve written it in a way that is easy to follow, understand and contribute to. Most of these functions are fully &lt;a href="https://github.com/Zaid-Ajaj/pulumi-converter-bicep/blob/master/src/Tests/Program.fs"&gt;unit-tested&lt;/a&gt; and I am working on adding more tests to cover more of the Bicep language features.&lt;/p&gt;
&lt;h2 id="bonus-converter-arm-to-pulumi"&gt;Bonus converter: ARM to Pulumi&lt;/h2&gt;
&lt;p&gt;Those who are familiar with the Bicep CLI, know that it has a built-in &lt;em&gt;decompiler&lt;/em&gt; that converts ARM templates to Bicep. I thought it would be fun to build another converter that takes ARM templates to Pulumi but without writing any conversion logic for it. Instead, I would decompile the ARM template to Bicep, then use the logic from the Bicep converter to generate the final PCL code. Fortunately, the decompiler code is embedded in the nuget package &lt;a href="https://www.nuget.org/packages/Azure.Bicep.Core"&gt;Azure.Bicep.Core&lt;/a&gt; so we don&amp;rsquo;t have to rely on the existence of the Bicep CLI in order to make this converter work.&lt;/p&gt;
&lt;p&gt;From all of this, I created another converter &lt;code&gt;pulumi-converter-arm&lt;/code&gt; that works exactly like the Bicep converter but takes ARM templates as input. Head over to the &lt;a href="https://github.com/Zaid-Ajaj/pulumi-converter-arm"&gt;repository&lt;/a&gt; to learn more about how to install it and how to use it.&lt;/p&gt;
&lt;p&gt;Depending on how well this ARM converter does its job, we might consider deprecating &lt;a href="https://www.pulumi.com/arm2pulumi/"&gt;arm2pulumi&lt;/a&gt; in favor of it. However, we first need to test it against more templates and iron out the rough edges. It would be the ideal situation because there are virtually zero maintenance costs, any improvements to the Bicep converter would automatically get picked up here. Also, improvements to the decompiler from the &lt;a href="https://www.nuget.org/packages/Azure.Bicep.Core"&gt;Azure.Bicep.Core&lt;/a&gt; nuget package would benefit the converter as well.&lt;/p&gt;
&lt;h2 id="building-your-own-pulumi-language-converter"&gt;Building your own Pulumi language converter&lt;/h2&gt;
&lt;p&gt;Converter plugins are a great way to extend the Pulumi CLI with &lt;code&gt;pulumi convert&lt;/code&gt; to support new languages. When we shipped support for &lt;a href="https://www.pulumi.com/blog/converting-full-terraform-programs-to-pulumi/"&gt;Converting full Terraform Programs To Pulumi&lt;/a&gt;, we extended the &lt;code&gt;pulumi convert&lt;/code&gt; command to allow installing and using converter plugins that are shipped independently from the Pulumi CLI.&lt;/p&gt;
&lt;p&gt;In essence, those plugins are executable binaries that can be written in any language, which serves up a gRPC server that implements the &lt;code&gt;Converter&lt;/code&gt; contract:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-protobuf" data-lang="protobuf"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Converter is a service for converting between other ecosystems and Pulumi.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// This is currently unstable and experimental.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;Converter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&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;// ConvertState converts state from the target ecosystem into a form that can be imported into Pulumi.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;ConvertState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConvertStateRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConvertStateResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&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;// ConvertProgram converts a program from the target ecosystem into a form that can be used with Pulumi.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;ConvertProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConvertProgramRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConvertProgramResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="err"&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="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This Converter interface is considered experimental and is subject to change in the future.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, learning how to setup gRPC servers and implement the &lt;code&gt;Converter&lt;/code&gt; contract is not an easy task unless you are familiar with it. For .NET, we shipped the &lt;em&gt;experimental&lt;/em&gt; Pulumi converter SDK to the main Pulumi nuget package that makes it extremely easy to build converter plugins.&lt;/p&gt;
&lt;p&gt;A bare-bones converter plugin that does nothing looks like this in F#:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fsharp" data-lang="fsharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Pulumi.Experimental.Converter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Pulumi.Codegen&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;let&lt;/span&gt; &lt;span class="nv"&gt;convertProgram&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ConvertProgramRequest&lt;/span&gt;&lt;span class="o"&gt;):&lt;/span&gt; &lt;span class="n"&gt;ConvertProgramResponse&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="c1"&gt;// TODO: Implement the conversion logic here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;ConvertProgramResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&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="n"&gt;convertProgram&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateSimple&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serve&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Async&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AwaitTask&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Async&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunSynchronously&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will spin up a gRPC server that implements the &lt;code&gt;Converter&lt;/code&gt; contract and will serve it on a random port assigned during startup. The Pulumi converter SDK automatically implements gRPC reflection which allows tools such as &lt;a href="https://www.postman.com/"&gt;Postman&lt;/a&gt; to discover the available gRPC services and methods and send example requests to them. See &lt;a href="https://blog.postman.com/postman-now-supports-grpc/"&gt;Postman with gRPC&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="publishing-converter-plugins"&gt;Publishing converter plugins&lt;/h2&gt;
&lt;p&gt;The easiest way to make your converter plugin available to others is to publish it on GitHub releases. The Pulumi CLI expects a naming convention &lt;code&gt;pulumi-converter-&amp;lt;name&amp;gt;&lt;/code&gt; and will automatically download the plugin from the latest GitHub release of the repository when given the &lt;code&gt;--server&lt;/code&gt; flag during &lt;code&gt;pulumi plugin install converter &amp;lt;name&amp;gt; --server &amp;lt;url&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Converter plugins written in .NET are simple console applications that are compiled to native binaries for each target platform. To see how this is done, refer to this &lt;a href="https://github.com/Zaid-Ajaj/pulumi-converter-bicep/blob/master/build/Program.fs#L197"&gt;createAndPublishArtifacts&lt;/a&gt; function written for the Bicep converter that does the heavy lifting of building the native binaries for each platform, putting them in an archive then publishing them to GitHub releases.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: when publishing native binaries for macOS, the Github Action executing the publishing process must be using a macOS runner so that the generated binary from &lt;code&gt;dotnet publish&lt;/code&gt; gets &lt;em&gt;signed&lt;/em&gt; with a developer certificate. This is required for the binary to be executable on macOS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="final-thoughts"&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;I hope you enjoyed this article and learned something new about the Pulumi converter plugins and Pulumi internals. I am very excited about the Bicep converter plugin and I am looking forward to hearing your feedback on it from the community. It was a lot of fun writing this in F# and made writing complex pieces of code very trivial.&lt;/p&gt;
&lt;p&gt;If you have any questions or feedback, don&amp;rsquo;t hesitate to reach out!&lt;/p&gt;</description><author>Zaid Ajaj</author><category>community</category><category>bicep</category><category>azure</category><category>convert</category></item><item><title>How Starburst Data Creates Infrastructure Automation Magic With Code</title><link>https://www.pulumi.com/blog/how-starburst-data-creates-infrastructure-automation-magic-with-code/</link><pubDate>Wed, 23 Aug 2023 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-starburst-data-creates-infrastructure-automation-magic-with-code/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-starburst-data-creates-infrastructure-automation-magic-with-code/index.png" /&gt;
&lt;div class="note note-info"&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.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;This blog post summarizes a presentation by Matt Stephenson at &lt;a href="https://www.pulumi.com/pulumi-up/"&gt;PulumiUP 2023&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/mattstep"&gt;Matt Stephenson&lt;/a&gt; is Senior Principal Software Engineer for &lt;a href="https://www.starburst.io"&gt;Starburst Data&lt;/a&gt; and a &lt;a href="https://www.pulumi.com/community/puluminaries/"&gt;Puluminary&lt;/a&gt; member. He’s deeply involved in the &lt;a href="https://www.pulumi.com/what-is/what-is-infrastructure-as-code/"&gt;Infrastructure as Code (IaC)&lt;/a&gt; space, having contributed to Ansible, been a core contributor to Apache jclouds, and has written many Terraform plugins. He leads infrastructure architecture at Starburst and originally introduced Pulumi to the company. Starburst provides a data lake analytics platform that’s powered by Trino - an open-source distributed SQL query engine designed for running fast analytic queries across large datasets in multiple data sources. At Starburst, Matt helped revamp and improve how the company manages its multi-cloud and cloud native infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/t-oSFZuNqXQ?rel=0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
Watch Matt Stephenson&amp;rsquo;s full presentation with demos. 8:03 demo of CI/CD. 15:33 demo of infrastructure automation.&lt;/p&gt;
&lt;h2 id="technical-and-business-problems-to-solve"&gt;Technical and Business Problems to Solve&lt;/h2&gt;
&lt;p&gt;Starburst operates a managed Trino service that runs on Kubernetes clusters on multiple clouds. Their Kubernetes clusters needed frequent manual scaling based on workloads. Furthermore, upgrading Trino versions running on the clusters ran the risk of potential service disruptions for customers. The company had been using tools like &lt;a href="https://www.pulumi.com/docs/iac/comparisons/terraform/"&gt;Terraform&lt;/a&gt; for IaC, Argo CD for deploying Helm charts, Maven, Docker containers, and a Slack bot for infrastructure and deployments. Their CI/CD workflow was difficult to maintain because they needed to create wrappers for their previous IaC tooling, making it less approachable in CI/CD environments. The pipeline was tenuously bound together using GitHub Actions and a Slack bot, requiring specialized GHA runners, leading to a messy architecture.&lt;/p&gt;
&lt;h2 id="why-starburst-chose-pulumi"&gt;Why Starburst Chose Pulumi&lt;/h2&gt;
&lt;p&gt;Recognizing opportunities for improvement, Matt brought in Pulumi and its &lt;a href="https://www.pulumi.com/automation"&gt;Automation API&lt;/a&gt;. This unified Starburst&amp;rsquo;s software development and infrastructure management practices, especially when deploying and managing Trino clusters on &lt;a href="https://www.pulumi.com/kubernetes"&gt;Kubernetes&lt;/a&gt;. The top two reasons Matt chose Pulumi were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Support for programming languages:&lt;/strong&gt; Pulumi supports the main languages used by Starburst&amp;rsquo;s developers, Java and TypeScript. This enables all engineers to write infrastructure code and promotes a cohesive application and infrastructure codebase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pulumi Automation API:&lt;/strong&gt; Automation API is a programmatic interface for the Pulumi CLI and Pulumi IaC engine, which enables Starburst engineers to build &lt;a href="https://github.com/pulumi/automation-api-examples"&gt;infrastructure automation&lt;/a&gt; into their applications and CI/CD processes, and orchestrate complex provisioning workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="benefits-of-using-pulumi"&gt;Benefits of Using Pulumi&lt;/h2&gt;
&lt;p&gt;Adopting Pulumi and its Automation API provided several benefits that made Starburst&amp;rsquo;s engineers more efficient and productive, ultimately leading to faster and more reliable releases for Starburst&amp;rsquo;s customers. Matt summed up the benefits of Pulumi for Starburst’s engineers: &amp;ldquo;When I think about building an airplane, I think about precision and having the right tools for the job.&amp;rdquo; Pulumi, for Starburst, epitomized this precision and efficiency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Increased deployment speeds by 3x for new customer releases:&lt;/strong&gt; Starburst engineers used the Pulumi Automation API to triple the deployment speed of multi-cloud Kubernetes clusters with blue-green deployments that complete within minutes. They did this by orchestrating blue-green updates across their data plane running on the clusters, ensuring zero customer downtime. They built intricate integrations between Pulumi and internal APIs that enabled them to seamlessly transition customers to new clusters. Now they can update their customers’ runtimes, including VPCs and Kubernetes clusters, twice a week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unified cluster and infrastructure management:&lt;/strong&gt; Rather than juggling multiple tools for managing Kubernetes and infrastructure, Starburst engineers use a single tool to manage over 50 &lt;a href="https://www.pulumi.com/templates/kubernetes/"&gt;Kubernetes clusters&lt;/a&gt; and &lt;a href="https://www.pulumi.com/templates/kubernetes-application/helm-chart/"&gt;deploy Helm charts&lt;/a&gt; into the clusters, spanning multiple regions. They also use Pulumi to manage SaaS platforms like Cloudflare, Confluent, and DataDog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consistent deployment lifecycles:&lt;/strong&gt; Using Pulumi’s &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/github-actions/"&gt;integration with GitHub Actions&lt;/a&gt;, Starburst engineers built a streamlined CI/CD process where infrastructure and application code builds within the same pipelines with automated test environments. They accomplished this using Pulumi Automation API to abstract the underlying runtime environments, circumventing the need for additional layers and wrappers.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/uploads/content/blog/how-starburst-data-creates-infrastructure-automation-magic-with-code/starburst-code.png" alt="Infrastructure automation code"&gt;&lt;/p&gt;
&lt;h2 id="try-pulumi-for-infrastructure-as-code-automation"&gt;Try Pulumi for Infrastructure as Code Automation&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://app.pulumi.com/signup"&gt;Sign up for a free account&lt;/a&gt; to try deploying infrastructure on any cloud with &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/deployments/"&gt;Pulumi Deployments&lt;/a&gt;, or &lt;a href="https://www.pulumi.com/resources/#upcoming"&gt;register for an upcoming workshop&lt;/a&gt; to learn more about how Pulumi can help you ship cloud infrastructure faster and more safely.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/case-studies/"&gt;Go to more case studies →&lt;/a&gt;&lt;/p&gt;</description><author>George Huang</author><category>case-studies</category><category>kubernetes</category><category>cloud-native</category><category>automation-api</category><category>community</category><category>pulumi-events</category></item><item><title>Join a Pulumi User Group (PUG) Meetup!</title><link>https://www.pulumi.com/blog/join-pulumi-user-group-community/</link><pubDate>Mon, 01 May 2023 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/join-pulumi-user-group-community/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/join-pulumi-user-group-community/index.png" /&gt;
&lt;div class="note note-info"&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.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;Hi everyone,&lt;/p&gt;
&lt;p&gt;After several great years on Meetup, the Pulumi Community is moving to a new home: &lt;a href="https://luma.com/pulumi"&gt;Luma&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This wasn&amp;rsquo;t an easy decision. Together we&amp;rsquo;ve built 38 Pulumi User Groups across 14 countries, with nearly 10,000 members between them. Meetup is where that community took shape, and we&amp;rsquo;re grateful for everything that happened there.&lt;/p&gt;
&lt;p&gt;We want to make it as easy as possible for you to show up. All upcoming events and workshops will be published on Luma going forward.&lt;/p&gt;
&lt;p&gt;Follow us at &lt;a href="https://luma.com/pulumi"&gt;luma.com/pulumi&lt;/a&gt; so you don&amp;rsquo;t miss out.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We have a global community made up of people from many different countries, but we also have many local communities. We want to enable our users to meet, share knowledge and ideas, answer questions, and learn about Pulumi and best practices. For that reason, we created the &lt;a href="https://luma.com/pulumi"&gt;Pulumi User Groups, aka PUGs&lt;/a&gt; on Meetup.&lt;/p&gt;
&lt;p&gt;Members of PUGs will have access to a global network of experts who can offer help, advice, and support on using Pulumi effectively. The PUGs will also feature regular in-person meetups, webinars, in-person and virtual workshops, and other events to help members stay up-to-date with the latest cloud infrastructure and Pulumi developments.&lt;/p&gt;
&lt;h2 id="23-pulumi-user-groups-pugs-meetups-in-10-countries"&gt;23 Pulumi User Groups (PUGs) Meetups in 10 countries&lt;/h2&gt;
&lt;p&gt;PUGs are led by Pulumi employees and &lt;a href="https://www.pulumi.com/community/puluminaries/"&gt;Puluminaries&lt;/a&gt;, who are passionate community members that are experts in topics related to cloud infrastructure, Pulumi Cloud, cloud-native software development, and much more, and are willing to share their knowledge with the community.&lt;/p&gt;
&lt;h3 id="united-states-pulumi-user-groups"&gt;United States Pulumi User Groups&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Seattle Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;New York Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;San Francisco Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;San Diego Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Austin Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Denver Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Chicago Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Columbus Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Philadelphia Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Boston Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Detroit Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Nashville Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="south-america-pulumi-user-groups"&gt;South America Pulumi User Groups&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;São Paulo Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="european-pulumi-user-groups"&gt;European Pulumi User Groups&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;London Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Oslo Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Berlin Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Munich Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Prague Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="middle-east-pulumi-user-groups"&gt;Middle East Pulumi User Groups&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Tel Aviv Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="asia-pulumi-user-groups"&gt;Asia Pulumi User Groups&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Singapore Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="oceania-pulumi-user-groups"&gt;Oceania Pulumi User Groups&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Australia Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://luma.com/pulumi"&gt;Auckland Pulumi User Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-first-pug-in-person"&gt;The First PUG in person&lt;/h2&gt;
&lt;p&gt;Our very first in-person PUG meetup took place in London, UK. The event was well-attended by developers, cloud architects, and other infrastructure enthusiasts. The attendees had the opportunity to network with other like-minded professionals and learn more about Pulumi&amp;rsquo;s tools and features from expert speakers.&lt;/p&gt;
&lt;p&gt;During the meetup, attendees heard from several speakers, including Joe Duffy, Co-Founder &amp;amp; CEO at Pulumi, who was there to speak about Infrastructure as Code that writes itself after the release of &lt;a href="https://www.pulumi.com/blog/pulumi-insights/"&gt;Pulumi Insights&lt;/a&gt;, which happened that same day.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/join-pulumi-user-group-community/pulumi-user-group-pug-london-joe-duffy.png" alt="Photo of Pulumi CEO Joe Duffy speaking at a Pulumi User Group (PUG) in London, UK"&gt;&lt;/p&gt;
&lt;p&gt;Other speakers included Liam Hampton, Sr. Regional Cloud Advocate at Microsoft, who spoke about Microsoft Azure and IaC, and Sam Cogan, Director of Solution Architecture at Willis Towers Watson, who spoke about building a library of reusable modules you can repeatedly reuse in your IaC projects.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/join-pulumi-user-group-community/pulumi-user-group-pug-london-sam-cogan.png" alt="Photo of Sam Cogan speaking at a Pulumi User Group (PUG) in London, UK"&gt;&lt;/p&gt;
&lt;p&gt;Overall, the first PUG meetup was a huge success, and attendees left feeling inspired and energized about Pulumi&amp;rsquo;s tools and the community&amp;rsquo;s possibilities. With the launch of PUGs, Pulumi is demonstrating its commitment to building a strong and supportive community of professionals passionate about cloud infrastructure.&lt;/p&gt;
&lt;h2 id="more-pugs-and-events-to-come"&gt;More PUGs and events to come&lt;/h2&gt;
&lt;p&gt;We are planning more in-person PUG events for the months to come. With PUGs, you&amp;rsquo;ll have the opportunity to connect with like-minded professionals, share knowledge and best practices, and stay up-to-date with the latest developments in cloud infrastructure and Pulumi.&lt;/p&gt;
&lt;p&gt;Join the &lt;a href="https://luma.com/pulumi"&gt;Pulumi User Groups&lt;/a&gt; in your local area, or if there isn’t one yet, you are welcome to join the &lt;a href="https://slack.pulumi.com/"&gt;Pulumi Slack community&lt;/a&gt; and meet people from all over the world.&lt;/p&gt;</description><author>Sara Huddleston</author><category>announcements</category><category>community</category><category>pulumi-events</category></item><item><title>International Women's Day: Celebrating our Women in Tech</title><link>https://www.pulumi.com/blog/international-women-day-celebrating-women-in-tech/</link><pubDate>Wed, 08 Mar 2023 13:00:00 -0800</pubDate><guid>https://www.pulumi.com/blog/international-women-day-celebrating-women-in-tech/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/international-women-day-celebrating-women-in-tech/index.png" /&gt;
&lt;p&gt;Today is International Women&amp;rsquo;s Day, and this year the theme is #EmbraceEquity - which means creating an equitable environment. An equitable work environment means understanding that everyone, regardless of gender, religion, ethnicity, background, or resources, brings strength to the workforce and that opportunities should be given to them based on their individual needs.&lt;/p&gt;
&lt;p&gt;For Pulumi, it means a work environment where everyone can share ideas and respect them even when disagreeing. Women&amp;rsquo;s experiences - as well as men&amp;rsquo;s and nonbinary&amp;rsquo;s experiences - inform the direction of digital technology and innovation.&lt;/p&gt;
&lt;p&gt;Being International Women&amp;rsquo;s Day, today we focus on women in tech.&lt;/p&gt;
&lt;h2 id="women-in-tech-stats-awareness"&gt;Women in Tech Stats Awareness&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Women earn an average of 17% (US) to 13% (EU) less than men&lt;/strong&gt;, according to &lt;a href="https://www.gao.gov/products/gao-23-106041"&gt;U.S. Census Bureau&lt;/a&gt; and &lt;a href="https://commission.europa.eu/strategy-and-policy/policies/justice-and-fundamental-rights/gender-equality/equal-pay/gender-pay-gap-situation-eu_en"&gt;European Commission&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;60% of women in tech said a parent or teacher encouraged them&lt;/strong&gt;. &lt;a href="https://www.logitech.com/en-us/mx/women-who-master.html#read-the-report"&gt;Logitech and Girls Who Code&amp;rsquo;s report&lt;/a&gt; on understanding the barriers women face in tech noted the importance of having role models and people encouraging them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In small companies, 30.2% of employees are women&lt;/strong&gt;, according to the &lt;a href="https://anitab.org/research-and-impact/top-companies/2020-results/"&gt;report Top Companies for Women Technologists&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;75% of women in tech are asked to handle more administrative tasks&lt;/strong&gt; compared to their male colleagues, according to &lt;a href="https://www.navisite.com/wp-content/uploads/2022/04/Gender-Divide-in-Tech-Infographic.pdf"&gt;Navisite&amp;rsquo;s survey&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below, you can meet some of Pulumi&amp;rsquo;s tech women - their experience, why they chose tech, and advice to thrive.&lt;/p&gt;
&lt;h2 id="pulumis-women-in-tech"&gt;Pulumi&amp;rsquo;s Women in Tech&lt;/h2&gt;
&lt;h3 id="sarah-hughes-software-engineer"&gt;Sarah Hughes, Software Engineer&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;I was lucky enough to go to high school with a ton of other girls who were into tech, so I took AP Computer Science in a class that was close to a 50/50 split. It really helped to start from scratch on equal footing. (Almost) everyone knew absolutely nothing, and it was obvious that our strengths and weaknesses were completely independent of gender.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="lina-desloge-account-executive"&gt;Lina Desloge, Account Executive&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;I have always enjoyed problem-solving and working with people&amp;hellip;which is what ultimately led me to a technical product. The advice I would give my younger self is don&amp;rsquo;t worry, just start!&amp;rdquo;&lt;/p&gt;
&lt;h3 id="kelsey-dirks-customer-success-manager"&gt;Kelsey Dirks, Customer Success Manager&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;When I entered university, I chose Electrical Engineering as my major. I soon learned coding was not my specialty and switched to Communications, but I loved the people in the classes. It was clear both the professors and students were passionate about their craft. I knew that when I left university, I wanted to work with these types of people, but I had no idea in what capacity. Over the years, my experience led me to be a Customer Success Manager.&lt;/p&gt;
&lt;p&gt;What I would tell my younger self is to not stress about trying to figure everything out right away. Every job will have its ups and downs, but when it comes to a career, trust that it will all work out. The biggest piece of advice I can give to women entering the tech field is don’t be afraid to lean on your female support system. If you aren’t able to advocate for yourself, your sisters in STEM will.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="jonelle-boyd-ux-designer"&gt;Jonelle Boyd, UX Designer&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;If I could give advice to women entering the tech field, I would tell them don’t be afraid of change. Whether that be a role change or a job change, because every new opportunity is a chance to learn and grow. I’ve changed roles as well as worked at a variety of different places during my career and I’ve learned something new in every position. There’s tremendous value in having a wide range of experience, so never be afraid of making that change.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="casey-huang-software-engineer"&gt;Casey Huang, Software Engineer&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;To my younger self - At some point, you’ve heard people sell working in the tech industry by saying the hours are more flexible - you’re not chained to your desk 9 to 5, you can leave early as long as you get your work done. This is somewhat true, but you should know: there is always more work to do. Some of it is important enough to move the business needle. Some of it isn’t as important, but you just care a lot about it. It’ll be up to you to define your boundaries, and it will be up to you to defend them. But it’s okay; if your work environment is truly a good fit for you, they’ll follow your lead and support you. You can’t min/max life like you like to do in video games. What you’re doing is good enough. Don’t be so afraid of making mistakes. If anything, you’ll learn faster that way.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="guinevere-saenger-software-engineer"&gt;Guinevere Saenger, Software Engineer&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;Working in tech provides me with significant financial independence, professional respect, and a flexible work schedule. These are worthwhile for anyone to have, but especially so for women.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re always looking for more amazing talent to &lt;a href="https://www.pulumi.com/community"&gt;join us in our community&lt;/a&gt; and as &lt;a href="https://www.pulumi.com/careers"&gt;part of the Pulumi team&lt;/a&gt;.&lt;/p&gt;</description><author>Sara Huddleston</author><category>pulumi-culture</category><category>community</category></item><item><title>Managing NOAA Open Data across Multiple Clouds with Pulumi</title><link>https://www.pulumi.com/blog/managing-multi-cloud-open-data-noaa/</link><pubDate>Tue, 08 Nov 2022 21:01:21 +0000</pubDate><guid>https://www.pulumi.com/blog/managing-multi-cloud-open-data-noaa/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/managing-multi-cloud-open-data-noaa/index.png" /&gt;
&lt;blockquote&gt;
&lt;p&gt;Denis Willett is a software engineer at the &lt;a href="https://ncics.org"&gt;North Carolina Institute of Climate Studies&lt;/a&gt; who works on the NOAA Open Data Dissemination Program. His work focuses on leveraging cloud technologies for the development of data processing and machine learning pipelines. Denis did his PhD in Entomology and Nematology at University of Florida and his undergraduate and masters work in Earth Systems at Stanford University. You can read his full bio &lt;a href="https://ncics.org/people/denis-willet-2/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://www.noaa.gov/information-technology/open-data-dissemination"&gt;NOAA Open Data Dissemination (NODD)&lt;/a&gt; makes environmental data freely and publicly accessible across Amazon Web Services (AWS), Microsoft Azure (Azure), and Google Cloud Platform (GCP). These data include near real-time satellite imagery, weather models, radar feeds, drought information, ocean databases, and a suite of climate data records among many others. This program supports more than 220 datasets and over 24PB of open data. Since its inception, the program has been growing rapidly, almost doubling in size over the past year.&lt;/p&gt;
&lt;p&gt;Availability of the petabyte scale environmental data on the cloud is only one aspect of NODD. The program places a premium of performant data delivery and access. NODD data pipelines provide resilient and low, in many cases near real-time, latencies to NOAA’s core data offerings. In addition, NODD provides performant, parallel access to these data supporting billions of requests per day and accessions of individual datasets exceeding 1PB in a single day.&lt;/p&gt;
&lt;p&gt;To support this program, the North Carolina Institute for Climate Studies (NCICS) through the Cooperative Institute for Satellite Earth System Studies (CISESS) engineers infrastructure on all three major public clouds ranging from object stores to messaging platforms, data ingest and transformation pipelines, and monitoring systems. These systems are deployed and managed by a very small team that, by necessity, must be agile, resilient and facilitate low latency data transfers at petabyte scale. Given the scale and diversity of resources managed through this initiative, we began looking for tools to facilitate multi-cloud management using &lt;a href="https://www.pulumi.com/what-is/what-is-infrastructure-as-code"&gt;infrastructure as code (IAC)&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="before-pulumi"&gt;Before Pulumi&lt;/h2&gt;
&lt;p&gt;Prior to beginning to transition to Pulumi, our team built infrastructure primarily using the Command Line Interface (CLIs) and consoles of each cloud service provider. While this worked well, it required extensive Cloud Service Partner (CSP) specific training to be able to not only navigate the nuances of each provider’s offerings, but also the unique interface to deploy resources. Changing and updating infrastructure in this manner resulted in long development cycles where upgrades were mostly manual and composed of repetitive tasks.&lt;/p&gt;
&lt;p&gt;Because of the manual nature of these deployments across three different cloud service providers, infrastructure began to sprawl. A clear snapshot of what resources were deployed where became difficult to compile and management of legacy and deprecated systems depended on manual intervention for clean-up. Infrastructure also began to drift. What started as uniform deployment of resources across multiple cloud platforms morphed as manual updates to each instance resulted in unique environments that were hard to replicate if they had to be deleted and redeployed.&lt;/p&gt;
&lt;p&gt;As an example, our program relies on Apache NiFi deployments on AWS, GCP, and Azure to coordinate and manage petabyte scale data transfers while managing data provenance. Drift in these deployments makes it especially difficult to redeploy identical configurations (even though we were using Ansible and Containerization) if they go down.&lt;/p&gt;
&lt;p&gt;An additional consequence of this approach is knowledge silos. The team member who deployed a group of resources on AWS had trouble (even with extensive documentation) communicating reusable architectures to another team member who was working on similar situations in Azure. While there are obvious nuances relevant to each cloud environment, our need to replicate similar infrastructure in a multi-cloud environment at scale necessitated extensive detailed communication in a team environment. Questions like: “What is the ARN of the most recent version of this service?” and “What queues do we have on GCP?” required communication and time taken by the individual responsible to answer those questions.&lt;/p&gt;
&lt;h2 id="how-and-why-we-chose-pulumi"&gt;How and why we chose Pulumi&lt;/h2&gt;
&lt;p&gt;As our team grew and our platform scaled (more than doubling in storage size over the past year by more than 10PB), we began to explore tooling to help us better navigate and manage our multi-cloud and multi-account environment. The nature of the NOAA Open Data Dissemination program agreements with each cloud service provider (CSP) meant that we had multiple accounts with different permissions, funding, and configuration on each CSP, resulting in flexible options.&lt;/p&gt;
&lt;p&gt;We began looking at infrastructure as code (IAC) solutions and initially considered many of the cloud-specific options: CloudFormation on AWS, Deployment Manager on Google, and Resource Manager on Azure. While these were nice for automation, we quickly discarded these as options because each had an individual learning curve for that environment; they did not translate well in a multi-cloud environment. These tools also did not have the control features of a full-fledged programming language; looping and if-then statements were challenging to implement.&lt;/p&gt;
&lt;p&gt;We also considered other Infrastructure as Code providers. While these solutions would meet a lot of our needs, and one of our team members had extensive experience with one of these tools, the ability to use a common tool seamlessly in a multi-cloud environment was attractive. However, the learning curve associated with a domain specific language was cause for reticence. In addition, we really wanted to be able to use the features of a full-fledged programming language.&lt;/p&gt;
&lt;p&gt;After experimenting with a few IAC options, our research institute began using Pulumi in late 2021 and immediately began implementing it to expand our infrastructure.&lt;/p&gt;
&lt;h2 id="benefits-of-using-pulumi-for-multi-cloud-infrastructure-as-code"&gt;Benefits of using Pulumi for multi-cloud infrastructure as code&lt;/h2&gt;
&lt;p&gt;Pulumi had all the benefits we were looking for in a multi-cloud infrastructure as code platform. In terms of automation, manual checklists could be converted to code reducing both deployment time and human errors in deployment. This automation with infrastructure as code (and accompanying state management) allowed us to adopt an immutable infrastructure paradigm where old resources are replaced with new updated infrastructure. While these benefits are accessible via other IAC tools, we particularly appreciated the Pulumi interface to be able to quickly access and inspect infrastructure deployments.&lt;/p&gt;
&lt;p&gt;We saw immediate wins for our teams in the following areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Drift was eliminated. Single source of truth, version controlled (and tagged) code documented exactly what was deployed where. When updates were needed, the code was updated and redeployed without manually tweaking individual resources through the console.&lt;/li&gt;
&lt;li&gt;Management of legacy and deprecated resources were obviated. With IAC managed deployment that would automatically clean up old resources, we saved many future hours cleaning up unused cloud resources.&lt;/li&gt;
&lt;li&gt;Team collaboration was enhanced. With a single source of truth and a visual snapshot of resources and deployments, the number of ‘What was the ARN of that resource again?” type questions dropped considerably. Team members could simply look at the deployment in one location and see all of the resources across all three major clouds. This type of friction-reduction really enhances team productivity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to these IAC benefits, we saw a number of key advantages in implementing Pulumi. First and foremost was the ability to use full-featured programming languages to deploy IAC solutions in a multi-cloud environment. This not only allowed us to fully take advantage of programming control structures (e.g., need 100 different rules on AWS? Now you do not have to manually script each one, just put it in a loop), but also flattened the learning curve for adoption. Our team already used Python extensively in our day-to-day work. Adopting Pulumi was almost as simple as importing a package. Accessing Pulumi through Python made it much easier to embrace.&lt;/p&gt;
&lt;p&gt;This accessibility through Python also meant that learning how to create, deploy, and share reusable templates was trivial as all team members already knew how to build, share, and import modules in Python. The ability to rapidly share these templates accelerated our development and allowed for standardization around best practices. For example, we use serverless capabilities on all three CSPs. Once we had a template for deploying our function as a service (FAAS) code on one platform, we did not have to reinvent the wheel or remember to go through a checklist; it was as simple as importing our internal FAAS module and pointing it to the correct code to deploy.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next steps&lt;/h2&gt;
&lt;p&gt;Pulumi has become an automation and collaboration tool that is accelerating our ability to provide petabyte scale data to the public in a multi-cloud environment. As we continue to scale our program, Pulumi is allowing us to leverage a small team to manage increasingly larger, more complex deployments in a dynamic environment. In addition, it is rapidly becoming a key player in a cross-cloud fabric that we are building to facilitate working across multiple platforms. By providing a similar, intuitive interface through our language of choice, Python, it is allowing us to more quickly adapt and reproducibly deploy infrastructure across AWS, Azure, and GCP.&lt;/p&gt;
&lt;p&gt;Please reach out to us with your thoughts, reactions, and comments at &lt;a href="mailto:NODD@NOAA.GOV"&gt;NODD@NOAA.GOV&lt;/a&gt;.&lt;/p&gt;</description><author>Denis Willett, PhD</author><category>aws</category><category>azure</category><category>google-cloud</category><category>python</category><category>guest-post</category><category>infrastructure-as-code</category><category>community</category></item><item><title>Teaming up to help deliver more meals to Ukraine</title><link>https://www.pulumi.com/blog/meals-for-ukraine/</link><pubDate>Mon, 16 May 2022 09:00:54 +0200</pubDate><guid>https://www.pulumi.com/blog/meals-for-ukraine/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/meals-for-ukraine/index.png" /&gt;
&lt;p&gt;Why not do good while learning about cloud native tech? &lt;a href="https://buoyant.io/"&gt;Buoyant&lt;/a&gt;, &lt;a href="https://www.civo.com/"&gt;Civo&lt;/a&gt;, &lt;a href="https://www.cockroachlabs.com/"&gt;Cockroach Labs&lt;/a&gt;, &lt;a href="https://www.fairwinds.com/"&gt;Fairwinds&lt;/a&gt;, &lt;a href="https://www.pulumi.com/"&gt;Pulumi&lt;/a&gt;, and &lt;a href="https://www.jetstack.io/"&gt;Jetstack&lt;/a&gt; have teamed up to donate $2 for each virtual booth visitor to &lt;a href="https://wck.org/"&gt;World Central Kitchen&lt;/a&gt; (WCK), a non-profit committed to providing meals in response to humanitarian crises.&lt;/p&gt;
&lt;p&gt;Working with restaurants across Ukraine and in six countries welcoming refugees, WCK is providing hundreds of thousands of daily meals for people in need. WCK serves meals at shelters, hospitals, transit hubs, and more. It&amp;rsquo;s a wonderful mission and, if you agree, we encourage you to &lt;a href="https://donate.wck.org/give/398293#!/donation/checkout"&gt;donate to WCK directly&lt;/a&gt; 💙 🇺🇦 💛&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it works&lt;/h2&gt;
&lt;p&gt;Participating is easy. If you registered for KubeCon, just click on the booth links below to visit our virtual booths. Each visit will count as a $2 donation, so if you visit us all, that&amp;rsquo;s $12 for the cause right there!&lt;/p&gt;
&lt;p&gt;Of course, there are many more reasons to visit our booths. You&amp;rsquo;ll find lots of great cloud native resources including demos, tutorials, ebooks, and more. Here are some of the highlights you can expect at each booth.&lt;/p&gt;
&lt;h3 id="pulumi--universal-infrastructure-as-code"&gt;Pulumi — Universal Infrastructure as Code&lt;/h3&gt;
&lt;p&gt;At the &lt;a href="https://www.pulumi.com/"&gt;Pulumi&lt;/a&gt; booth, you&amp;rsquo;ll learn about our cloud engineering platform, which brings infrastructure, developer, and security teams together through a unified software engineering process that tames cloud complexity and accelerates innovation. Don&amp;rsquo;t miss our Ask an Expert live sessions to hear directly from the engineering team. Whether you want to know more about how to build with Universal Infrastructure as Code in general-purpose programming and markup languages or just learn more about what&amp;rsquo;s on the roadmap, this is your chance to ask us anything.&lt;/p&gt;
&lt;h3 id="buoyant--the-creator-of-linkerd"&gt;Buoyant — the creator of Linkerd&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://buoyant.io/"&gt;Buoyant&lt;/a&gt; is announcing something really exciting: the introduction of &lt;em&gt;fully managed Linkerd&lt;/em&gt;. You can be one of the first to check out the demo, and you&amp;rsquo;ll also find a ton of service mesh content including a service mesh 101 in English and Spanish, their Engineer&amp;rsquo;s Guide to mTLS, and details on Buoyant&amp;rsquo;s Service Mesh Academy.&lt;/p&gt;
&lt;h3 id="civo--fast-simple-managed-kubernetes"&gt;Civo — Fast Simple, Managed Kubernetes&lt;/h3&gt;
&lt;p&gt;Stop by the &lt;a href="https://www.civo.com/"&gt;Civo&lt;/a&gt; booth for live sessions on how to get the most out of your Kubernetes environment - including demos, Civo marketplace walkthroughs and an inside look at how they live-provision a private region. You&amp;rsquo;ll find lots of resources and friendly team members to answer any questions on Civo’s easy-to-use managed Kubernetes service. It is fully CNCF certified and 100% Kubernetes upstream compatible.&lt;/p&gt;
&lt;h3 id="cockroach-labs--build-what-you-dream"&gt;Cockroach Labs — Build What you Dream&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/"&gt;CockroachDB&lt;/a&gt;&amp;rsquo;s presence this year at KubeCon (both physical and virtual) is primarily a charitable one (if you attend in person, visit booth P16, where for each badge scanned they&amp;rsquo;ll donate $3 to either Black Girls Code, Women Who Code, the Cancer Research Institute, or UNICEF for Ukrainian relief). Visit their booth to see demos and architecture overviews that relate what they’ve learned from building a multi-region, managed distributed SQL database offering on Kubernetes.&lt;/p&gt;
&lt;h3 id="fairwinds--kubernetes-governance-and-security"&gt;Fairwinds — Kubernetes Governance and Security&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.fairwinds.com"&gt;Fairwinds&lt;/a&gt;, a leader in Kubernetes governance providing a unified view between dev, sec, and ops, offers lots of great resources at their virtual booth this KubeCon. You can get a quick tour of Fairwinds Insights, learn about Kubernetes cost savings, how to make Kubernetes service ownership possible, typical Kubernetes security mistakes and much more. Come chat with a friendly Fairwinds engineer to talk about the many great open source solutions including Polaris, Goldilocks, Nova, and Pluto. Visit the &lt;a href="https://www.fairwinds.com/"&gt;Fairwinds&lt;/a&gt; booth today!&lt;/p&gt;
&lt;h3 id="jetstack--the-creator-of-cert-manager"&gt;Jetstack — the creator of cert-manager&lt;/h3&gt;
&lt;p&gt;Get more out of &lt;a href="https://cert-manager.io/"&gt;cert-manager&lt;/a&gt; and find out how &lt;a href="https://www.jetstack.io/"&gt;Jetstack&lt;/a&gt; Secure can eliminate workload misconfigurations and provide full visibility of each certificate across every production cluster. Chat directly to one of the project maintainers and learn to use cert-manager to secure Ingress endpoints and internal workloads using mTLS.&lt;/p&gt;
&lt;h2 id="lots-of-great-reasons-to-visit-our-booths"&gt;Lots of great reasons to visit our booths&lt;/h2&gt;
&lt;p&gt;There are plenty of good reasons to check out the great content of these amazing companies while supporting a wonderful charity that is doing an incredible job at helping the people of Ukraine.&lt;/p&gt;</description><author>Wendy Smith</author><category>community</category><category>pulumi-events</category></item><item><title>Introducing the Puluminaries</title><link>https://www.pulumi.com/blog/introducing-the-puluminaries/</link><pubDate>Tue, 03 May 2022 10:04:47 -0500</pubDate><guid>https://www.pulumi.com/blog/introducing-the-puluminaries/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/introducing-the-puluminaries/index.png" /&gt;
&lt;p&gt;Pulumi is more than a way to build, deploy, and manage your infrastructure and cloud applications. Pulumi is also a strong and vibrant community. We are very excited to announce and showcase our new program of community champions, the Puluminaries!&lt;/p&gt;
&lt;img src="puluminaries.png" width="500" height="500" alt="Puluminaries logo"/&gt;
&lt;p&gt;The Puluminaries program was created to recognize members of the Pulumi community who are experts and leaders in the field of programming and infrastructure, but also who give back to the community to make it grow! They help others get started, give feedback to make Pulumi&amp;rsquo;s roadmap even better, and educate people through content or public speaking. The program is open to all members of the community, and we welcome anyone to join the program.&lt;/p&gt;
&lt;p&gt;While the Puluminaries themselves have been active in the community for quite some time, we officially launched the program at &lt;a href="https://pulumi.com/pulumi-up"&gt;PulumiUP&lt;/a&gt; in 2022, and it is our pleasure to introduce the first cohort of members to the program!&lt;/p&gt;
&lt;h2 id="meet-the-puluminaries"&gt;Meet The Puluminaries!&lt;/h2&gt;
&lt;h3 id="engin-diri"&gt;Engin Diri&lt;/h3&gt;
&lt;img src="engin-diri.png" width="200" height="200" alt="Engin Diri"/&gt;
&lt;a data-track="twitter" href="https://twitter.com/_ediri"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-x"/&gt;&lt;/svg&gt;
&lt;/a&gt;&amp;nbsp;&lt;a data-track="github" href="https://github.com/dirien"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-github"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;p&gt;Engin&amp;rsquo;s excitement for Pulumi is not possible to be measured using current science. He will always share his thoughts on new developments in the Pulumi project, as well as join in for fun and discussion in various PulumiVision livestreams!&lt;/p&gt;
&lt;h3 id="joshua-studt"&gt;Joshua Studt&lt;/h3&gt;
&lt;img src="joshua-studt.png" width="200" height="200" alt="Joshua Studt"/&gt;
&lt;a data-track="github" href="https://github.com/orionstudt"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-github"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;p&gt;Joshua is a longtime member of the Pulumi community, and he contributed to the implementation of Automation API for C#.&lt;/p&gt;
&lt;h3 id="kat-morgan"&gt;Kat Morgan&lt;/h3&gt;
&lt;img src="kat-morgan.png" width="200" height="200" alt="Kat Morgan"/&gt;
&lt;a data-track="twitter" href="https://twitter.com/usrbinkat"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-x"/&gt;&lt;/svg&gt;
&lt;/a&gt;&amp;nbsp;&lt;a data-track="github" href="https://github.com/usrbinkat"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-github"/&gt;&lt;/svg&gt;
&lt;/a&gt;&amp;nbsp;&lt;a data-track="linkedin" href="https://linkedin.com/in/usrbinkat"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-linkedin"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;p&gt;Kat has been a helpful member of the Pulumi community - not only providing feedback on real-world use of Pulumi, but also sharing her knowledge with &lt;a href="https://konghq.com/webinars/devmyops-deploy-kong-with-pulumi?utm_souce=pulumi&amp;amp;utm_medium=pulumi"&gt;workshops and webinars&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="komal-ali"&gt;Komal Ali&lt;/h3&gt;
&lt;img src="komal-ali.png" width="200" height="200" alt="Komal Ali"/&gt;
&lt;a data-track="twitter" href="https://twitter.com/zwitkali"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-x"/&gt;&lt;/svg&gt;
&lt;/a&gt;&amp;nbsp;&lt;a data-track="github" href="https://github.com/komalali"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-github"/&gt;&lt;/svg&gt;
&lt;/a&gt;&amp;nbsp;&lt;a data-track="linkedin" href="https://linkedin.com/in/komal-ali"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-linkedin"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;p&gt;In addition to being a Puluminary, Komal is also a Pulumni, having spent time working to make Pulumi a better tool for developers - they were ever-present in making our Python SDK better and creating the &lt;a href="https://www.pulumi.com/docs/using-pulumi/automation-api/"&gt;Automation API&lt;/a&gt;!&lt;/p&gt;
&lt;h3 id="paul-hicks"&gt;Paul Hicks&lt;/h3&gt;
&lt;img src="paul-hicks.png" width="200" height="200" alt="Paul Hicks"/&gt;
&lt;a data-track="github" href="https://github.com/tenwit"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-github"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;p&gt;Paul seems to have an answer to everything. He is ever-present in the Pulumi Community, sharing his knowledge to folks who are just getting started, but also bringing to bear his real-world expertise in complex Pulumi implementations. Chances are, if you see a great answer to a question in &lt;a href="https://slack.pulumi.com/"&gt;Pulumi Community Slack&lt;/a&gt;, it came from Paul.&lt;/p&gt;
&lt;h3 id="ringo-de-smet"&gt;Ringo De Smet&lt;/h3&gt;
&lt;img src="ringo-smet.png" width="200" height="200" alt="Ringo De Smet"/&gt;
&lt;a data-track="twitter" href="https://twitter.com/ringods"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-x"/&gt;&lt;/svg&gt;
&lt;/a&gt;&amp;nbsp;&lt;a data-track="github" href="https://github.com/ringods"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-github"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;p&gt;Ringo is one of the founding board members of &lt;a href="https://www.pulumi.com/blog/2022-03-30-introducing-pulumiverse/"&gt;the Pulumiverse&lt;/a&gt; - a place to interact and collaborate on Pulumi-based libraries, projects, and educational materials. Ringo (along with fellow Puluminary Simen A. W. Olsen) have brought their vision for expanding the Pulumi community to life with the creating of this community-operated space! Ringo also created the &lt;a href="https://github.com/ringods/pulumi-resource"&gt;Pulumi Resource Type for Concourse&lt;/a&gt;, and is ever-present in every community he is a part of, not just Pulumi&amp;rsquo;s!&lt;/p&gt;
&lt;h3 id="rizel-scarlett"&gt;Rizel Scarlett&lt;/h3&gt;
&lt;img src="rizel-scarlett.png" width="200" height="200" alt="Rizel Scarlett"/&gt;
&lt;a data-track="twitter" href="https://twitter.com/blackgirlbytes"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-x"/&gt;&lt;/svg&gt;
&lt;/a&gt;&amp;nbsp;&lt;a data-track="github" href="https://github.com/blackgirlbytes"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-github"/&gt;&lt;/svg&gt;
&lt;/a&gt;&amp;nbsp;&lt;a data-track="linkedin" href="https://www.linkedin.com/in/rizel-bobb-semple/"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-linkedin"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;p&gt;Rizel brings her remarkable talent for guidance and teaching to the Pulumi community. She has participated in many Pulumi events, including Cloud Engineering Summit, and &lt;a href="https://youtu.be/2uQEIYuJBZ4"&gt;appearing on the PulumiVision livestream&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="simen-a-w-olsen"&gt;Simen A. W. Olsen&lt;/h3&gt;
&lt;img src="simen-olsen.png" width="200" height="200" alt="Simen W Olsen"/&gt;
&lt;a data-track="github" href="https://github.com/cobraz"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#b-github"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;p&gt;Another one of the founding board members of &lt;a href="https://www.pulumi.com/blog/2022-03-30-introducing-pulumiverse/"&gt;the Pulumiverse&lt;/a&gt;, Simen has a great passion for technology communities, but especially for the Pulumi community. Simen was instrumental on the &lt;a href="https://github.com/pulumi/actions"&gt;Pulumi GitHub Action&lt;/a&gt; rewrite - designing and rearchitecting it in his free time.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Do you know someone who would make a great Puluminary? Let us know! You can email us at da@pulumi.com with your nominations!&lt;/em&gt;&lt;/p&gt;</description><author>Matty Stratton</author><category>community</category></item><item><title>Announcing Pulumi Community Slack Archive</title><link>https://www.pulumi.com/blog/announcing-community-slack-archive/</link><pubDate>Mon, 02 May 2022 12:00:00 -0700</pubDate><guid>https://www.pulumi.com/blog/announcing-community-slack-archive/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/announcing-community-slack-archive/index.png" /&gt;
&lt;p&gt;Creating a place for the Pulumi community to gather, ask questions, get help in real-time, and share successes has been an important part of the explosive growth we&amp;rsquo;ve seen in both users and customers. The Pulumi community slack has grown to over 7000 members and well over 200,000 messages.&lt;/p&gt;
&lt;p&gt;Within those 200,000 messages are years of information kept behind a &amp;ldquo;walled garden&amp;rdquo; that is undiscoverable outside Slack&amp;rsquo;s search capabilities.&lt;/p&gt;
&lt;p&gt;Today we&amp;rsquo;re announcing that our community slack is now publicly available via &lt;a href="https://archive.pulumi.com"&gt;https://archive.pulumi.com&lt;/a&gt; thanks to &lt;a href="https://linen.dev"&gt;Linen&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For many, Slack has become the de-facto collaboration tool. Adding Slack workspaces is relatively straightforward and means you can communicate with various different communities and your colleagues in real-time.&lt;/p&gt;
&lt;p&gt;This ability to use the tools you&amp;rsquo;re using in your everyday work to also quickly get help with Pulumi has proven to be extremely successful. Our users often tell us that having access to Pulumi staff and other community members helps them accelerate their cloud engineering journey. In addition to providing value to our users, the Pulumi team enjoys having a mechanism to engage with users to get feedback and inform them about the Pulumi ecosystem. It&amp;rsquo;s clear to us that the Pulumi community slack has proven invaluable as Pulumi has grown.&lt;/p&gt;
&lt;p&gt;The use of Slack for our community meeting place isn&amp;rsquo;t without its problems. Often, the first port of call for users that need answers is to use search engines, and all of the information stored in Slack isn&amp;rsquo;t readily available by default with Slack. The Pulumi team regularly answers questions on &lt;a href="https://stackoverflow.com/questions/tagged/pulumi"&gt;StackOverflow&lt;/a&gt; and via &lt;a href="https://github.com/pulumi/pulumi/discussions"&gt;GitHub Discussions&lt;/a&gt; which are searchable and indexed by search engines, but adding the treasure trove of information from Slack will improve the experience for eager Pulumi users.&lt;/p&gt;
&lt;p&gt;Until now, this has been exceptionally difficult. With the recent launch of &lt;a href="https://linen.dev"&gt;Linen&lt;/a&gt; we are able to bring our community chat outside of Slack&amp;rsquo;s boundaries in an easily searchable, indexable way.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re excited to provide this to the community and encourage you to try it out at &lt;a href="https://archive.pulumi.com"&gt;https://archive.pulumi.com&lt;/a&gt;.&lt;/p&gt;</description><author>Lee Briggs</author><category>community</category></item><item><title>Using Go Generics with Pulumi</title><link>https://www.pulumi.com/blog/2022-03-31-go-generics-with-pulumi/</link><pubDate>Thu, 31 Mar 2022 14:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/2022-03-31-go-generics-with-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/2022-03-31-go-generics-with-pulumi/index.png" /&gt;
&lt;div class="note note-warning"&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.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#p-warning-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;This post is outdated. For the latest on Go Generics, &lt;a href="https://www.pulumi.com/blog/go-generics-preview/"&gt;visit this blog post&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;March 15th, 2022&amp;hellip; just two weeks ago. The Go team &lt;a href="https://go.dev/blog/go1.18"&gt;released Go 1.18&lt;/a&gt; to the world. What seems like a trivial point release actually brings a huge new feature to the Go language: Generics.&lt;/p&gt;
&lt;p&gt;In this article, I want to show you how you can use this new feature to build a great developer experience with your abstractions for your Pulumi programs.&lt;/p&gt;
&lt;h2 id="using-go-118"&gt;Using Go 1.18&lt;/h2&gt;
&lt;p&gt;The first thing you need to do is ensure you have Go 1.18 installed. You can run &lt;code&gt;go version&lt;/code&gt; to check. If you see anything less than 1.18, you&amp;rsquo;ll need to go upgrade first. The second thing you need to do is update the &lt;code&gt;go.mod&lt;/code&gt; in your Pulumi program to specify 1.18 as the minimum version.&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="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.18&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;Once you&amp;rsquo;ve done these two steps, you&amp;rsquo;re ready to start using generics in your Pulumi programs.&lt;/p&gt;
&lt;h2 id="using-generics"&gt;Using Generics&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start by asking a question. What is a good use-case for generics? In my experience, generics work really well for allowing us to provide a common interface to our consumers (developers using our APIs) that allows them to use that same interface to accomplish a collection of similar tasks that require different implementations.&lt;/p&gt;
&lt;p&gt;For example, let&amp;rsquo;s assume we want to provide a platform to our developers and allow them to install &lt;strong&gt;ANY&lt;/strong&gt; Kubernetes resource. Our goal is to provide an &lt;code&gt;AddComponent&lt;/code&gt; method that they can call to install either pre-supported components, components created by the platform team, or their own custom components. The glue and important aspect here is that all these components conform to the same interface.&lt;/p&gt;
&lt;h3 id="defining-the-interface"&gt;Defining The Interface&lt;/h3&gt;
&lt;p&gt;In the simplest form, we just need an &lt;code&gt;Install&lt;/code&gt; function to call that returns either an error or an array of Pulumi resources.&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;Component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&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="nf"&gt;Install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&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;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([]&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;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&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="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;h3 id="creating-our-components"&gt;Creating our Components&lt;/h3&gt;
&lt;p&gt;Now we need to provide a component that satisfies this interface. So let&amp;rsquo;s assume that we want to install nginx. First, we create a struct that contains fields for any points of configuration. For today&amp;rsquo;s example, we&amp;rsquo;ll just request the version to be installed and a name; the name being used to ensure if the component is installed more than once, it can be uniquely identified.&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;Nginx&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;Version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&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;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Nginx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&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;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([]&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;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;return&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="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;From there, we can begin to flesh out the &lt;code&gt;Install&lt;/code&gt; implementation for nginx.&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;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Nginx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&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;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([]&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;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&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;deployment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;appsv1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;appsv1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeploymentArgs&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;Metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectMetaArgs&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;Labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &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;StringMap&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="s"&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&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="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="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;Spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;appsv1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeploymentSpecArgs&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;Replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;Selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LabelSelectorArgs&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;MatchLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &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;StringMap&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="s"&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&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="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="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;Template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PodTemplateSpecArgs&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;Metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectMetaArgs&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;Labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &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;StringMap&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="s"&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&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="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="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;Spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PodSpecArgs&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;Containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerArray&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerArgs&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;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;nginx&amp;#34;&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;Image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;nginx:%s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Version&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;Ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerPortArray&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerPortArgs&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;ContainerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&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="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="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="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="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="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="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="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="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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="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="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&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;Resource&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;deployment&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="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;This is rather contrived, but hopefully you can see the power of using generics as an interface for platform engineering. Our &lt;code&gt;AddComponent&lt;/code&gt; implementation for nginx could return a deployment, a service, an ingress with horizontal pod auto-scalers, or any other resource that you want.&lt;/p&gt;
&lt;h2 id="using-the-components"&gt;Using the Components&lt;/h2&gt;
&lt;p&gt;Finally, we create our generic function, &lt;code&gt;AddComponent&lt;/code&gt;, and start having some fun.&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;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AddComponent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&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;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([]&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;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&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="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;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&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;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&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;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&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="nf"&gt;AddComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;my-nginx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;Nginx&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1.14.2&amp;#34;&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&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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="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="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;Now you and your organization can provide a repository of these components for your developers to consume with their platforms.&lt;/p&gt;
&lt;p&gt;The possibilities are endless.&lt;/p&gt;
&lt;p&gt;Have fun and see you soon! 👋&lt;/p&gt;</description><author>David Flanagan</author><category>community</category></item><item><title>Introducing the Pulumiverse</title><link>https://www.pulumi.com/blog/2022-03-30-introducing-pulumiverse/</link><pubDate>Wed, 30 Mar 2022 10:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/2022-03-30-introducing-pulumiverse/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/2022-03-30-introducing-pulumiverse/index.png" /&gt;
&lt;p&gt;Today, we&amp;rsquo;re excited to announce that we&amp;rsquo;re working with the Pulumi community to provide a place to interact and collaborate on Pulumi-based libraries, projects, and educational materials: the &lt;a href="https://github.com/pulumiverse"&gt;Pulumiverse&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This project is especially important to me, because it&amp;rsquo;s an idea that I&amp;rsquo;ve tried in the past, 2018 😯, and failed to commit to and give it the attention it deserves.&lt;/p&gt;
&lt;p&gt;Fortunately, two other wonderful Pulumi community members stepped up and started to put together their own programme and with me now being at Pulumi, the timing was amazingly serendipitous.&lt;/p&gt;
&lt;p&gt;I spoke with &lt;a href="https://twitter.com/ringods"&gt;Ringo De Smet&lt;/a&gt; and &lt;a href="https://twitter.com/cobrazo"&gt;Simen A. W. Olsen&lt;/a&gt;, the founding members of the Pulumiverse.&lt;/p&gt;
&lt;p&gt;Ringo, Head of CI/CD at Napoleon Games, had this to say about why he&amp;rsquo;s excited to create the Pulumiverse:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For infrastructure automation, we had other great tools which preceded Pulumi: Chef, Puppet, Ansible. Any of these tools have a vibrant community of people preventing that all of us need to re-invent the wheel. We had &lt;a href="https://sous-chefs.org"&gt;Sous-Chefs&lt;/a&gt; for Chef &amp;amp; &lt;a href="https://voxpupuli.org"&gt;Vox Pupuli&lt;/a&gt; for Puppet. But I see a lot of Pulumi users implementing the same stuff over and over again. That&amp;rsquo;s why I think Pulumi needs it&amp;rsquo;s own community. So I started it: &lt;a href="https://github.com/pulumiverse"&gt;Pulumiverse&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Simon, a Developer at &lt;a href="https://github.com/bjerkio"&gt;Bjerk AS&lt;/a&gt;, shared:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I imagine a community filled with like-minded people who build and support Pulumi providers, opinionated components, configurations and share ideas on how we can utilize the power of Pulumi in new ways. So I created Pulumiverse to share my enthusiasm, give back to Pulumi and support developers by building a platform where we can work together!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="what-is-the-pulumiverse"&gt;What is the Pulumiverse?&lt;/h2&gt;
&lt;p&gt;Pulumiverse, in the simplest form, is a GitHub organization. It provides a community-operated place for anyone to communicate, contribute, and collaborate with other members of the Pulumi community and build abstractions and SDKs that can be shared and used by anyone.&lt;/p&gt;
&lt;p&gt;There are many libraries or collections of components for Pulumi out there. Most of them are made based on requirements of individual companies and developers. Pulumiverse aims to make a community where these libraries can be created and where opinionated configurations for Pulumi resources can be stored and discussed.&lt;/p&gt;
&lt;h2 id="current-projects"&gt;Current Projects&lt;/h2&gt;
&lt;p&gt;The Pulumiverse launches with just a few projects, but we know that, with the support of such an amazing community, the Pulumiverse will grow and grow and provide invaluable resources for anyone who wants to build or use Pulumi for a library or project.&lt;/p&gt;
&lt;h3 id="awesome-pulumi"&gt;Awesome Pulumi&lt;/h3&gt;
&lt;p&gt;🐙 &lt;a href="https://github.com/pulumiverse/awesome-pulumi"&gt;Awesome Pulumi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Everybody loves an awesome list, and this one delivers to you a list of awesome Pulumi resources, books, tutorials, talks, and much more.&lt;/p&gt;
&lt;p&gt;Want your project listed? Open a PR. We can&amp;rsquo;t wait to share it with everyone.&lt;/p&gt;
&lt;h3 id="kubernetes-sdks"&gt;Kubernetes SDKs&lt;/h3&gt;
&lt;p&gt;🐙 &lt;a href="https://github.com/pulumiverse/kubernetes-sdks"&gt;Kubernetes SDKs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Kubernetes SDKs project aims to provide multi-language SDKs for interacting with Kubernetes Custom Resource Definitions (CRDs). Currently, it automatically builds SDKs for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ArgoCD&lt;/li&gt;
&lt;li&gt;CertManager&lt;/li&gt;
&lt;li&gt;Contour&lt;/li&gt;
&lt;li&gt;Crossplane&lt;/li&gt;
&lt;li&gt;Istio&lt;/li&gt;
&lt;li&gt;Kong Ingress&lt;/li&gt;
&lt;li&gt;Knative&lt;/li&gt;
&lt;li&gt;RedPanda&lt;/li&gt;
&lt;li&gt;Tyk&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is just the beginning, and we want to expand this collection quickly. So if you&amp;rsquo;re working with Kubernetes and want a rich developer experience for authoring custom resources, open an issue and let us know what you need.&lt;/p&gt;
&lt;h3 id="pulumi-concourse--unifi"&gt;Pulumi Concourse &amp;amp; Unifi&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pulumiverse/pulumi-concourse"&gt;Concourse Provider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pulumiverse/pulumi-unifi"&gt;Unifi Provider&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are community-maintained providers and are examples of how you can share your providers with the world as part of the Pulumiverse.&lt;/p&gt;
&lt;h3 id="your-project"&gt;Your Project?&lt;/h3&gt;
&lt;p&gt;Got your own library that you think would be beneficial to the community?&lt;/p&gt;
&lt;p&gt;Join us in the #pulumiverse channel on the &lt;a href="https://pulumi.slack.com"&gt;Pulumi Slack&lt;/a&gt;, and we&amp;rsquo;ll be happy to help you get it over to the Pulumiverse.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll see you soon! 👋&lt;/p&gt;</description><author>David Flanagan</author><category>community</category></item><item><title>Pulumi Secrets Management</title><link>https://www.pulumi.com/blog/pulumi-secrets-management/</link><pubDate>Wed, 16 Mar 2022 09:15:57 -0800</pubDate><guid>https://www.pulumi.com/blog/pulumi-secrets-management/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/pulumi-secrets-management/index.png" /&gt;
&lt;p&gt;Pulumi community member &lt;a href="https://www.sanjaybhagia.com/"&gt;Sanjay Bhagia&lt;/a&gt; explores using Pulumi to manage secrets.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We will build on the &lt;a href="https://www.sanjaybhagia.com/pulumi-configuration-management"&gt;Configuration Management&lt;/a&gt; blog post that I wrote earlier so please have a look if you haven&amp;rsquo;t done so already.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Having sensitive information such as Passwords, API Keys, Tokens, ConnectionStrings etc. is quite common for any application. However keeping them in your source code in plaintext is never a good idea, let alone checking it in your source control.&lt;/p&gt;
&lt;p&gt;Pulumi&amp;rsquo;s configuration system lets us add secrets to our configuration in a similar way we manage any other configuration. Pulumi offers multiple approaches to manage sensitive information and exposes a number of APIs and tools we can use to work with them seamlessly, so without further a do, let&amp;rsquo;s explore how it all works.&lt;/p&gt;
&lt;h2 id="handling-secrets"&gt;Handling Secrets&lt;/h2&gt;
&lt;p&gt;First off, let&amp;rsquo;s see how can we set secrets in our configuration.&lt;/p&gt;
&lt;p&gt;We use &lt;code&gt;pulumi config set&lt;/code&gt; with &lt;code&gt;--secret&lt;/code&gt; flag to set a secret&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi config &lt;span class="nb"&gt;set&lt;/span&gt; --secret mySecretKey mySecretValue
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check the config &lt;code&gt;pulumi config&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To retrieve the secrets programmatically, the use of &lt;code&gt;RequireSecrets&lt;/code&gt; variant is recommended.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$&amp;#34;mySecretKey: {config.RequireSecret(&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;mySecretKey&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;)}&amp;#34;&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;see the value is masked
&lt;img src="https://www.pulumi.com/blog/pulumi-secrets-management/images/pulumi-config-get-secret.jpg" alt="Pulumi Config Get Secret"&gt;&lt;/p&gt;
&lt;p&gt;If we look at the &lt;code&gt;Pulumi.dev.yaml&lt;/code&gt; file in VSCode, this is what we see:&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;config&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="nt"&gt;azure:location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;australiaeast&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="nt"&gt;hello-pulumi:data&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="nt"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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="nt"&gt;nums&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="m"&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="m"&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="m"&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="nt"&gt;hello-pulumi:location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;australiasoutheast&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="nt"&gt;hello-pulumi:mySecretKey&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="nt"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;AAABAFybCK/w34qectPQHPWSoivUhG7uI5//y7Xqc/6Cczh3k3Yi35zvrk+U&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="nt"&gt;hello-pulumi:resourcesName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pulumi&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;Looking at this, what we see is that the configuration we set as a secret is not stored in plaintext, it is in fact encrypted. By default, Pulumi uses per-stack encryption key managed by the Pulumi Service to encrypt values.&lt;/p&gt;
&lt;p&gt;However, this might not be desirable in situations where you want to manage the state by yourself by using any external storage like S3 bucket, Azure Blob storage. Or you simply don&amp;rsquo;t want Pulumi to manage the keys and would prefer keeping that control and management with you. For such scenarios, Pulumi supports multiple providers for secrets management.&lt;/p&gt;
&lt;p&gt;As of this writing, these are the supported providers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;default&lt;/li&gt;
&lt;li&gt;passphrase&lt;/li&gt;
&lt;li&gt;awskms&lt;/li&gt;
&lt;li&gt;azurekeyvault&lt;/li&gt;
&lt;li&gt;gcpkms&lt;/li&gt;
&lt;li&gt;hashivault&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We used &lt;code&gt;default&lt;/code&gt; provider in our examples above. &lt;code&gt;passphrase&lt;/code&gt; allows you to keep the encryption keys to yourself but still use Pulumi&amp;rsquo;s backend service for state management OR you are self-managing the state storage (e.g., Azure Storage). The last option is where you are completely managing your own keys (bring your own key a.k.a BYOK)&lt;/p&gt;
&lt;h3 id="using-passphrase"&gt;Using Passphrase&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s create a new stack in our codebase that we used in the previous blogpost (grab the core &lt;a href="https://github.com/sanjaybhagia/pulumi-examples/tree/d035014aa852ff2d68a8958d5036037a5cf50ae4/hello-pulumi"&gt;here&lt;/a&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 init passphrasestack --secrets-provider passphrase
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;or create a new project:&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 new azure-csharp --secrets-provider passphrase
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;ll ask for the passphrase. This is the passphrase that Pulumi is going to use to encrypt the configuration system.
&lt;img src="https://www.pulumi.com/blog/pulumi-secrets-management/images/pulumi-stack-init-passphrase-1.jpg" alt="Initialize the stack"&gt;&lt;/p&gt;
&lt;p&gt;Looking at the project in VSCode, we can see a new file has been created.
&lt;img src="https://www.pulumi.com/blog/pulumi-secrets-management/images/pulumi-stack-init-passphrase-2.jpg" alt="Create new file"&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s add two keys (one normal and one secret):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi config &lt;span class="nb"&gt;set&lt;/span&gt; normalKey normalValue&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;pulumi config &lt;span class="nb"&gt;set&lt;/span&gt; --secret secretKey secretValue
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-secrets-management/images/pulumi-stack-init-passphrase-3.jpg" alt="Add two keys"&gt;&lt;/p&gt;
&lt;p&gt;This is how our stack file looks like:
&lt;img src="https://www.pulumi.com/blog/pulumi-secrets-management/images/pulumi-stack-init-passphrase-4.jpg" alt="Stack output"&gt;&lt;/p&gt;
&lt;p&gt;If you pay attention, the secure value in &lt;code&gt;Pulumi.passphrasestack.yaml&lt;/code&gt; is different to what we had in &lt;code&gt;Pulumi.dev.yaml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Notice, we need to enter the passphrase every time we are performing any operations. This can be avoided by setting &lt;code&gt;PULUMI_CONFIG_PASSPHRASE&lt;/code&gt; in your environment variables (can be done on the local machine or in the CI system).&lt;/p&gt;
&lt;h3 id="using-azure-key-vault-for-encryption"&gt;Using Azure Key Vault for encryption&lt;/h3&gt;
&lt;p&gt;This would be a similar approach for any other cloud-based provider but we are looking at Azure Key Vault here. In order to use the Azure Key Vault for managing the encryption keys, the approach is same i.e., we set &lt;code&gt;--secret-provider&lt;/code&gt; to Azure Key Vault via the CLI&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi stack init &amp;lt;name&amp;gt; --secrets-provider&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;provider&amp;gt;://&amp;lt;provider-settings&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;e.g.,&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi stack init &amp;lt;name&amp;gt; --secrets-provider&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;azurekeyvault://&amp;lt;keyvaultname&amp;gt;.vault.azure.net/keys/&amp;lt;key name&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before we could do this, we need to create the Key Vault and a Key in the vault that we need.&lt;/p&gt;
&lt;h3 id="create-azure-key-vault"&gt;Create Azure Key Vault&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ az keyvault create --name pulumiazvault --resource-group rg-pulumi-dev --location australiasoutheast
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="create-the-key-in-vault"&gt;Create the Key in Vault&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ az keyvault key create --name pulumiencryptionkey --vault-name pulumiazvault
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="assign-access"&gt;Assign Access&lt;/h3&gt;
&lt;p&gt;When you create the vault and key from the CLI, a policy is created by default but it doesn&amp;rsquo;t set &amp;rsquo;encrypt&amp;rsquo; and &amp;lsquo;decrypt&amp;rsquo; policy for us, so use this command to include that.&lt;/p&gt;
&lt;p&gt;Extract Object Id of your account (you can pull this from Azure AD or run the command below to extract it. Use your account here.):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ az ad user show --id &amp;lt;your account name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Look up for ObjectId of your account:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ az keyvault set-policy --name pulumiazvault --object-id &amp;lt;Object Id from above&amp;gt; --key-permissions encrypt decrypt get create delete list update import backup restore recover
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;I prefer using Azure AD Application instead of any individual user account for such scenarios. In that case you will need the ObjectId of the application instead.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now we have the key vault with a key that we will use for encryption along with the right access policy set.
&lt;img src="https://www.pulumi.com/blog/pulumi-secrets-management/images/az-keyvault-with-key.jpg" alt="Screenshot of the azure keyvault"&gt;&lt;/p&gt;
&lt;h3 id="set-environment-variables"&gt;Set Environment Variables&lt;/h3&gt;
&lt;p&gt;You can set this if you want to use the credentials from the CLI (that you already have set up). &lt;code&gt;AZURE_KEYVAULT_AUTH_VIA_CLI=true&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Checkout &lt;a href="https://docs.microsoft.com/en-us/azure/developer/go/azure-sdk-authorization#use-environment-based-authentication"&gt;Azure Environment Authentication&lt;/a&gt; if you wish to use other authentication mechanism such as Client Credentials, Username/Password etc.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I prefer using Client Credentials since I&amp;rsquo;m using Azure AD Application approach.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="create-a-new-stack"&gt;Create a new stack&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m creating a new project for this scenario rather than using the previous one but you could very well create a new stack in the same project that uses Azure key Vault provider.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pulumi new azure-csharp --secrets-provider&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;azurekeyvault://pulumiazvault.vault.azure.net/keys/pulumiencryptionkey&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Pulumi.dev.yaml&lt;/code&gt; files looks like this after creating the new project. As you notice, we have the information about secrets provider and encryption key. From this point onward, Pulumi uses this information to encrypt the configuration system for us.&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;secretsprovider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;azurekeyvault://pulumiazvault.vault.azure.net/keys/pulumiencryptionkey&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="nt"&gt;encryptedkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;N1JsSnFjWGpKUl9jY3U1SlRQd2J2WXd3TEw0XzQxeWExY2hTOEhtLTRKWkd3cTViblg3OWF4VHFTcDY4amo4RDc4RUxyT3poNC0tUEVSM0ppbUxfcUdiN1dOWm9Cb0VrNU5TODF6dUlMVEg3c2VVc1ItTVZfRjFBQWpmdGNiX29xYU1BdTN2d0NlMmtWUWFUWUc0NjNJMnh4eUNybzdJMzlXZHdXMVRpODh4TnZfdDM1R2pfdS1rS3VlQlh5cDI4OXZmMkxXaFFuNzNBckJaRU9vcnl5WmJPeUVXMHpJSzlIOEZXOG5WTnl6akFXYmxWYkkyYVB6c3BsUWpDOGw5OFRJNXFyV0JBSjAybHVkTE9UMGF6MllqSW4xbm9GVW1pODRsbVJZbEh6OThPLUdBemlhcWp4V1dKaE5vN0NDeEFZdFM2cXk3emozSDI0UHlGQllESTdR&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="nt"&gt;config&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="nt"&gt;azure:location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;australiasoutheast&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;Rest of the things works the same as &lt;code&gt;default&lt;/code&gt; or &lt;code&gt;passphrase&lt;/code&gt; provider for encryption.&lt;/p&gt;
&lt;h2 id="switching-encryption-providers"&gt;Switching Encryption providers&lt;/h2&gt;
&lt;p&gt;You change the secrets provider of any of your stack anytime via this command.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pulumi stack change-secrets-provider &lt;span class="s2"&gt;&amp;#34;&amp;lt;secrets-provider&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt; It&amp;rsquo;s pretty straightforward, once the provider is changed, Pulumi will use the new secrets provider to encrypt configuration and state files.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Pulumi provides a robust mechanism for configuration management of your infrastructure. It provides support for handling secrets as well. You have the possibility to use external service providers for secrets management such as Azure Key Vault by using a familiar approach. The default option is secure and configurations and secrets are encrypted by the keys that Pulumi manages for us however we have the flexibility to take ownership of this and go to that extra mile for enhanced security or compliance reasons.&lt;/p&gt;
&lt;p&gt;You can find the source code for this blog post &lt;a href="https://github.com/sanjaybhagia/pulumi-examples/tree/950d6b4889999be548a21cccb9945ad5f9cd20a0/pulumi-azvault"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This article was originally posted at &lt;a href="https://www.sanjaybhagia.com/2021/01/26/pulumi-secrets-management"&gt;https://www.sanjaybhagia.com/2021/01/26/pulumi-secrets-management&lt;/a&gt;. Reposted here with permission.&lt;/em&gt;&lt;/p&gt;</description><author>Sanjay Bhagia</author><category>community</category><category>secrets</category></item><item><title>Migrating My Infrastructure From Terraform to Pulumi</title><link>https://www.pulumi.com/blog/migrating-my-infrastructure-from-terraform-to-pulumi/</link><pubDate>Tue, 22 Feb 2022 12:36:23 -0600</pubDate><guid>https://www.pulumi.com/blog/migrating-my-infrastructure-from-terraform-to-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/migrating-my-infrastructure-from-terraform-to-pulumi/index.png" /&gt;
&lt;p&gt;Pulumi community member &lt;a href="https://blog.ekik.org/"&gt;Erik Näslund&lt;/a&gt; shares his thoughts on how to migrate from Terraform to Pulumi. Read on to learn all the details of his experience!&lt;/p&gt;
&lt;div class="note note-info"&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.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;
Since this post was published, Pulumi has added first-class support for HashiCorp
Configuration Language (HCL). You can now write Pulumi programs in HCL directly, alongside
general-purpose languages like TypeScript, Python, Go, and C#. To see how it works, see
&lt;a href="https://www.pulumi.com/docs/iac/languages-sdks/hcl/"&gt;Pulumi HCL&lt;/a&gt;.
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;ve been using &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; for a couple of years and overall I&amp;rsquo;ve been quite happy with it. However there&amp;rsquo;s a few things that started to bother me more and more recently.&lt;/p&gt;
&lt;p&gt;Terraform uses a language called Hashicorp HCL to define the infrastructure. It&amp;rsquo;s a relatively simple declarative language, but it&amp;rsquo;s something I had to learn along the way. Just like any language it has it&amp;rsquo;s little quirks, and I often found myself spending more time than I wanted to figure out how to do certain things. As I&amp;rsquo;m doing all the infrastructure myself I really wanted to be able to use a language I&amp;rsquo;m familiar with, to make things simple.&lt;/p&gt;
&lt;p&gt;I had written my own &lt;a href="https://www.terraform.io/docs/language/modules/develop/index.html"&gt;Terraform modules&lt;/a&gt; to organize my code. Modules are a great help when it comes to organizing your code in a logical way, and I strongly recommend it if you haven&amp;rsquo;t tried using them already. However, after using it for a while I realized that the Terraform &lt;a href="https://www.terraform.io/docs/language/expressions/types.html"&gt;type system&lt;/a&gt; left something to be desired. There are a few basic types available, but not much more than that. This caused bad &amp;ldquo;micro interactions&amp;rdquo; between me and Terraform. It was commonplace for me to submit an MR to my Gitlab CI where I passed in a security group &lt;em&gt;name&lt;/em&gt; instead of an &lt;em&gt;arn&lt;/em&gt; or &lt;em&gt;id&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The third and perhaps most important point is that I think a custom language like HCL is the wrong way to go long term. Don&amp;rsquo;t get me wrong - I love the people at Hashicorp and I&amp;rsquo;m forever grateful for Terraform because it&amp;rsquo;s the first product that made me enjoy writing IaC. That being said, I think it&amp;rsquo;s a better choice to write libraries for existing languages, instead of trying to create a configuration language of your own. There&amp;rsquo;s a lot of constructs that I&amp;rsquo;m used to having in Python that I sorely miss having available in Terraform. In my opinion there&amp;rsquo;s no reason to develop your own configuration language since it&amp;rsquo;s a time consuming task, and I don&amp;rsquo;t find it better than a general purpose language anyways.&lt;/p&gt;
&lt;h3 id="why-did-i-choose-pulumi"&gt;Why did I choose Pulumi?&lt;/h3&gt;
&lt;p&gt;I have this thing called &amp;ldquo;Fun time Friday&amp;rdquo; where I spend the first half day on Fridays exploring new things I&amp;rsquo;ve heard about. Most of the things I look at during that time end up being scrapped, and there&amp;rsquo;s zero pressure to push things into production.&lt;/p&gt;
&lt;p&gt;However after exploring Pulumi I felt that it might be significantly better for me than Terraform. I decided to take a day to see if I could migrate my infrastructure.&lt;/p&gt;
&lt;p&gt;Could I have used something else, like &lt;a href="https://aws.amazon.com/cdk/"&gt;AWS CDK&lt;/a&gt;? I most probably could. The nice thing about running a company of one is that there&amp;rsquo;s no need for analysis paralysis! At this point I believed that Pulumi was a better choice for me than Terraform, so I went for it.&lt;/p&gt;
&lt;p&gt;Personally I struggle a bit with CDK being mostly Amazon-centric. I sometimes use my IaC to configure things outside of AWS as well. The other issue for me is that CDK by default uses AWS CloudFormation to provision resources. I&amp;rsquo;ve been bitten a few times by CF. Some times it&amp;rsquo;s been because of buggy behavior, e.g. a stack that refused to terminate. Other times it&amp;rsquo;s been the fact that CF often lags behind in capabilities, so it&amp;rsquo;s common to have to wait a few months for a new feature to be usable through CF.&lt;/p&gt;
&lt;p&gt;Sure, I could use &lt;a href="https://www.hashicorp.com/blog/cdk-for-terraform-enabling-python-and-typescript-support"&gt;CDK for Terraform&lt;/a&gt; to have CDK generate Terraform HCL for me. However, now we&amp;rsquo;re quickly getting away from the simple system I wanted in the first place.&lt;/p&gt;
&lt;p&gt;Apologies to those of you who are screaming &lt;em&gt;&amp;ldquo;FFS - CDK is the best thing ever!&amp;quot;&lt;/em&gt; at your monitors right now. You might be 100% right! From what I&amp;rsquo;ve seen CDK seems pretty awesome for the most part. I didn&amp;rsquo;t end up with Pulumi because CDK seemed bad. I ended up with Pulumi because it seemed like a slightly better fit for my needs :).&lt;/p&gt;
&lt;h3 id="pulumi-in-3-minutes"&gt;Pulumi in 3 minutes&lt;/h3&gt;
&lt;p&gt;You might already know a bit of Pulumi, but here&amp;rsquo;s a very quick overview plus some advice on where to go from here.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/migrating-my-infrastructure-from-terraform-to-pulumi/resources.png" alt="image.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pulumi allows you to write your IaC using TypeScript, Javascript, Python, Go or C#.&lt;/li&gt;
&lt;li&gt;You define &lt;em&gt;resources&lt;/em&gt; which have &lt;em&gt;inputs&lt;/em&gt; and &lt;em&gt;outputs&lt;/em&gt;. As an example, a resource could be an RDS database Instance. One of it&amp;rsquo;s inputs would be the database version to use. One of the outputs would be the hostname / address of the server.&lt;/li&gt;
&lt;li&gt;The outputs are lazily evaluated, and their exact value is often not known until after you&amp;rsquo;ve executed your code. I.e. you wouldn&amp;rsquo;t have the &lt;code&gt;db_instance.address&lt;/code&gt; until the instance had been created. Think of outputs as promises / futures.&lt;/li&gt;
&lt;li&gt;You can use &lt;a href="https://www.pulumi.com/docs/concepts/resources/#components"&gt;component resources&lt;/a&gt; to group your resources into logical groups - similar to how Terraform modules work. If you don&amp;rsquo;t do this there&amp;rsquo;s a high risk you&amp;rsquo;ll end up with a very messy setup further down the line.&lt;/li&gt;
&lt;li&gt;Read Pulumi&amp;rsquo;s own &lt;a href="https://www.pulumi.com/docs/concepts/"&gt;overview&lt;/a&gt; and &lt;a href="https://www.pulumi.com/docs/concepts/resources/#autonaming"&gt;naming&lt;/a&gt; docs before attempting to use Pulumi yourself. I&amp;rsquo;d say that&amp;rsquo;s the most important bit to know about.&lt;/li&gt;
&lt;li&gt;Pulumi are using the &lt;a href="https://en.wikipedia.org/wiki/Open-core_model"&gt;open core&lt;/a&gt; model where most features are free, but some extra goodies are on a paid tier. You can use the open source version and do state management yourself, but you miss out on certain features.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So what does Pulumi code look like? Below is a minimal extract of some of the Python code I&amp;rsquo;ve written (slightly altered).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# __main__.py&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="n"&gt;prod_redis_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RedisServer&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="n"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;redis-prod&amp;#39;&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="n"&gt;route53_zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;example_com_route53_zone&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="n"&gt;route53_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;redis-prod.example.com&amp;#39;&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="n"&gt;security_groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;my_ecs_service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;task_security_group&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="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 class="c1"&gt;# components/redis_server.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RedisServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ComponentResource&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="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&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="bp"&gt;self&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="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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="n"&gt;route53_zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route53&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Route53 Zome where the DNS record will be created.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;route53_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Full DNS name of the CNAME record, e.g. &amp;#39;redis.example.com&amp;#39;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;security_groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecurityGroup&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# Security groups which should be granted access to the server.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&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="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vt:RedisServer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&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="c1"&gt;# Prefix with `self.` if you want to access the security group from outside this component.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;security_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecurityGroup&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="n"&gt;identifier&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="n"&gt;ingress&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="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecurityGroupIngressArgs&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="n"&gt;from_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6379&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="n"&gt;to_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6379&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="n"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp&amp;#39;&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="n"&gt;security_groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;security_groups&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 class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ResourceOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&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="c1"&gt;# Create all the other sub resources needed.&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see it&amp;rsquo;s really simple and clean for me to set up a Redis server now.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also the fact that I can use the Pulumi Console (web interface) to check the state of my infrastructure really quickly. I found myself doing this instead of logging in to the AWS console or using the AWS cli.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/migrating-my-infrastructure-from-terraform-to-pulumi/image1.jpeg" alt="image.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/migrating-my-infrastructure-from-terraform-to-pulumi/image2.jpeg" alt="image.png"&gt;&lt;/p&gt;
&lt;h3 id="how-i-went-about-the-migration"&gt;How I went about the migration&lt;/h3&gt;
&lt;p&gt;After reading the &lt;a href="https://www.pulumi.com/docs/iac/adopting-pulumi/migrating-to-pulumi/from-terraform/"&gt;migration guide&lt;/a&gt; I immediately tried out what seemed to be the obvious option - &lt;a href="https://www.pulumi.com/tf2pulumi/"&gt;tf2pulumi&lt;/a&gt;. This is supposed to be able to convert Terraform code directly to Pulumi. In my case it simply errored out and was not able to convert my Terraform code. To be fair my TF code was using slightly more &amp;ldquo;advanced&amp;rdquo; features like custom written modules, so it wasn&amp;rsquo;t the easiest thing to handle for tf2pulumi. I recommend you try it out, because it sounds great in theory. However it wasn&amp;rsquo;t an option for me, so I had to go for a more manual approach. In retrospect I&amp;rsquo;m actually quite happy I did, because it gave me the opportunity to organise my code in a way that I preferred.&lt;/p&gt;
&lt;div class="note note-info"&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.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;As of Pulumi CLI v3.71.0, &lt;code&gt;tf2pulumi&lt;/code&gt; has been replaced with &lt;code&gt;pulumi convert --from terraform&lt;/code&gt;. &lt;a href="https://www.pulumi.com/blog/converting-full-terraform-programs-to-pulumi/"&gt;Converting Full Terraform Programs to Pulumi blog&lt;/a&gt; has more details.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Don&amp;rsquo;t worry though - &amp;ldquo;manual&amp;rdquo; imports using Pulumi is still rather easy to perform, albeit a bit time consuming. Here&amp;rsquo;s how I did it.&lt;/p&gt;
&lt;p&gt;I started by testing out the &lt;a href="https://www.pulumi.com/docs/iac/get-started/aws/"&gt;AWS getting started guide&lt;/a&gt; to get a feel for how Pulumi worked. This was a great move and I recommend everyone else to do the same.&lt;/p&gt;
&lt;p&gt;I was then ready to start importing my existing resources. The general workflow was to look at my existing Terraform code, rewrite it using Pulumi, and then import the existing resources. It was a bit time consuming in the beginning, but I got a lot faster at it after a few hours.&lt;/p&gt;
&lt;p&gt;Since Pulumi starts out &amp;ldquo;empty&amp;rdquo; you can rewrite and import a piece at a time. I&amp;rsquo;d recommend doing this because you will most probably do some mistakes initially, and it&amp;rsquo;s good to get early feedback.&lt;/p&gt;
&lt;p&gt;You can check the &lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucket/"&gt;API reference&lt;/a&gt; for details on how to import each specific resource. The instructions on how to do it is usually at the bottom of the reference page for each resource type.&lt;/p&gt;
&lt;p&gt;Once I had everything migrated to Pulumi I set up a Gitlab CI/CD flow which allowed me to preview and apply the infrastructure changes. Up until then I&amp;rsquo;d been doing it locally from my own computer. If possible, I recommend doing it that way. The extra time taken by involving a separate CI/CD pipeline would make your migration take a lot longer. It&amp;rsquo;s perfectly fine when everything is done and dusted, however when learning Pulumi you&amp;rsquo;ll often need to use the &lt;code&gt;pulumi&lt;/code&gt; command to look at the resources.&lt;/p&gt;
&lt;h3 id="issues-workarounds-and-general-tips"&gt;Issues, workarounds and general tips&lt;/h3&gt;
&lt;h4 id="use-component-resources-to-group-resources"&gt;Use component resources to group resources&lt;/h4&gt;
&lt;p&gt;When your infrastructure starts growing it becomes hard to manage unless you group your resources. Pulumi&amp;rsquo;s solution to this are the so called &lt;a href="https://www.pulumi.com/docs/concepts/resources/#components"&gt;component resources&lt;/a&gt;. They are &amp;ldquo;logical components&amp;rdquo; which you define yourself.&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
&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;─ vt:RedisServer vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├─ aws:elasticache/parameterGroup:ParameterGroup vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├─ aws:ec2/securityGroup:SecurityGroup vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └─ aws:elasticache/cluster:Cluster vt-redis-prod
&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;As you can see I&amp;rsquo;ve defined a component called &amp;ldquo;vt:RedisServer&amp;rdquo; which I use to group all the resources associated with my Redis server. You&amp;rsquo;ll get a graph representation of your whole infrastructure in the &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/"&gt;Pulumi Web Console&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/migrating-my-infrastructure-from-terraform-to-pulumi/graph.jpeg" alt="image.png"&gt;&lt;/p&gt;
&lt;p&gt;It might seem like a small thing, but it &lt;em&gt;really&lt;/em&gt; helps to have a logical grouping of your resources as your infrastructure grows. It also help because you can suddenly decide which resources your component exposes and which ones are &amp;ldquo;internal&amp;rdquo; to the component. Check out the code sample from earlier in the article to see how to do this using &lt;code&gt;self.&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="importing-a-resource-that-has-a-parent-was-a-bit-confusing-at-first"&gt;Importing a resource that has a parent was a bit confusing at first&lt;/h4&gt;
&lt;p&gt;Each resource in pulumi has a globally unique &lt;a href="https://www.pulumi.com/docs/concepts/resources/#urns"&gt;URN&lt;/a&gt;. You can show the URNs for the infrastructure you&amp;rsquo;ve codeified in pulumi by issuing &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;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├─ vt:RedisServer vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ URN: urn:pulumi:prod::VT::vt:RedisServer::vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├─ aws:elasticache/parameterGroup:ParameterGroup vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ URN: urn:pulumi:prod::VT::vt:RedisServer&lt;span class="nv"&gt;$aws&lt;/span&gt;:elasticache/parameterGroup:ParameterGroup::vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├─ aws:ec2/securityGroup:SecurityGroup vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ URN: urn:pulumi:prod::VT::vt:RedisServer&lt;span class="nv"&gt;$aws&lt;/span&gt;:ec2/securityGroup:SecurityGroup::vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └─ aws:elasticache/cluster:Cluster vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ URN: urn:pulumi:prod::VT::vt:RedisServer&lt;span class="nv"&gt;$aws&lt;/span&gt;:elasticache/cluster:Cluster::vt-redis-prod
&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;Let&amp;rsquo;s have a look at one of the URNs in more detail, shall we?&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;urn:pulumi:prod::VT::vt:RedisServer&lt;span class="nv"&gt;$aws&lt;/span&gt;:elasticache/parameterGroup:ParameterGroup::vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;stack name: prod&lt;/li&gt;
&lt;li&gt;project name: VT&lt;/li&gt;
&lt;li&gt;parent resource type: vt:RedisServer&lt;/li&gt;
&lt;li&gt;resource type: aws:elasticache/parameterGroup:ParameterGroup&lt;/li&gt;
&lt;li&gt;resource name: vt-redis-prod&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All URNs need to be unique, which means every resource of the same type, with the same parent (in the same stack and project) needs to have a unique resource name.&lt;/p&gt;
&lt;p&gt;Normally I would have written and imported my parameter group definition something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# components/redis_server.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RedisServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ComponentResource&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="s2"&gt;&amp;#34;&amp;#34;&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; Redis server running on ElastiCache (single node).
&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;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&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="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vt:RedisServer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&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="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 class="n"&gt;parameter_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elasticache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParameterGroup&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="n"&gt;identifier&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="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;identifier&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="n"&gt;family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;redis6.x&amp;#39;&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="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ResourceOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&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="c1"&gt;# __main__.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vt_prod_redis_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RedisServer&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="n"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vt-redis-prod&amp;#39;&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;And I would then try to import it like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Executing the import (THIS WON&amp;#39;T WORK, DON&amp;#39;T TRY IT)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pulumi import\
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; aws:elasticache/parameterGroup:ParameterGroup\
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; vt-redis-prod\
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first &lt;code&gt;vt-redis-prod&lt;/code&gt; in the command above is the &lt;em&gt;identifier&lt;/em&gt; used by the &lt;code&gt;aws.elasticache.ParameterGroup&lt;/code&gt;. This is actually called &amp;ldquo;resource name&amp;rdquo; using Pulumi-speak. I prefer to call it identifier in my own code though, because &amp;ldquo;resource name&amp;rdquo; is too similar to the actual name it has to AWS for my liking.&lt;/p&gt;
&lt;p&gt;The second &lt;code&gt;vt-redis-prod&lt;/code&gt; is the actual name of my parameter group on AWS (remember we&amp;rsquo;re importing existing stuff).&lt;/p&gt;
&lt;p&gt;However, the above won&amp;rsquo;t work for one simple reason - the URN won&amp;rsquo;t match because we haven&amp;rsquo;t told Pulumi that this parameter group resource we&amp;rsquo;re importing is a child of our &lt;code&gt;RedisServer&lt;/code&gt; component. What&amp;rsquo;s the solution? Pretty simple, but somewhat obscure at the same time:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Executing the import (the right way)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pulumi import\
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --parent vt_prod_redis_server=urn:pulumi:prod::VT::vt:RedisServer::vt-redis-prod\
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; aws:elasticache/parameterGroup:ParameterGroup\
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; vt-redis-prod\
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You tell Pulumi about the parent by using the &lt;code&gt;--parent&lt;/code&gt; argument (this is the simple part). The &lt;em&gt;parent&lt;/em&gt; argument is a key=value pair, and the key is actually the &lt;em&gt;python variable name&lt;/em&gt; you&amp;rsquo;ve used in your code. If you scroll back up you can see that I stored my RedisServer instance in a variable called &lt;code&gt;vt_prod_redis_server&lt;/code&gt;. This part took longer than I&amp;rsquo;d like to admit for me to figure out.&lt;/p&gt;
&lt;p&gt;The value is the URN of the parent component, which you can get with &lt;code&gt;pulumi stack --show-urns&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="you-cant-import-a-child-without-having-the-parent-in-place-first"&gt;You can&amp;rsquo;t import a child without having the parent in place first&lt;/h4&gt;
&lt;p&gt;That last one was a bit heavy, let&amp;rsquo;s take a breather and go for something a bit simpler!&lt;/p&gt;
&lt;p&gt;When importing a child component you need to import the parent first. If you simply run &lt;code&gt;pulumi up&lt;/code&gt; it will try to import all your resources all at once, and it won&amp;rsquo;t work because the parent haven&amp;rsquo;t been created yet.&lt;/p&gt;
&lt;p&gt;Instead simply do this first to update just a single resource:&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 up --target urn:pulumi:prod::VT::vt:RedisServer::vt-redis-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will cause my &lt;em&gt;vt-redis-prod&lt;/em&gt; resource to be created in the Pulumi state. Nothing actually happens on AWS since it&amp;rsquo;s just a logical component. Once this is done you&amp;rsquo;ll be able to import all of it&amp;rsquo;s child resources as you saw above.&lt;/p&gt;
&lt;h2 id="writing-iam-json-policies-with-pulumi-outputs"&gt;Writing IAM (JSON) policies with Pulumi outputs&lt;/h2&gt;
&lt;p&gt;Imagine having an IAM policy that referenced an ECR repository, just as an example. You might want the policy to grant access only to that particular ECR repository. You might be temped to try something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;aws.iam.Policy(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;policy=json.dumps(&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="err"&gt;&amp;#39;Version&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;2012-10-17&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Statement&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Sid&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;1&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Effect&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;Allow&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Action&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;ecr:GetAuthorizationToken&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Resource&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;ecr_repository.arn&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="err"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While this might look sensible, it won&amp;rsquo;t quite work. Remember early on I sad that Pulumi outputs are &lt;em&gt;futures&lt;/em&gt;, and not variables whose values you can access just like that. If you did like the example above your policy would just end up with something similar to &lt;code&gt;'Resource': &amp;lt;Pulumi.Output(...)&amp;gt;&lt;/code&gt; in it. Not quite what we wanted.&lt;/p&gt;
&lt;p&gt;Instead we have to tell Pulumi to resolve these futures, so we get the actual &lt;em&gt;value&lt;/em&gt; for them, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;aws.iam.Policy(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;policy=ecr_repository.arn.apply(lambda&lt;/span&gt; &lt;span class="err"&gt;arn:&lt;/span&gt; &lt;span class="err"&gt;json.dumps(&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="err"&gt;&amp;#39;Version&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;2012-10-17&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Statement&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Sid&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;1&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Effect&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;Allow&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Action&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;ecr:GetAuthorizationToken&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Resource&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;arn&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="err"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have more than one Output you need to access you can use &lt;code&gt;Output.all&lt;/code&gt;, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;policy=(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;Output.all(origin_access_identity.iam_arn,&lt;/span&gt; &lt;span class="err"&gt;self.s&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;_bucket.arn)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;.apply(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;lambda&lt;/span&gt; &lt;span class="err"&gt;args:&lt;/span&gt; &lt;span class="err"&gt;json.dumps(&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="err"&gt;&amp;#39;Version&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;2008-10-17&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Id&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;PolicyForCloudFrontPrivateContent&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Statement&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Sid&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;1&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Effect&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;Allow&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Principal&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;AWS&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;args[0]&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="err"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Action&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;s&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;:GetObject&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;Resource&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;f&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;args[1]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;/*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;.apply&lt;/code&gt; method can also be useful for troubleshooting when you need to access the particular value of an output.&lt;/p&gt;
&lt;p&gt;This might seem tricky at first, but digest this for a bit, then have a look at the docs for &lt;a href="https://www.pulumi.com/docs/concepts/inputs-outputs/"&gt;Inputs and Outputs&lt;/a&gt; and I&amp;rsquo;m sure it&amp;rsquo;ll all make sense.&lt;/p&gt;
&lt;h3 id="dont-name-resources-unless-you-really-have-to"&gt;Don&amp;rsquo;t name resources unless you really have to&lt;/h3&gt;
&lt;p&gt;Pulumi is pretty smart when it comes to naming the actual resources on AWS. If you don&amp;rsquo;t specify a &lt;code&gt;name&lt;/code&gt; when creating a resource Pulumi will use the identifier and &lt;a href="https://www.pulumi.com/docs/support/faq/#why-do-resource-names-have-random-hex-character-suffixes"&gt;append a hex character suffix&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;This gives two great advantages. The first is that you can have multiple stacks next to each other, e.g. a &amp;ldquo;review&amp;rdquo; and a &amp;ldquo;production&amp;rdquo; environment. If you hard coded the names this wouldn&amp;rsquo;t be possible.&lt;/p&gt;
&lt;p&gt;The second advantage is that it makes replacement of resources much easier. If you hard code the name you leave Pulumi no choice but to tear down the existing resource before creating the replacement one. This makes a big difference if you are to perform a version update of one of your dependencies. In case you allow Pulumi to handle the naming it can create the new resource before terminating the previous one. This coupled with a DNS entry update can make for updates with virtually zero downtime.&lt;/p&gt;
&lt;p&gt;Since I imported existing resources I had to keep hard coded names in a few places. However I&amp;rsquo;ve taken the time to get rid of the hard coding as much as possible since then.&lt;/p&gt;
&lt;h3 id="integrating-with-cicd-gitlab"&gt;Integrating with CI/CD (Gitlab)&lt;/h3&gt;
&lt;p&gt;I use Gitlab for my Ci/CD, and I&amp;rsquo;m really happy with it. Let me show you what my very simple Pulumi pipeline looks like right now:&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;stages&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="l"&gt;build&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="nt"&gt;preview&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="nt"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;build&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="nt"&gt;image&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pulumi/pulumi-python:3.2.1 &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Choose the right image for your language&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="nt"&gt;entrypoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# This is important for things to work&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="nt"&gt;script&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="l"&gt;pulumi stack select prod&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="l"&gt;pulumi preview --color always --diff &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# I added &amp;#39;--diff&amp;#39; to always see more details&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="nt"&gt;only&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="l"&gt;merge_requests &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Only run the preview on merge requests&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="nt"&gt;update&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="nt"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;build&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="nt"&gt;image&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pulumi/pulumi-python:3.2.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="nt"&gt;entrypoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&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="nt"&gt;script&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="l"&gt;pulumi stack select prod&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="l"&gt;pulumi update --color always --diff --yes &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Answer &amp;#34;yes&amp;#34; when asking to apply the changes&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="nt"&gt;only&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="l"&gt;master &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Only run the update on the master branch (post merge)&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;As you can see my pipeline is very simple for this little project which is still in an Alpha phase. There&amp;rsquo;s just the &amp;ldquo;production&amp;rdquo; environment, and changes are automatically applied once the MR is merged.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll have to make use of your gitlab-fu (or other-ci-cd-fu) to adapt this to your own needs. Read the &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/"&gt;docs from Pulumi&lt;/a&gt; to get started. It&amp;rsquo;s usually very simple and straight forward!&lt;/p&gt;
&lt;p&gt;I did things quite differently from what&amp;rsquo;s described in the Pulumi docs. I used the ready made image from Docker Hub, while they build their own. I did this to make the whole pipeline quicker. It seems to work really well so far, and I haven&amp;rsquo;t found any downsides to my approach (yet).&lt;/p&gt;
&lt;h3 id="i-couldnt-get-the-gitlab-integration-webhooks-to-work"&gt;I couldn&amp;rsquo;t get the Gitlab integration webhooks to work&lt;/h3&gt;
&lt;p&gt;You are normally able to have Pulumi post a report of the planned changes &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/gitlab-app/"&gt;attached to your merge request&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/migrating-my-infrastructure-from-terraform-to-pulumi/preview.jpeg" alt="image.png"&gt;&lt;/p&gt;
&lt;p&gt;However I couldn&amp;rsquo;t make this work because of authentication issues. With the help of their support (which was excellent btw) we figured out that it was related to the fact that I was using &lt;a href="https://docs.gitlab.com/ee/user/group/"&gt;Gitlab groups&lt;/a&gt; to organise my repositories.&lt;/p&gt;
&lt;p&gt;As things stand right now you have to create a Pulumi Organization for each Gitlab Group. There&amp;rsquo;s a bit of an issue with Pulumi&amp;rsquo;s pricing right now, because the only way to be able to get more than 1 organisation is to go for their enterprise plan.&lt;/p&gt;
&lt;p&gt;I think Pulumi will be fixing this in one way or another in the future. For now I&amp;rsquo;m still pretty happy just checking my changes from the Gitlab job output.&lt;/p&gt;
&lt;h3 id="whats-the-verdict"&gt;What&amp;rsquo;s the verdict?&lt;/h3&gt;
&lt;p&gt;So I&amp;rsquo;ve been using Pulumi for a few weeks now, and here are my impressions.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s really fast compared to Terraform. I didn&amp;rsquo;t even expect this one, but for some reason Pulumi manages to at least &amp;ldquo;feel&amp;rdquo; a lot faster. I haven&amp;rsquo;t done any measurements to verify this, but it definitely feels much faster.&lt;/p&gt;
&lt;p&gt;Their documentation is really good, although it took me a bit to learn to navigate it.&lt;/p&gt;
&lt;p&gt;Having type annotations is great! This makes it a lot more friendly to work with as a developer. No more passing around just plain strings in your code. However the type annotations doesn&amp;rsquo;t quite work in PyCharm because of how the Pulumi code is written. I consider this a PyCharm bug though, and I expect it to be fixed in the future. For now I can simply &lt;em&gt;ctrl+b&lt;/em&gt; the class in question, and read the type annotations from the source code.&lt;/p&gt;
&lt;p&gt;The Pulumi (Web) Console is a neat way to look at your infrastructure. I know there are similar solutions for Terraform, but I hadn&amp;rsquo;t used them personally.&lt;/p&gt;
&lt;p&gt;The developer ergonomics are great. Using your language of choice helps a lot. When running a company of one, like myself, it&amp;rsquo;s really important to limit the number of things you need to learn as much as possible. While HCL is simple it&amp;rsquo;s yet another language to remember. If you&amp;rsquo;re working at a larger company it&amp;rsquo;s a great way to encourage developers to do more infrastructure work.&lt;/p&gt;
&lt;p&gt;Pulumi is an open core type product, and you will have to pay to access the features on the higher tiers. Their current plans are very fairly priced. I myself is using the free community plan, which works great for me. There&amp;rsquo;s obviously the risk that they will remove the free plan in the future. However even if that happens I could either pay the fee, or transfer to state storage to AWS S3, and retain most of the product functionality. This feels like an acceptable level of risk for me.&lt;/p&gt;
&lt;p&gt;My goal was to do the whole migration in one day. In reality it ended up being closer to two days. However, given that I learned Pulumi and migrated ~130 resources in that amount of time I&amp;rsquo;d say that Pulumi is quite easy to get started with.&lt;/p&gt;
&lt;p&gt;All in all, I&amp;rsquo;m very happy with making the transition to Pulumi! I became way more productive managing my infrastructure. At the same time I ended up enjoying the work of doing so a lot more - which is really important too.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This article was originally posted at &lt;a href="https://blog.ekik.org/my-experience-migrating-my-infrastructure-from-terraform-to-pulumi"&gt;https://blog.ekik.org/my-experience-migrating-my-infrastructure-from-terraform-to-pulumi&lt;/a&gt;. Reposted here with permission.&lt;/em&gt;&lt;/p&gt;</description><author>Erik Näslund</author><category>community</category><category>migration</category></item><item><title>Observable Infrastructure as Code</title><link>https://www.pulumi.com/blog/observable-infrastructure-as-code/</link><pubDate>Thu, 20 Jan 2022 08:36:11 -0600</pubDate><guid>https://www.pulumi.com/blog/observable-infrastructure-as-code/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/observable-infrastructure-as-code/index.png" /&gt;
&lt;p&gt;Pulumi community member &lt;a href="https://kay.is/"&gt;Kay Plößer&lt;/a&gt; spent some time digging into setting up observability of a Pulumi deployment using &lt;a href="https://www.honeycomb.io/"&gt;Honeycomb&lt;/a&gt;. Read more to find out all the details on configuring Honeycomb and Pulumi together, with a side dish of &lt;a href="https://www.pulumi.com/docs/using-pulumi/automation-api/"&gt;Automation API&lt;/a&gt;!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Last few weeks, I procrastinated doing real product-related things with setting up monitoring for my infrastructure deployment.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m using &lt;a href="https://dev.to/pulumidev"&gt;@pulumidev&lt;/a&gt;, a cloud-provider independent IaC framework that allows me to define my infrastructure in TypeScript and use whatever provider I need.&lt;/p&gt;
&lt;p&gt;I read something about &lt;a href="https://www.honeycomb.io/"&gt;Honeycomb&amp;rsquo;s&lt;/a&gt; observability approach to monitoring, and it got me thinking about my deployment code. Later, it will probably run with something like GitHub Actions, so it&amp;rsquo;s not on my machine when it executes. This means I need some insights in case things go wrong. But it would also be good to see how the deployment behaves over time. How do new resources affect deployment durations etc.?&lt;/p&gt;
&lt;p&gt;This is what a trace could look like in Honeycomb:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/observable-infrastructure-as-code/example-trace.png"&gt;&lt;img src="https://www.pulumi.com/blog/observable-infrastructure-as-code/example-trace.png" alt="Example Trace"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Why not setting things up before I move deployment from my development machine to the cloud?&lt;/p&gt;
&lt;p&gt;This article will show you how to make your Pulumi deployment observable with &lt;a href="https://dev.to/honeycomb_io"&gt;@honeycomb_io&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/"&gt;A Pulumi account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/install/"&gt;The Pulumi CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A Pulumi stack&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.honeycomb.io/"&gt;A Honeycomb account&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-pulumi-automation-api"&gt;The Pulumi Automation-API&lt;/h2&gt;
&lt;p&gt;This API is a programmatical interface to the Pulumi CLI. Think Puppeteer for Pulumi instead of Chrome.&lt;/p&gt;
&lt;p&gt;Since it controls the CLI, it can do everything the CLI can and more.&lt;/p&gt;
&lt;p&gt;For our use case, we need information about which resource is deployed to tell Honeycomb about it, and it will create nice trace graphs for us.&lt;/p&gt;
&lt;p&gt;To run a &lt;code&gt;pulumi up&lt;/code&gt; command with the automation API, we need the following code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@pulumi/pulumi/automation&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;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createOrSelectStack&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;stackName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;development&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;workDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&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="nx"&gt;main&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;The &lt;code&gt;@pulumi/pulumi&lt;/code&gt; package already includes the automation API, so it&amp;rsquo;s the only one we need.&lt;/p&gt;
&lt;p&gt;We use the &lt;code&gt;LocalWorkspace&lt;/code&gt; object to load our stack. This leaves the wholes stack intact; we can still deploy/destroy it with the CLI.&lt;/p&gt;
&lt;p&gt;Then we call &lt;code&gt;stack.up()&lt;/code&gt; and wait. The outputs generated from the API aren&amp;rsquo;t as pretty as the ones from the CLI, but they contain the same information.&lt;/p&gt;
&lt;p&gt;Also, the &lt;code&gt;up&lt;/code&gt; command of the API allows us to catch all events that are happening during the deployment. This is the data we&amp;rsquo;re after.&lt;/p&gt;
&lt;h2 id="the-deployment-events"&gt;The Deployment Events&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;up&lt;/code&gt; method of the &lt;code&gt;stack&lt;/code&gt; object takes a config object. We can use the &lt;code&gt;onEvent&lt;/code&gt; attribute to listen to all the changes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&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;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we log out every event. If we look at the output, we notice there are different types of events.&lt;/p&gt;
&lt;p&gt;-The &lt;code&gt;preludeEvent&lt;/code&gt; fires at the start with information about the stack config.
-The &lt;code&gt;resourcePreEvent&lt;/code&gt; fires before a resource is deployed, and the &lt;code&gt;resOutputsEvent&lt;/code&gt; fires after it has been deployed.
-The &lt;code&gt;diagnosticEvent&lt;/code&gt; contains eventual errors that could happen.
-The &lt;code&gt;summaryEvent&lt;/code&gt; includes info about what has been done and if e everything was successful.&lt;/p&gt;
&lt;p&gt;The event structure is a bit strange. The events don&amp;rsquo;t have a type attribute we could &lt;code&gt;switch&lt;/code&gt; over, but an attribute called like the type of the event.&lt;/p&gt;
&lt;p&gt;The following code shows how to get the right event, and where the data hides in that event:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;preludeEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preludeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resourcePreEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourcePreEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resOutputsEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resOutputsEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;diagnosticEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diagnosticEvent&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;summaryEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summaryEvent&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;If we run things like this, we get all the information we need to send to Honeycomb. The next step is to tell Honeycomb what&amp;rsquo;s up.&lt;/p&gt;
&lt;h2 id="sending-data-to-honeycomb"&gt;Sending Data to Honeycomb&lt;/h2&gt;
&lt;p&gt;Honeycomb provides us with two libraries. The &lt;a href="https://docs.honeycomb.io/getting-data-in/javascript/beeline-nodejs/"&gt;Node.js Beeline&lt;/a&gt; and &lt;a href="https://docs.honeycomb.io/getting-data-in/javascript/libhoney/"&gt;Libhoney&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Libhoney is a low-level Honeycomb client that allows sending raw events to their service API. The Beeline is an abstraction on top of Libhoney to help with instrumentation and traces.&lt;/p&gt;
&lt;p&gt;Usually, it would be best if you used the Beeline because it&amp;rsquo;s easy to set up and works with Node.js&amp;rsquo;s internal HTTP module and the Express framework out of the box.&lt;/p&gt;
&lt;p&gt;In this case, we aren&amp;rsquo;t building an HTTP server, so the Beelines instrumentation doesn&amp;rsquo;t help much.&lt;/p&gt;
&lt;p&gt;So for our deployment example, I will stick to Libhoney.&lt;/p&gt;
&lt;h2 id="honeycomb-events--traces"&gt;Honeycomb Events &amp;amp; Traces&lt;/h2&gt;
&lt;p&gt;Honeycomb uses events to figure out what&amp;rsquo;s happening in your system. Suppose the events have special attributes, like &lt;code&gt;trace.trace_id&lt;/code&gt; and &lt;code&gt;trace.span_id&lt;/code&gt;, Honeycomb can correlate them. This way, it can tell you things like, &amp;ldquo;The trace for your request took 300ms and consisted of 100ms authentication and 200ms database access.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So you can pump all your information to Honeycomb via events. Sometimes it doesn&amp;rsquo;t need to be correlated; you only want to tell Honeycomb that your server uses 20% of memory. But in our case, we want to tie up all events related to one deployment into a trace, so we can figure out what we deploy, how long it takes, and if something goes wrong, what resource was responsible.&lt;/p&gt;
&lt;h2 id="linking-up-pulumi--honeycomb"&gt;Linking up Pulumi &amp;amp; Honeycomb&lt;/h2&gt;
&lt;p&gt;We need to initialize Libhoney and send the correct &amp;ldquo;Honeycomb event&amp;rdquo; for every &amp;ldquo;Pulumi event.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;But let&amp;rsquo;s start with getting one event into Honeycomb: the &lt;code&gt;pulumi up&lt;/code&gt; event.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&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;Libhoney&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;libhoney&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@pulumi/pulumi/automation&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;hny&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;Libhoney&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;writeKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;HONEYCOMB_API_KEY&amp;gt;&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;dataset&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;example-iac&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&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&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;traceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`trace`&lt;/span&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;rootSpanId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`trace-root-span`&lt;/span&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;startTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&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;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createOrSelectStack&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;stackName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;development&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;workDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&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;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendNow&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;up&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;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;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="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&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="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&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;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We define a &lt;code&gt;traceId&lt;/code&gt;, a &lt;code&gt;rootSpanId&lt;/code&gt;, and a &lt;code&gt;startTimestamp&lt;/code&gt; at the beginning, and then we send it off when everything is done.&lt;/p&gt;
&lt;p&gt;An event with &lt;code&gt;trace.trace_id&lt;/code&gt; will be grouped up with all other events with the same trace ID. In this case, we only have one event.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; is usually the action you perform and want to measure, and the &lt;code&gt;service_name&lt;/code&gt; is, well, the service that performs this action. In this case, we run &lt;code&gt;pulumi up&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the end, we also send a &lt;code&gt;duration_ms&lt;/code&gt; attribute to track how long things take.&lt;/p&gt;
&lt;p&gt;The result will look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/observable-infrastructure-as-code/trace-result.png"&gt;&lt;img src="https://www.pulumi.com/blog/observable-infrastructure-as-code/trace-result.png" alt="Honeycomb trace graph"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Not very interesting, but at least we know it run without crashing and how long it took.&lt;/p&gt;
&lt;h2 id="getting-more-details"&gt;Getting More Details&lt;/h2&gt;
&lt;p&gt;The next step is to get the details. What were the config parameters? How long did different parts of the deployment take?&lt;/p&gt;
&lt;p&gt;For this, we need to look into two events, the &lt;code&gt;preludeEvent&lt;/code&gt; and the &lt;code&gt;summaryEvent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Until now, we only send one Honeycomb event for the Pulumi up command.&lt;/p&gt;
&lt;p&gt;Now, we will send three events for the stack.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;init&lt;/code&gt; event will contain the time it took to get to the actual deployment of resources and the config parameters of the stack&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;run&lt;/code&gt; event will contain the duration all resources took to do their thing.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;finish&lt;/code&gt; event will contain the duration of shutting down the automation API and the summary data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s look at the updated example code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&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;traceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`trace`&lt;/span&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;rootSpanId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`trace`&lt;/span&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;startTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stackFinishStartTimestamp&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;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initStartTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&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;const&lt;/span&gt; &lt;span class="nx"&gt;runStackSpanId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`stack`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stackRunStartTimestamp&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;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createOrSelectStack&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;stackName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;development&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;workDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&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;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;preludeEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initStartTimestamp&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;init&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;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Stack&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="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&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="s2"&gt;&amp;#34;trace.parent_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&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="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`stack`&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;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;initStartTimestamp&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="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preludeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&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="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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;stackRunStartTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&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="k"&gt;return&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;summaryEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stackRunStartTimestamp&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;run&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;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Stack&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="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&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="s2"&gt;&amp;#34;trace.parent_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&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="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;runStackSpanId&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;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;stackRunStartTimestamp&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="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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;stackFinishStartTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&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="k"&gt;return&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 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="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stackFinishStartTimestamp&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;finish&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;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Stack&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="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&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="s2"&gt;&amp;#34;trace.parent_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&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="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`stack`&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;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;stackFinishStartTimestamp&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="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startTimestamp&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;up&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;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;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="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&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="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&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;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&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="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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;First, we need to sprinkle variables on the code that capture the start times of our three events.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;init&lt;/code&gt; event starts before the call to the up command and takes until the &lt;code&gt;preludeEvent&lt;/code&gt; is fired by the automation API.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;run&lt;/code&gt; event starts right after the &lt;code&gt;init&lt;/code&gt; event finishes and takes until the &lt;code&gt;summaryEvent&lt;/code&gt; fires.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;finish&lt;/code&gt; event starts right after &lt;code&gt;run&lt;/code&gt; finishes and stops right before the parent event for Pulumi up is finished.&lt;/p&gt;
&lt;p&gt;These events all get the &lt;code&gt;trace.parent_id&lt;/code&gt; of the Pulumi up event, so they get nested in the Honeycomb view later.&lt;/p&gt;
&lt;p&gt;Also, the &lt;code&gt;init&lt;/code&gt; event gets the &lt;code&gt;event.preludeEvent.config&lt;/code&gt; object, so we can see how the stack was configured inside Honeycomb.&lt;/p&gt;
&lt;p&gt;If we run that example, we should see something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/observable-infrastructure-as-code/second-result.png"&gt;&lt;img src="https://www.pulumi.com/blog/observable-infrastructure-as-code/second-result.png" alt="Honeycomb span with details"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="getting-resource-deployment-durations"&gt;Getting Resource Deployment Durations&lt;/h2&gt;
&lt;p&gt;The resources have additional details for us. As a &lt;code&gt;name&lt;/code&gt; for the event, we will use the operation (create, update, delete) and as &lt;code&gt;service_name&lt;/code&gt; we use the name of the resource in the Pulumi definition.&lt;/p&gt;
&lt;p&gt;To get the parent of a resource, we have to save the URNs of every resource for later. But since we have to keep the start times anyway, we can do this in one step when the &lt;code&gt;resourcePreEvent&lt;/code&gt; is fired.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at the example code. This time I removed some boilerplate code and the other events to make things a bit more readable.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&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;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resourceSpans&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;Map&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="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="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&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;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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="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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resourcePreEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourcePreEvent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;resourceSpans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&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;spanId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`span`&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;startTimestamp&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&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="k"&gt;return&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resOutputsEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resOutputsEvent&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;serviceName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;::&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;3&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;spanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resourceSpans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&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;const&lt;/span&gt; &lt;span class="nx"&gt;parentUrn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;old&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&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;parentId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resourceSpans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parentUrn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spanId&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;runStackSpanId&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;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startTimestamp&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;op&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;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;serviceName&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="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&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="s2"&gt;&amp;#34;trace.parent_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parentId&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="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;spanId&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;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;update&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;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;diffs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;, &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="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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="k"&gt;return&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="c1"&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 class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, what&amp;rsquo;s going on here?&lt;/p&gt;
&lt;p&gt;First, the resources are deployed by their hierarchy, but siblings can be deployed in a different order. That&amp;rsquo;s why we need to save their &lt;code&gt;spanId&lt;/code&gt; and &lt;code&gt;startTimestamp&lt;/code&gt; when we get a &lt;code&gt;resourcePreEvent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I used a &lt;code&gt;Map&lt;/code&gt; for this and keyed it with the URN of the resource because URNs are used for parent-child relationships and are part of all events we care about here.&lt;/p&gt;
&lt;p&gt;Later, when the &lt;code&gt;resOutputsEvent&lt;/code&gt; fires, it&amp;rsquo;s time to wrap it all up and send it to Honeycomb.&lt;/p&gt;
&lt;p&gt;The event has the URN to find the related &lt;code&gt;spanId&lt;/code&gt; and &lt;code&gt;startTimestamp&lt;/code&gt;. We can also use the URN in the parent attribute of our resource to fetch the &lt;code&gt;spanId&lt;/code&gt; of our resource&amp;rsquo;s parent. This way, our spans are correctly nested in Honeycomb later.&lt;/p&gt;
&lt;p&gt;If the operation for the resource is an &lt;code&gt;update&lt;/code&gt;, we also add the &lt;code&gt;diffs&lt;/code&gt; to that event. This way, we can see in Honeycomb what inputs have changed since the last deployment.&lt;/p&gt;
&lt;p&gt;The final trace should look a bit like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/observable-infrastructure-as-code/final-trace.png"&gt;&lt;img src="https://www.pulumi.com/blog/observable-infrastructure-as-code/final-trace.png" alt="Final Honeycomb trace"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="getting-errors"&gt;Getting Errors&lt;/h2&gt;
&lt;p&gt;The last step, getting deployment errors, I leave as an exercise for the reader.&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;ll give you a few hints!&lt;/p&gt;
&lt;p&gt;First, the interesting event is the &lt;code&gt;diagnosticEvent&lt;/code&gt;, which comes with a &lt;code&gt;severity&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt; attribute. If you only need errors, you have to filter out &lt;code&gt;debug&lt;/code&gt; and &lt;code&gt;info&lt;/code&gt; severity.&lt;/p&gt;
&lt;p&gt;Also, the event includes an &lt;code&gt;urn&lt;/code&gt; that can be used to correlate the error to a Honeycomb span later (&lt;code&gt;resourceSpans&lt;/code&gt; etc.). If the errors isn&amp;rsquo;t related to an URN, you can simply use the run stack event as target.&lt;/p&gt;
&lt;p&gt;If you add an &lt;code&gt;error&lt;/code&gt; field to a Honeycomb event, it will automatically counted to your errors in Honeycomb.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;IaC observability becomes rather simple when using Pulumi and Honeycomb.&lt;/p&gt;
&lt;p&gt;Pulumi&amp;rsquo;s automation API allows us to control the CLI with JavaScript, and Honeycomb&amp;rsquo;s Libhoney is low-level enough to instrument every part of our deployment process.&lt;/p&gt;
&lt;p&gt;This way we can keep track which resources are slowing our deployment down, or which was responsible for a failed deployment.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This article was originally posted at &lt;a href="https://dev.to/fllstck/observable-infrastructure-as-code-52ha"&gt;https://dev.to/fllstck/observable-infrastructure-as-code-52ha&lt;/a&gt;. Reposted here with permission.&lt;/em&gt;&lt;/p&gt;</description><author>Kay Plößer</author><category>community</category><category>javascript</category><category>observability</category><category>automation-api</category></item><item><title>Pulumi and LocalStack — beyond the basics</title><link>https://www.pulumi.com/blog/pulumi-and-localstack/</link><pubDate>Mon, 10 Jan 2022 11:10:06 -0600</pubDate><guid>https://www.pulumi.com/blog/pulumi-and-localstack/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/pulumi-and-localstack/index.png" /&gt;
&lt;p&gt;Recently, Pulumi community member Josh Graham decided to bootstrap a simple application using a serverless approach, with a focus on using good engineering practices and being able to run the application locally. Given that Josh is the (OG) SaaS architect of Atlassian and an AWS user, &lt;a href="https://github.com/localstack/localstack#overview"&gt;LocalStack&lt;/a&gt; was a natural choice.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I first encountered &lt;a href="https://www.pulumi.com/"&gt;Pulumi&lt;/a&gt; a few years ago when I was getting a little frustrated at the lack of some expressibility in &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt;. I&amp;rsquo;ve been using Terraform from almost day 1 and it really does the job well &amp;mdash; providing you really grok its declarative nature, are very good at modularisation, and never need an escape hatch to non-declarative instructions. At the time, though, Pulumi wasn&amp;rsquo;t ready for me to use, so I watched from a distance.&lt;/p&gt;
&lt;p&gt;Recently, I decided to bootstrap a simple application using a serverless approach, making it a good opportunity to try Pulumi again. I still wanted to use good engineering practices and be able to run the application locally. As I was the (OG) SaaS architect of Atlassian and am an AWS user, &lt;a href="https://github.com/localstack/localstack#overview"&gt;LocalStack&lt;/a&gt; was a natural choice.&lt;/p&gt;
&lt;p&gt;Pulumi has come a long way since I first saw it. I am super happy with it so far and I&amp;rsquo;m using it for real infrastructure work at the startups I&amp;rsquo;m working with. It seems the software engineers have less cognitive load using a more familiar language to express infrastructure definitions. It is worth noting that Pulumi uses Terraform in various ways under the covers and I&amp;rsquo;m still a big fan of Terraform and Hashicorp in general.&lt;/p&gt;
&lt;h2 id="the-application-and-its-infrastructure"&gt;The Application and its Infrastructure&lt;/h2&gt;
&lt;p&gt;The application I&amp;rsquo;ve based the following sample on is a website with a simple form to capture a visitor&amp;rsquo;s email address, responding with a cookie to set so the site knows not to bother showing them a form the next time they visit.&lt;/p&gt;
&lt;p&gt;In keeping with a serverless goal, I use AWS DynamoDB to store the information. I also want to be able to query for the latest new contacts. In order to try out Pulumi more thoroughly (and although I&amp;rsquo;m no fan of NodeJS), I decided to do the backend on NodeJS in an AWS Lambda so as to try out the Pulumi Cloud API. Both the backend and the infrastructure-as-code with Pulumi are coded using TypeScript.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s lots more to the application, but the extract in the following sample is sufficient to showcase #Pulumi and #LocalStack using a #TDD approach to #IaC for #serverless apps.&lt;/p&gt;
&lt;h2 id="the-sample"&gt;The Sample&lt;/h2&gt;
&lt;p&gt;Unlike every sample or blog I found for either Pulumi or LocalStack, I wanted to organize the code properly. Inevitably, most samples did nothing other than return a simple string as a response (a distributed &amp;ldquo;hello world&amp;rdquo;). Otherwise, everything I found was one big file with no cohesion and no separation of concerns. Even worse, the samples mix the IaC &amp;ldquo;deploy/provision-time&amp;rdquo; code with the application &amp;ldquo;runtime&amp;rdquo; code in the same file. Exactly zero had any tests, let alone ones that were written to specify the expected behaviour.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve divided this article into various sections depending on what you&amp;rsquo;re wanting to focus on.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modularized Pulumi&lt;/li&gt;
&lt;li&gt;Running Pulumi locally with LocalStack&lt;/li&gt;
&lt;li&gt;TDD infrastructure code and an AWS Lambda calling DynamoDB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;d like to follow along or use parts as a template, you can fork the &lt;a href="https://bitbucket.org/delitescere/contact"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="acknowledgements"&gt;Acknowledgements&lt;/h3&gt;
&lt;p&gt;Grazie mille to my former ThoughtWorks colleague &lt;a href="https://twitter.com/javame"&gt;Toni Terreno&lt;/a&gt; for reviewing the article and the code to help smooth out some wrinkles.&lt;/p&gt;
&lt;h2 id="modularized-pulumi"&gt;Modularized Pulumi&lt;/h2&gt;
&lt;p&gt;As with any Pulumi project, it is defined by a &lt;a href="https://bitbucket.org/delitescere/contact/src/main/Pulumi.yaml"&gt;Pulumi.yaml&lt;/a&gt; file. I&amp;rsquo;ve split the &amp;ldquo;infrastructure code&amp;rdquo; &amp;mdash; the code that drives Pulumi &amp;mdash; apart from the application code. This infrastructure code is in the &lt;a href="https://bitbucket.org/delitescere/contact/src/main/infra/"&gt;infra&lt;/a&gt; directory. So, the &lt;a href="https://bitbucket.org/delitescere/contact/src/main/package.json"&gt;package.json&lt;/a&gt; also specifies a &lt;code&gt;main&lt;/code&gt; (even though this app isn&amp;rsquo;t a module for others to include) for &lt;code&gt;pulumi&lt;/code&gt; CLI to find the Pulumi entry point.&lt;/p&gt;
&lt;h2 id="outputs--inputs"&gt;Outputs / Inputs&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s probably a good reason most samples that show how to use Pulumi are one big file. That&amp;rsquo;s because it can be initially difficult to grok the concept of provision-time &amp;ldquo;output&amp;rdquo; and &amp;ldquo;input&amp;rdquo; values. This isn&amp;rsquo;t isolated to Pulumi &amp;mdash; I&amp;rsquo;ve seen similar problems with examples (and real code) with other IaC tools like Terraform, Puppet, and Chef. It&amp;rsquo;s not a big surprise, either. When thinking imperatively or defining infrastructure manually we tend to have a different mindset to the one required to think declaratively.&lt;/p&gt;
&lt;p&gt;Despite the documentation, it can be quite confusing and becomes even more so when you deviate from the &amp;ldquo;one big file&amp;rdquo;. It can become positively baffling when, as this application does, the infrastructure code is aware of the application code and performs the deployment of the application as part of the infrastructure update. I wanted to test the application logic separately from having to run Pulumi, so that separation of code was essential.&lt;/p&gt;
&lt;p&gt;The big thing to remember is that a Pulumi &amp;ldquo;output&amp;rdquo; value &lt;a href="https://www.pulumi.com/docs/concepts/inputs-outputs/"&gt;is not known until after the infrastructure has been fully provisioned&lt;/a&gt;. They are a promise of a future value. If you want to refer to the value in other infrastructure code, Pulumi creates a dependency graph to be able to resolve the value at the right time. If, however, you want to refer to the value at runtime and you want to define that runtime code in a modular way, you have to be very clear about how that value is presented by Pulumi to that runtime code.&lt;/p&gt;
&lt;p&gt;Our infrastructure code provisions a DynamoDB table and an AWS Lambda. That Lambda needs to know the name of that table at runtime in order to query it. Because the &lt;em&gt;actual&lt;/em&gt; name of the table isn&amp;rsquo;t known until provisioning is underway (Pulumi creates many infrastructure resources based on the logical name but with suffixes to avoid naming collisions), we need a way to get the &amp;ldquo;output&amp;rdquo; table name from Pulumi&amp;rsquo;s &lt;code&gt;aws.dynamodb.Table&lt;/code&gt; &lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/dynamodb/table/#example-usage"&gt;API&lt;/a&gt; to the Lambda we&amp;rsquo;ll create with the &amp;ldquo;simpler&amp;rdquo; way to create a Lambda using Pulumi&amp;rsquo;s &lt;code&gt;cloud.API&lt;/code&gt; &lt;a href="https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/cloud/#API"&gt;API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see in the &lt;a href="https://bitbucket.org/delitescere/contact/src/main/infra/contact/api.ts"&gt;ContactApi&lt;/a&gt; infrastructure resource an &lt;code&gt;Inputs&lt;/code&gt; class with &lt;code&gt;static&lt;/code&gt; members. This is the key to transferring, in the simplest way I&amp;rsquo;ve found, values from provision-time infrastructure code to runtime application code. This class is only available in that file, so for each resource that needs to pass provision-time values to runtime code, you can create one specifically for the values you want to pass.&lt;/p&gt;
&lt;p&gt;In the case of &lt;code&gt;ContactApi&lt;/code&gt; we receive a &lt;code&gt;Table&lt;/code&gt; as an &amp;ldquo;input&amp;rdquo;, convert it to an &amp;ldquo;output&amp;rdquo; and store it in a static variable. This variable is then used in the constructor for our &lt;code&gt;Lambda&lt;/code&gt; application class when wiring up the integration between the API Gateway route / method and the Lambda. The way the &lt;code&gt;Lambda&lt;/code&gt; constructor is called in the infrastructure code is the same as we&amp;rsquo;d do from a test or any other application code that wanted to call it.&lt;/p&gt;
&lt;p&gt;I played with various ways to transfer Pulumi inputs to runtime code but this was the most intentional, most decoupled, and least convoluted.&lt;/p&gt;
&lt;h3 id="resources"&gt;Resources&lt;/h3&gt;
&lt;p&gt;As mentioned above, we provision a DynamoDB table, an API Gateway, and a Lambda.&lt;/p&gt;
&lt;p&gt;We could (and indeed I originally did) provision the table with the &amp;ldquo;simpler&amp;rdquo; &lt;code&gt;cloud.Table&lt;/code&gt; &lt;a href="https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/cloud/#Table"&gt;API&lt;/a&gt; but I wanted specific control over the attributes and secondary indexes for DynamoDB (so I could, for example, query for new records since a particular date). The &lt;code&gt;cloud.Table&lt;/code&gt; abstraction works across cloud platform providers, and there may be mechanisms to offer finer-grained / platform-specific options (but I haven&amp;rsquo;t found them yet).&lt;/p&gt;
&lt;p&gt;We do provision a Lambda, &lt;em&gt;but with the actual simplicity of this application&amp;rsquo;s needs, we could dispense with the Lambda and have the API Gateway do a non-proxy integration straight to DynamoDB&amp;rsquo;s HTTP API&lt;/em&gt;. Given many applications would include a Lambda somewhere along the way, I thought it was worth showing Pulumi provisioning a Lambda and integrating it to an API Gateway using it&amp;rsquo;s &lt;code&gt;cloud.API&lt;/code&gt; abstraction.&lt;/p&gt;
&lt;h3 id="aws-tags"&gt;AWS tags&lt;/h3&gt;
&lt;p&gt;Because we&amp;rsquo;re using AWS, we can set default tags on taggable resources. These can be put into the configuration file (&lt;code&gt;Pulumi.&amp;lt;stack&amp;gt;.yaml&lt;/code&gt;) which is great for static / non-computed, optional tags.&lt;/p&gt;
&lt;p&gt;If we want to ensure certain tags are applied, even if they were omitted from the stack-specific configuration, or the tag keys or values are computed, then we can use the &lt;code&gt;registerStackTransformation&lt;/code&gt; method of Pulumi runtime. Our &lt;code&gt;[registerAutoTags](https://bitbucket.org/delitescere/contact/src/main/infra/registerAutoTags.ts)&lt;/code&gt; (based on some code by Joe Duffy) function does this nicely for us, while including resource-specific tags and overrides.&lt;/p&gt;
&lt;h2 id="pulumi--localstack"&gt;Pulumi + LocalStack&lt;/h2&gt;
&lt;p&gt;Using LocalStack to emulate AWS services on your local machine is a great way to get faster feedback, isolate errors, and reduce costs. It&amp;rsquo;s obviously not a perfect emulation but for most purposes, it is more than sufficient.&lt;/p&gt;
&lt;h3 id="stack-templates"&gt;Stack templates&lt;/h3&gt;
&lt;p&gt;Because Pulumi doesn&amp;rsquo;t (&lt;a href="https://github.com/pulumi/pulumi/issues/2307"&gt;yet&lt;/a&gt;!) support project-wide configuration, I have a project-wide template &lt;code&gt;stack.template.yaml&lt;/code&gt; (for any stack) and a local stack template &lt;code&gt;local-stack.template.yaml&lt;/code&gt; (just for stacks targeting LocalStack).&lt;/p&gt;
&lt;p&gt;These templates are merged and interpolated with shell environment variables in the &lt;code&gt;init-local-stack&lt;/code&gt; script. Keys in the local template override keys in the non-local template.&lt;/p&gt;
&lt;p&gt;You may wish to use a similar technique to the &lt;code&gt;concatAndInterpolateTemplates&lt;/code&gt; function for non-local stacks. Perhaps each engineer has their own stack, but you have configuration keys that everyone should set, that either have common values or machine-specific values. In combination with various &lt;code&gt;.env&lt;/code&gt; files (including &lt;code&gt;.env&lt;/code&gt; files per-stack), this could be quite useful for those of you, like &lt;a href="https://twitter.com/javame"&gt;Tony&lt;/a&gt;, with many engineers (who have a stack each) and also many shared stacks.&lt;/p&gt;
&lt;p&gt;In a subsequent article, I&amp;rsquo;ll write about how to set things up if you&amp;rsquo;re using multiple AWS accounts, across multiple regions, each with multiple VPCs.&lt;/p&gt;
&lt;h3 id="dockerized-lambdas"&gt;Dockerized Lambdas&lt;/h3&gt;
&lt;p&gt;One issue I encountered was because the Lambda I wrote uses the NodeJS runtime, LocalStack will create a Docker container to run it in. While much of the necessary environment was copied to that container, a subtle bug in LocalStack&amp;rsquo;s Lambda executor failed to include the AWS credentials in the environment. This meant that any other resources the Lambda needs to access (like a DynamoDB table) would fail to authorize as the requests could not be authenticated. I fixed the bug, added some tests, and the behavior is now correct from LocalStack version &lt;code&gt;0.12.16&lt;/code&gt; (available since 1 Aug, 2021.&lt;/p&gt;
&lt;p&gt;Using the included &lt;a href="https://bitbucket.org/delitescere/contact/src/main/docker-compose.yml"&gt;docker-compose&lt;/a&gt; file, when you &lt;code&gt;docker compose up --detach&lt;/code&gt; you will have a LocalStack container running with the requisite faked AWS services for this application.&lt;/p&gt;
&lt;p&gt;If you ever want to get a shell into that container, you can either use standard &lt;code&gt;docker compose exec&lt;/code&gt; commands if you didn&amp;rsquo;t install &lt;code&gt;localstack&lt;/code&gt;, or use &lt;code&gt;MAIN_CONTAINER_NAME=$(basename $PWD)_localstack_1 localstack ssh&lt;/code&gt; if you did install it (you don&amp;rsquo;t &lt;em&gt;have&lt;/em&gt; to install it because Docker Compose is handling it for you).&lt;/p&gt;
&lt;p&gt;The docker-compose file also mentions what to change if you&amp;rsquo;re going to be doing lots of tests / Lambda invocations. In this case, you pay the price of a slower &lt;code&gt;pulumi up&lt;/code&gt;&amp;mdash;because the Lambda creation is writing lots of files, particularly &lt;code&gt;node_modules&lt;/code&gt;&amp;mdash; to the file system. If that is being synchronized to the host file system, in my single Lambda case, the infrastructure creation time goes from about 20 seconds to about 50 seconds. However, you get back time in Lambda invocations when &lt;code&gt;LAMBDA_REMOTE_DOCKER&lt;/code&gt; is falsey which takes off roughly 1/3 of the time to execute a Lambda.&lt;/p&gt;
&lt;h2 id="tdd-infra--lambda--db"&gt;TDD Infra + Lambda + DB&lt;/h2&gt;
&lt;p&gt;This test set up might not look like many JavaScript projects you&amp;rsquo;ve encountered. We have &amp;ldquo;unit&amp;rdquo; tests segregated from &amp;ldquo;functional&amp;rdquo; tests. If we had more things to test, I might have &amp;ldquo;integration&amp;rdquo; tests, too.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re curious about the different approaches to unit testing I&amp;rsquo;ve employed here, please read the excellent (now almost two decades old!) &lt;a href="https://martinfowler.com/articles/mocksArentStubs.html"&gt;&amp;ldquo;Mocks aren&amp;rsquo;t Stubs&amp;rdquo; article&lt;/a&gt; by my friend and &lt;a href="https://thoughtworks.medium.com/"&gt;Thoughtworks&lt;/a&gt; Chief Scientist, &lt;a href="https://twitter.com/martinfowler"&gt;Martin Fowler&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="unit-tests"&gt;Unit tests&lt;/h3&gt;
&lt;p&gt;These are &amp;ldquo;pure&amp;rdquo; logic tests requiring just the JavaScript class under test and test-doubles for infrastructure dependencies.&lt;/p&gt;
&lt;p&gt;In the case of our contact &lt;code&gt;Table&lt;/code&gt; class, we mock the DynamoDB SDK and stub responses from its &lt;code&gt;getItem&lt;/code&gt; and &lt;code&gt;putItem&lt;/code&gt; methods to match the case we want to test. In the case of our contact &lt;code&gt;Lambda&lt;/code&gt; class, we mock its&lt;code&gt;Table&lt;/code&gt; class and stub responses from its &lt;code&gt;findOne&lt;/code&gt; and &lt;code&gt;insert&lt;/code&gt; methods, and we also need to mock the Pulumi Cloud API&amp;rsquo;s &lt;code&gt;Request&lt;/code&gt; and &lt;code&gt;Response&lt;/code&gt; classes.&lt;/p&gt;
&lt;p&gt;Although the infrasructure-as-code in our project driving Pulumi is fairly declarative and therefore doesn&amp;rsquo;t really need testing, we do have a little logic that should be tested. In those cases, Pulumi provides the capability set mocks for testing purposes so you don&amp;rsquo;t have to be running the Pulumi engine for unit tests.&lt;/p&gt;
&lt;p&gt;Unit tests can be run with &lt;code&gt;npm test unit&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="stack-class"&gt;Stack class&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;ve abstracted the notion of a &amp;ldquo;stack&amp;rdquo; into something that is useful in the context of having using LocalStack or a real AWS region, and having &amp;ldquo;production&amp;rdquo; and non-production environments. &lt;em&gt;As mentioned earlier, things can get a lot more complicated than that for more sophisticated AWS users, which I&amp;rsquo;ll write about in a subsequent article.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the jest config we include &lt;code&gt;tests/env.ts&lt;/code&gt; as a setup file. This sets the environment variables before imports and static initialization, allowing us to test things like &lt;code&gt;aws.config.region&lt;/code&gt; having been set from the environment. You can also set this value in the &lt;code&gt;Pulumi.&amp;lt;stack&amp;gt;.yaml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;We use the &lt;code&gt;pulumi.runtime.setMocks&lt;/code&gt; function so that we can mock the parts of Pulumi called by our &lt;code&gt;Stack&lt;/code&gt; class (&lt;code&gt;getProject&lt;/code&gt; and &lt;code&gt;getStack&lt;/code&gt;). We now specify the logic for when a resource is qualified by the stack name (e.g. &lt;code&gt;contact-sandbox&lt;/code&gt; for the sandbox stack versus &lt;code&gt;contact&lt;/code&gt; for the production stack), and also for when a DNS record is qualified by the stack name (e.g. &lt;code&gt;www.example.com&lt;/code&gt; for the production environment record but &lt;code&gt;www.test.example.com&lt;/code&gt; for the test environment record).&lt;/p&gt;
&lt;h3 id="result-type"&gt;Result type&lt;/h3&gt;
&lt;p&gt;I don&amp;rsquo;t particularly like the JavaScript approach to Promises and &lt;a href="https://avaq.medium.com/broken-promises-2ae92780f33"&gt;its many flaws&lt;/a&gt;. However, as this exercise is for a fairly simple function-as-a-service, I didn&amp;rsquo;t want to go down the path of using a replacement approach like &lt;a href="https://github.com/fluture-js/Fluture"&gt;Fluture&lt;/a&gt;. I did, however, want to work with the results of promises using a something approximating a sum type and be able to transform exceptions and rejected promises into the &amp;ldquo;not right&amp;rdquo; side of that type.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Result&lt;/code&gt; type uses lightweight type checking and convenience methods. All the Promises we are working with end up resolving to a &lt;code&gt;Result&lt;/code&gt; which is either an &lt;code&gt;Error&lt;/code&gt; or a non-error value. We&amp;rsquo;re careful to catch rejected Promises or exceptions and turn them into a resolved &lt;code&gt;Promise&amp;lt;Error&amp;gt;&lt;/code&gt;or &lt;code&gt;Error&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="table-class"&gt;Table class&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;Table&lt;/code&gt; class in &lt;code&gt;contact&lt;/code&gt; only has two methods so far, &lt;code&gt;findOne&lt;/code&gt; and &lt;code&gt;insert&lt;/code&gt;, because that&amp;rsquo;s all the application needs right now. You can imagine other applications might need other typical methods like &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;findOneByEmail&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;, and so on.&lt;/p&gt;
&lt;p&gt;To use this class in a unit test, we need to mock the &lt;code&gt;DynamoDB&lt;/code&gt; client and provide various stubs for its &lt;code&gt;getItem&lt;/code&gt; and &lt;code&gt;putItem&lt;/code&gt; methods. Each of these stubs tests a different code path in how &lt;code&gt;Table&lt;/code&gt; handles responses from the database.&lt;/p&gt;
&lt;p&gt;If we had more than one table to interact with, there&amp;rsquo;d probably be an interface and perhaps an abstract class or other mechanism of providing behavior common to all tables.&lt;/p&gt;
&lt;h3 id="lambda-class"&gt;Lambda class&lt;/h3&gt;
&lt;p&gt;Given the combination of inputs to the lambda and processing issues that may arise, we want to be able to test the lambda behaves in the expected way and handles those issues as specified.&lt;/p&gt;
&lt;p&gt;We provide stub responses on the &lt;code&gt;Table&lt;/code&gt; class methods to stimulate successful and unsuccessful operations. We also stub the input to the lambda (because we&amp;rsquo;re using the Pulumi Cloud API this is a &lt;code&gt;pulumi.cloud.Request&lt;/code&gt;) to initiate different logic in the lambda.&lt;/p&gt;
&lt;p&gt;The result of the lambda is a &lt;code&gt;pulumi.cloud.Response&lt;/code&gt; which we mock to capture the HTTP status code generated, which is sufficient to verify almost all of the behavior of this lambda. The other thing we need to verify is that a HTTP cookie is set when either a new item is created or a matching previously created item is found.&lt;/p&gt;
&lt;h3 id="abstractlambda-class"&gt;AbstractLambda class&lt;/h3&gt;
&lt;p&gt;Here are some convenience methods that lambdas might generally use. We have two for dealing with when the lambda is running on LocalStack and two for working with HTTP request headers and JSON entities.&lt;/p&gt;
&lt;p&gt;Normally, the &amp;ldquo;endpoint&amp;rdquo; for AWS is left undefined, which is interpreted bu the AWS SDK as &amp;ldquo;use the default for the region&amp;rdquo;. When running in LocalStack, however, we set it to the URL that LocalStack is listening to on our local machine.&lt;/p&gt;
&lt;p&gt;When running locally, we also include error information in the HTTP Response to help make diagnosing issues during development easier. &lt;em&gt;(I&amp;rsquo;m still shocked that the NodeJS&lt;/em&gt;&lt;code&gt;Error&lt;/code&gt;&lt;em&gt;has non-enumerable properties that mean&lt;/em&gt;&lt;code&gt;*JSON.stringify*&lt;/code&gt;&lt;em&gt;is useless on instances of it).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To handle the situation where, depending on the HTTP client&amp;rsquo;s approach and because the NodeJS libraries don&amp;rsquo;t normalize for us, the same HTTP request header may either be repeated in the request or have been concatenated with multiple values (with some delimiter, usually a comma), we have a convenience method to deal with that.&lt;/p&gt;
&lt;p&gt;To avoid lots of conditionals and error checking when parsing the request entity, we have a convenience method to parse it as JSON. I hope you like the Easter Egg in the test &lt;a href="http://www.thattommyhall.com/"&gt;Tommy Hall&lt;/a&gt; and Dave Coombes 😆&lt;/p&gt;
&lt;h3 id="functional-tests"&gt;Functional tests&lt;/h3&gt;
&lt;p&gt;These test the application as it would be used in production. The infrastructure must be set up, the application must be running, and the tests interact with the application like a user or integrating system would.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;[bin/test](https://bitbucket.org/delitescere/contact/src/main/bin/test)&lt;/code&gt; script does the heavy-lifting of starting LocalStack, initializing a Pulumi stack for testing, running the Jest tests, and pulling everything down again, all handily invoked directly or more likely just with &lt;code&gt;npm test&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If something goes wrong with the tests, or you use &lt;code&gt;KEEP_TEST_STACK=1 npm test&lt;/code&gt;, the Pulumi test stack and LocalStack container stays around for diagnostic purposes.&lt;/p&gt;
&lt;p&gt;I have found that this test can be &amp;ldquo;brittle&amp;rdquo; due to timeouts when the LocalStack container is low on resources, or your computer is running slow and the Pulumi automation API results are slow (it seems exec&amp;rsquo;ing a &lt;code&gt;pulumi stack outputs&lt;/code&gt; command from the test is more efficient, albeit clumsier).&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;docker compose down --volumes&lt;/code&gt; to totally clear out the LocalStack environment.&lt;/p&gt;
&lt;h3 id="contact-resource"&gt;Contact resource&lt;/h3&gt;
&lt;p&gt;Given everything else has been tested in unit tests, this functional test doesn&amp;rsquo;t need to do much. However, it gives us confidence of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;that Pulumi has hooked up the API Gateway and Lambda properly&lt;/li&gt;
&lt;li&gt;that LocalStack is emulating the AWS services we need sufficiently well&lt;/li&gt;
&lt;li&gt;and most importantly, that our Lambda actually does talk to DynamoDB correctly at runtime (as the unit tests have mocked this out)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s it, I hope this has given you some hope that your Pulumi code doesn&amp;rsquo;t have to be a big ball of mud, and that using LocalStack to try it out before hitting AWS is straightforward.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This article was originally posted at &lt;a href="https://delitescere.medium.com/pulumi-and-localstack-beyond-the-basics-d993f3b94d17"&gt;https://delitescere.medium.com/pulumi-and-localstack-beyond-the-basics-d993f3b94d17&lt;/a&gt;. Reposted here with permission.&lt;/em&gt;&lt;/p&gt;</description><author>Josh Graham</author><category>aws</category><category>community</category></item></channel></rss>