<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Customers</title><link>https://www.pulumi.com/blog/category/customers/</link><description>Pulumi blog posts: Customers.</description><language>en-us</language><pubDate>Thu, 21 May 2026 06:00:00 -0700</pubDate><item><title>Seven Rules for Building an AI-Native Software Factory</title><link>https://www.pulumi.com/blog/seven-rules-ai-native-software-factory/</link><pubDate>Thu, 21 May 2026 06:00:00 -0700</pubDate><guid>https://www.pulumi.com/blog/seven-rules-ai-native-software-factory/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/seven-rules-ai-native-software-factory/index.png" /&gt;
&lt;p&gt;&lt;em&gt;Ewan Dawson is CTO of &lt;a href="https://compostable.ai/"&gt;Compostable AI&lt;/a&gt;, where five engineers run an AI-native software factory: nineteen clients, custom AWS deployments, most of them shipped within a day of contract signing. This article is adapted from his recent Pulumi webinar, and covers rules in more depth than we had time for on stage.&lt;/em&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/oHNdlWlsR-w?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;For the past twenty years, I&amp;rsquo;ve viewed software development as a craft. The best engineers drew on decades of experience to get every function right.&lt;/p&gt;
&lt;p&gt;But two years into the agentic AI revolution, I realised software is going to look more like a factory than a craft. The economics have changed. We can&amp;rsquo;t treat code as bespoke anymore. To scale, we have to think industrial — use the tools to ship more value with fewer engineers.&lt;/p&gt;
&lt;p&gt;I joined Compostable AI soon after it was founded 2.5 years ago, and I built the engineering org AI-native from day one. The technology has come a long way since then, and so has my understanding of what AI-native actually means. Here are seven rules I keep coming back to.&lt;/p&gt;
&lt;div class="note note-info note-large"&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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.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;strong&gt;An AI software factory&lt;/strong&gt; is a software operation where autonomous agents write and ship most of the code. The engineers stop writing it by hand and spend their time deciding what gets built and talking to customers. The rules below are our rules for building and running an AI-native software factory.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="1-transform-dont-enhance"&gt;1. Transform, don&amp;rsquo;t enhance&lt;/h2&gt;
&lt;img src="rule-1-chrysalis.png" alt="" class="float-right w-32 ml-4 mb-2 mt-1 rounded-lg sm:w-40 sm:ml-6 lg:w-44"&gt;
&lt;p&gt;Going AI-native isn&amp;rsquo;t an upgrade to your existing process. If you treat AI as a way to hand your developers smarter tools, you leave most of the value on the table. You get the leverage by rebuilding how you write software — and the culture and processes around it.&lt;/p&gt;
&lt;p&gt;I know that&amp;rsquo;s a tall order for a large, mature engineering org. My advice: start small. Pick one team or one business area and run it as a fully AI-native function. Take what you learn and roll it out from there. And do the political work early, especially with your Governance, Risk, and Compliance function. Get GRC on your side early. Otherwise AI becomes a compliance fight instead of a structural advantage.&lt;/p&gt;
&lt;div class="note note-tip note-large"&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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.svg#p-lightbulb-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;&lt;strong&gt;Don&amp;rsquo;t bolt AI onto your existing workflow. Redesign the workflow around what agents can do.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most of the leverage in this technology comes from rebuilding around it. The tool change is the small part.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="2-remove-the-problem-dont-solve-it"&gt;2. Remove the problem, don&amp;rsquo;t solve it&lt;/h2&gt;
&lt;img src="rule-2-knot.png" alt="" class="float-left w-32 mr-4 mb-2 mt-1 rounded-lg sm:w-40 sm:mr-6 lg:w-44"&gt;
&lt;p&gt;Going AI-native flips which problems are hard and which are easy. The right move often isn&amp;rsquo;t to engineer a solution. It&amp;rsquo;s to reframe the problem so it goes away.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example. Serving multiple clients with agents writing the code, blast radius wasn&amp;rsquo;t a hypothetical. One bad agent run could trash a customer&amp;rsquo;s database, or leak one client&amp;rsquo;s data into another&amp;rsquo;s. Our instinct was to build a secure multi-tenant sandbox with guardrails, approvals, rollback. But every version we tried still had agents loose in a shared environment, one bug away from making one customer&amp;rsquo;s data visible to another&amp;rsquo;s. So we removed the problem: every client gets two dedicated AWS accounts, one for production and one &amp;ldquo;digital twin&amp;rdquo; staging account. Agents iterate on staging until the work checks out. Only then does it ship to production. We have nineteen accounts now, one per client.&lt;/p&gt;
&lt;p&gt;Managing nineteen AWS accounts with five engineers used to be an administrative nightmare. When code is cheap, infrastructure-as-code tools like AWS Control Tower and Pulumi make it the easier path.&lt;/p&gt;
&lt;div class="note note-tip note-large"&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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.svg#p-lightbulb-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;&lt;strong&gt;Remove the problem before you try to solve it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s cheaper to reframe the problem than to engineer your way through it.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="3-pick-tools-your-agents-can-drive"&gt;3. Pick tools your agents can drive&lt;/h2&gt;
&lt;img src="rule-3-wrench.png" alt="" class="float-right w-32 ml-4 mb-2 mt-1 rounded-lg sm:w-40 sm:ml-6 lg:w-44"&gt;
&lt;p&gt;Removing problems is the process side. The other side is tooling. If you want an automated factory, your tech stack has to be something agents can drive. This overlaps a lot with tools that have great developer experience. If a tool has a robust API plus a clean CLI, agents can drive it. If it&amp;rsquo;s heavy click-ops around a web UI, agents stop there.&lt;/p&gt;
&lt;p&gt;We didn&amp;rsquo;t get there first try. Our first IaC tool worked fine when we had a couple of clients. As we added more, accounts drifted, deployments slowed, retries got complicated. We needed something built for where we were heading.&lt;/p&gt;
&lt;p&gt;I went looking, and &lt;a href="https://www.pulumi.com/"&gt;Pulumi&lt;/a&gt; fit. We express infrastructure as type-safe code — TypeScript, in our case, rather than HCL — and agents are good at writing it. Pair that with &lt;a href="https://www.pulumi.com/product/neo/"&gt;Pulumi Neo&lt;/a&gt; — pre-loaded with domain-specific Pulumi skills — and we ship infrastructure that follows best practices. One of my colleagues put it: &amp;ldquo;The scary thing about Neo is it just seems to know everything about what we do.&amp;rdquo; Pulumi IaC plus &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/esc/"&gt;Pulumi ESC&lt;/a&gt; for configuration beats stitching tools together. And TypeScript lets us build higher-level abstractions that keep the AWS account fleet tractable.&lt;/p&gt;
&lt;blockquote class="pullquote"&gt;
&lt;p&gt;&amp;ldquo;I don&amp;rsquo;t actually care if it&amp;rsquo;s HCL or TypeScript, as long as my software development agents can write it. And they do a better job with TypeScript than HCL.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="note note-tip note-large"&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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.svg#p-lightbulb-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;&lt;strong&gt;Tools have to share your AI-native mindset. If they don&amp;rsquo;t integrate deeply, the human becomes the glue.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If part of your stack still requires a human to click through a web UI to provision an account, your agents stop there.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="4-dont-let-one-agent-do-everything"&gt;4. Don&amp;rsquo;t let one agent do everything&lt;/h2&gt;
&lt;img src="rule-4-constellation.png" alt="" class="float-left w-32 mr-4 mb-2 mt-1 rounded-lg sm:w-40 sm:mr-6 lg:w-44"&gt;
&lt;p&gt;When I first started with agents, I reached for a god prompt: one massive system prompt meant to guide a single agent through the whole software lifecycle. It didn&amp;rsquo;t work. Agents struggle when you give them multiple goals. The writer is lenient on its own work — it won&amp;rsquo;t catch what it just shipped. You don&amp;rsquo;t want it reviewing the code, checking for security flaws, or hunting bugs.&lt;/p&gt;
&lt;p&gt;We get better results from a constellation of specialized agents, each handling one part of the line. Pulumi Neo handles infrastructure. Alongside it sit agents specialized in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code implementation&lt;/li&gt;
&lt;li&gt;Code review and testing&lt;/li&gt;
&lt;li&gt;Security auditing&lt;/li&gt;
&lt;li&gt;Internal standards compliance&lt;/li&gt;
&lt;li&gt;Documentation updates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tasks pass down the line. Clean code comes out the other end, with almost no human involved.&lt;/p&gt;
&lt;div class="note note-tip note-large"&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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.svg#p-lightbulb-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;&lt;strong&gt;Don&amp;rsquo;t let any agent mark its own homework. Specialize by job.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Treat agents the way you&amp;rsquo;d treat a team. The one who writes the code shouldn&amp;rsquo;t be the one signing it off.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="5-measure-human-hours-per-unit-of-value"&gt;5. Measure human hours per unit of value&lt;/h2&gt;
&lt;img src="rule-5-hourglass.png" alt="" class="float-right w-32 ml-4 mb-2 mt-1 rounded-lg sm:w-40 sm:ml-6 lg:w-44"&gt;
&lt;p&gt;Once we had agents writing and agents reviewing, throughput went up — but the bottleneck moved past the PR. Engineering hours were still the most expensive thing in the building, so my core metric is human hours per unit of value produced. Minimize that.&lt;/p&gt;
&lt;p&gt;That means hunting for every step that still goes through a person — especially the mid-pipeline steps between ideation and production. Automate the human touchpoints along that line, and the factory runs 24/7.&lt;/p&gt;
&lt;p&gt;Pushing automation this hard also forces good engineering. A chaotic, undocumented process is impossible to automate. Good engineering is still good engineering, AI or not. Agents won&amp;rsquo;t fix a weak process.&lt;/p&gt;
&lt;div class="note note-tip note-large"&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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.svg#p-lightbulb-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;&lt;strong&gt;Measure human hours per unit of value. Treat every one as a bottleneck to remove.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can&amp;rsquo;t automate what you can&amp;rsquo;t describe. Every human in the pipeline marks a piece that hasn&amp;rsquo;t been described yet.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="6-design-for-convergence-not-one-shot-correctness"&gt;6. Design for convergence, not one-shot correctness&lt;/h2&gt;
&lt;img src="rule-6-spiral.png" alt="" class="float-left w-32 mr-4 mb-2 mt-1 rounded-lg sm:w-40 sm:mr-6 lg:w-44"&gt;
&lt;p&gt;Even with the human touchpoints removed, the agents don&amp;rsquo;t ship right the first try. Once you embrace the factory pipeline, you stop needing them to. We design for convergence instead — a system that lands on the right answer through automated iteration.&lt;/p&gt;
&lt;p&gt;The loop we run looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Refinement:&lt;/strong&gt; agents iterate on the Product Requirements Document until the problem is clear.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Planning:&lt;/strong&gt; agents draft multiple technical approaches, and evaluation agents pick the best one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implementation:&lt;/strong&gt; coding agents write the software.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review:&lt;/strong&gt; specialized checking agents look for bugs, API misuse, and security flaws.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the checkers find a problem, they hand it back to the implementation agent. The loop repeats until the tests pass and the agents agree on a clean PR. Once it converges, we merge and deploy to staging.&lt;/p&gt;
&lt;p&gt;Two things have to be true. You need a way to evaluate the output. Without that, you don&amp;rsquo;t know when to stop. And the loop has to converge — each pass has to get closer. A checker that fails every PR for a different reason isn&amp;rsquo;t helping — it just keeps the work going in circles. The feedback has to narrow the search, not widen it.&lt;/p&gt;
&lt;p&gt;Once it converges, the question moves on. How cheap can we make it? Lower the time to PR, reduce token count, drop the overall cost. The optimization never really ends.&lt;/p&gt;
&lt;div class="note note-tip note-large"&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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.svg#p-lightbulb-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;&lt;strong&gt;Don&amp;rsquo;t aim for one-shot correctness. Design for convergence.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It doesn&amp;rsquo;t matter how many tries it takes, as long as the loop closes without a human in it. Get convergence first. The optimization comes after.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="7-run-the-factory-in-the-cloud-not-on-a-laptop"&gt;7. Run the factory in the cloud, not on a laptop&lt;/h2&gt;
&lt;img src="rule-7-cloud.png" alt="" class="float-right w-32 ml-4 mb-2 mt-1 rounded-lg sm:w-40 sm:ml-6 lg:w-44"&gt;
&lt;p&gt;Even a converged factory has to live somewhere. Try running a fully automated factory on individual developers&amp;rsquo; laptops, and it falls apart. Laptops are highly trusted machines. Put autonomous agents on them and your security posture drops, fast. And the factory has to run 24/7. Events come from elsewhere — PR comments, Slack threads, errors in test environments.&lt;/p&gt;
&lt;p&gt;Cloud also kills configuration drift across a dozen developer machines. The same prompts run against different model versions, and env vars sit half-set on half the laptops. The thing you&amp;rsquo;re trying to optimize lives in different states across the team. Cloud isn&amp;rsquo;t just where the factory runs; it&amp;rsquo;s the only place a team can iterate on it together. Keep everything in one place — AWS, Pulumi Cloud, GitHub. The specific stack matters less than the principle of one place.&lt;/p&gt;
&lt;p&gt;And the part that matters most: the factory keeps running, testing, and deploying long after we&amp;rsquo;ve closed our laptops and gone to sleep — the &lt;a href="https://www.pulumi.com/blog/dark-factory-pattern-pulumi-autonomous-iac/"&gt;dark factory&lt;/a&gt; pattern, where the line keeps producing with the lights off.&lt;/p&gt;
&lt;div class="note note-tip note-large"&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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.svg#p-lightbulb-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;&lt;strong&gt;Build the factory somewhere you can work on it — not just somewhere it can run.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A factory scattered across laptops can&amp;rsquo;t be improved as a system. Cloud keeps it in one shape, 24/7, and lets the team iterate together.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="how-to-build-an-ai-native-software-factory"&gt;How to build an AI-native software factory&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s no single blueprint for how to build a software factory, but these seven rules are the software factory model we run at Compostable AI: transform the workflow rather than bolt AI onto it, remove problems instead of engineering around them, pick tools your agents can drive, split work across specialized agents, measure human hours per unit of value, design for convergence over one-shot correctness, and run the factory in the cloud so it operates 24/7.&lt;/p&gt;
&lt;h2 id="closing-thought"&gt;Closing thought&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve shipped more code in the last two years than I did in the fifteen before that. Most of it in languages I couldn&amp;rsquo;t write by hand. And that&amp;rsquo;s after a stretch in leadership where I wrote almost none.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re where I was two years ago: don&amp;rsquo;t ask how AI fits into what you already do. The factory is built one rule at a time, and it&amp;rsquo;s not a template — it&amp;rsquo;s the practice of finding where you&amp;rsquo;re taking advantage of the new economics and where you&amp;rsquo;re not, where your practices still need an update. The leverage is in finding these places and improving them.&lt;/p&gt;
&lt;h2 id="frequently-asked-questions"&gt;Frequently asked questions&lt;/h2&gt;
&lt;p&gt;&lt;details&gt;
&lt;summary&gt;What is a software factory?&lt;/summary&gt;
A software factory runs software delivery as a repeatable industrial process instead of hand-crafting each feature, the way a physical factory turns out products on a line. The idea predates AI. An AI-native software factory is the version where agents do most of the building and the engineers steer.
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;How is a software factory different from traditional software development?&lt;/summary&gt;
Traditional development treats code as a craft, each function written and reviewed by hand. A software factory treats software delivery as an industrial process: agents handle the writing and checking through an automated, converging loop, and the expensive human hours move to defining the work and deciding what to build. The goal is to minimize human hours per unit of value shipped.
&lt;/details&gt;&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;How do you keep AI agents from breaking things across customers?&lt;/summary&gt;
Rather than build a shared multi-tenant sandbox, Compostable AI removes the problem: every client gets two dedicated AWS accounts, one for production and a &amp;ldquo;digital twin&amp;rdquo; staging account. Agents iterate on staging until the work checks out, and only then does it ship to production. Infrastructure-as-code tools like AWS Control Tower and Pulumi make running that account fleet tractable for a small team.
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;What tools does an AI-native software factory need?&lt;/summary&gt;
Tools your agents can actually drive, anything with a solid API and a clean CLI, rather than click-ops around a web UI. Compostable AI expresses infrastructure as type-safe TypeScript with &lt;a href="https://www.pulumi.com/"&gt;Pulumi&lt;/a&gt;, pairs it with &lt;a href="https://www.pulumi.com/product/neo/"&gt;Pulumi Neo&lt;/a&gt; for domain-specific infrastructure skills, and uses &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/esc/"&gt;Pulumi ESC&lt;/a&gt; for configuration. If part of your stack still requires a human to click through a UI, your agents stop there.
&lt;/details&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Watch the &lt;a href="https://www.youtube.com/watch?v=oHNdlWlsR-w"&gt;original Pulumi webinar&lt;/a&gt;. Read the &lt;a href="https://www.pulumi.com/case-studies/compostable-ai/"&gt;Compostable AI case study&lt;/a&gt;, and learn more about &lt;a href="https://compostable.ai/"&gt;Compostable AI&lt;/a&gt; and &lt;a href="https://www.pulumi.com/product/neo/"&gt;Pulumi Neo&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description><author>Ewan Dawson</author><author>Adam Gordon Bell</author><category>ai</category><category>ai-agents</category><category>infrastructure-as-code</category><category>pulumi-neo</category><category>esc</category></item><item><title>Platform Engineering Buffet at SEITENBAU</title><link>https://www.pulumi.com/blog/platform-engineering-buffet/</link><pubDate>Tue, 27 May 2025 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/platform-engineering-buffet/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/platform-engineering-buffet/index.png" /&gt;
&lt;p&gt;&lt;em&gt;This post is based on my video interview with Nico Thomas, a platform engineer at SEITENBAU. In this article, Nico shares how he and his team built a flexible, reusable platform to support 20+ diverse projects using Pulumi. Watch the video below or read on for Nico’s take on building an infrastructure buffet. - Adam Gordon Bell&lt;/em&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/wF9COpHJ9b4?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;Imagine your company isn&amp;rsquo;t Spotify with one product pipeline, but 20 independent government projects running on Ansible-provisioned VMs—some on Kubernetes, some delivered into other customer data centers, some operated by clients themselves. Each team is building its own CI/CD, secrets management, and integrations. You’re left with a million reinvented wheels.&lt;/p&gt;
&lt;p&gt;That was the situation my team and I faced at SEITENBAU, a German software company specializing in custom development for both public and private sectors. Unlike the typical platform engineering stories—where teams build infrastructure for a single product with multiple microservices—we had a fundamentally different challenge: &lt;strong&gt;How do you build a platform that serves dozens of completely independent projects, each with their own technology choices, deployment targets, and operational models?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The answer? Stop trying to force everyone onto the same plate. Instead, build a buffet.&lt;/p&gt;
&lt;h2 id="the-buffet-philosophy"&gt;The Buffet Philosophy&lt;/h2&gt;
&lt;p&gt;&lt;img src="slide-02.png" alt="Buffet visualization with labeled containers showing NPM Artifact Storage, Container Registry, and various infrastructure components"&gt;&lt;/p&gt;
&lt;p&gt;Think of a traditional platform as a set menu—also known as prix fixe—where everyone gets the same courses, prepared the same way, in the same order. But what if your diners include vegans, carnivores, and people with various dietary restrictions? What if some want a quick snack while others need a seven-course meal?&lt;/p&gt;
&lt;p&gt;That was exactly the situation we faced. We had:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Government projects that needed to run on-premises due to data sovereignty requirements.&lt;/li&gt;
&lt;li&gt;Modern cloud-native applications leveraging Kubernetes and GitOps.&lt;/li&gt;
&lt;li&gt;Legacy systems still operating on virtual machines managed with Ansible.&lt;/li&gt;
&lt;li&gt;Projects we ran as managed services and others we simply delivered to customers.&lt;/li&gt;
&lt;li&gt;Teams using various CI/CD tools, artifact repositories, and deployment strategies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A one-size-fits-all platform would have been a disaster. Instead, my team built a platform offering a rich selection of pre-configured, production-ready components. Teams could mix and match according to their specific needs, creating their own unique infrastructure combinations.&lt;/p&gt;
&lt;h2 id="the-challenge-beyond-microservices"&gt;The Challenge: Beyond Microservices&lt;/h2&gt;
&lt;p&gt;Most platform engineering resources assume you&amp;rsquo;re building infrastructure for a single product – Spotify&amp;rsquo;s Backstage, for example. But what happens when you&amp;rsquo;re dealing with over 20 completely independent projects, each with distinct requirements?&lt;/p&gt;
&lt;p&gt;Each project is distinct, featuring its own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Technology stack (Java predominates, but we support various frameworks and languages).&lt;/li&gt;
&lt;li&gt;Infrastructure requirements (cloud, on-premises, or hybrid setups).&lt;/li&gt;
&lt;li&gt;Security and compliance constraints.&lt;/li&gt;
&lt;li&gt;Operational models (managed by us, the client, or jointly).&lt;/li&gt;
&lt;li&gt;Release cadences and deployment strategies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="slide-07.png" alt="Choose your menu"&gt;&lt;/p&gt;
&lt;p&gt;Initially, this diversity seemed chaotic, but we quickly noticed that underneath these variations, common patterns emerged. Every project consistently required source control, CI/CD pipelines, artifact storage, secrets management, and deployment targets. The critical realization was that &lt;strong&gt;we didn&amp;rsquo;t need to standardize what teams chose—we needed to standardize how they connected these building blocks.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s where Pulumi became essential. Its flexibility allowed us to construct an infrastructure buffet that genuinely accommodated the diverse appetites and needs of every team.&lt;/p&gt;
&lt;h2 id="building-the-kitchen-architecture-with-pulumi"&gt;Building the Kitchen: Architecture with Pulumi&lt;/h2&gt;
&lt;p&gt;&lt;img src="slide-09.png" alt="Unified project configuration"&gt;&lt;/p&gt;
&lt;p&gt;Behind every great buffet is a well-organized kitchen. For our infrastructure, that kitchen is powered by Pulumi and Python, orchestrating a complex web of infrastructure services, configurations, and deployments. Using Pulumi together with Python has proven extremely valuable—it makes our day-to-day tasks easier to maintain and understand.&lt;/p&gt;
&lt;h3 id="the-multi-stack-approach"&gt;The Multi-Stack Approach&lt;/h3&gt;
&lt;p&gt;Rather than creating a single monolithic infrastructure program, we adopted a modular approach with multiple Pulumi stacks. This decision was essential for managing complexity and empowering team autonomy.&lt;/p&gt;
&lt;p&gt;&lt;img src="slide-10.png" alt="A look in the kitchen"&gt;&lt;/p&gt;
&lt;p&gt;Our architecture comprises several specialized Pulumi programs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitLab System&lt;/strong&gt;: Manages installations and configurations of GitLab.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Project Configuration&lt;/strong&gt;: Sets up team-specific GitLab groups, CI/CD runners, and agents.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cluster Management&lt;/strong&gt;: Provisions Kubernetes clusters complete with standard add-ons.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VM Infrastructure&lt;/strong&gt;: Manages traditional VM-based deployments utilizing Ansible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform Services&lt;/strong&gt;: Configures shared services such as artifact repositories and identity providers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each program can maintain multiple stacks (dev, staging, prod), allowing us to manage various environments independently while leveraging shared infrastructure code.&lt;/p&gt;
&lt;h2 id="from-ingredients-to-ready-made-dishes-reusable-components"&gt;From Ingredients to Ready-Made Dishes: Reusable Components&lt;/h2&gt;
&lt;p&gt;&lt;img src="slide-06.png" alt="Platform vision showing the complete self-service platform"&gt;&lt;/p&gt;
&lt;p&gt;The real magic happens in our component library. Just as a buffet offers complete dishes rather than raw ingredients, we&amp;rsquo;ve built high-level components that encapsulate complex infrastructure patterns.&lt;/p&gt;
&lt;h3 id="example-the-kubernetes-storage-component"&gt;Example: The Kubernetes Storage Component&lt;/h3&gt;
&lt;p&gt;&lt;img src="slide-15.png" alt="KubeLonghorn component diagram"&gt;&lt;/p&gt;
&lt;p&gt;Consider our Longhorn storage component. Instead of requiring teams to understand Helm charts, storage classes, and snapshot configurations, they simply declare they need persistent storage.&lt;/p&gt;
&lt;p&gt;The component handles all the complexity: installing Longhorn, configuring appropriate replication for test vs. production environments, setting up snapshot classes, and integrating with our internal certificate authority.&lt;/p&gt;
&lt;h2 id="two-years-later-the-buffet-is-open"&gt;Two Years Later: The Buffet is Open&lt;/h2&gt;
&lt;p&gt;After two years of running this platform in production, we&amp;rsquo;ve learned what works and what we&amp;rsquo;d do differently.&lt;/p&gt;
&lt;p&gt;Building a platform for diverse, independent projects requires a different mindset than traditional platform engineering. Instead of forcing standardization, we&amp;rsquo;ve embraced flexibility while maintaining consistency where it matters.&lt;/p&gt;
&lt;p&gt;The buffet approach—powered by Pulumi&amp;rsquo;s flexibility and Python&amp;rsquo;s expressiveness—has allowed us to serve 20+ different projects efficiently while giving teams the autonomy they need. It&amp;rsquo;s not about having one perfect dish; it&amp;rsquo;s about having a kitchen flexible enough to satisfy everyone.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re facing similar challenges with diverse project requirements, consider building a buffet instead of a prix fixe menu. Your teams will thank you for it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Want to build your own infrastructure buffet? &lt;a href="https://www.pulumi.com/docs/get-started/"&gt;Get started with Pulumi&lt;/a&gt; and join &lt;a href="https://slack.pulumi.com/"&gt;our community Slack&lt;/a&gt; to connect with engineers solving similar challenges.&lt;/em&gt;&lt;/p&gt;</description><author>Nico Thomas</author><author>Adam Gordon Bell</author><category>platform-engineering</category><category>kubernetes</category><category>infrastructure-as-code</category></item><item><title>How We Used Pulumi to Safely Migrate Oso's Global Infrastructure</title><link>https://www.pulumi.com/blog/how-we-used-pulumi-to-safely-migrate-osos-global-infrastructure/</link><pubDate>Mon, 05 May 2025 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-we-used-pulumi-to-safely-migrate-osos-global-infrastructure/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-we-used-pulumi-to-safely-migrate-osos-global-infrastructure/index.png" /&gt;
&lt;p&gt;Infrastructure as Code (IaC) tools such as Pulumi can provide enormous amounts of leverage, but they must be used correctly to also provide safety. One of our main jobs as infrastructure engineers is to not break things, so leverage without safety is useless. If something is safe, we can change things easily without even thinking about it. If it isn&amp;rsquo;t, we&amp;rsquo;ll be up until 2 a.m. fixing what we broke.&lt;/p&gt;
&lt;p&gt;At Oso, we recently had to do a large infrastructure migration and learned three key principles that make a huge difference in using Pulumi for safe migrations. First, understand how it&amp;rsquo;s even possible to safely change your infrastructure independently of Pulumi. Second, write reusable and predictable Pulumi code. Finally, refactor your Pulumi code to your new version using zero-diff refactors.&lt;/p&gt;
&lt;h2 id="osoʼs-migration"&gt;Osoʼs Migration&lt;/h2&gt;
&lt;p&gt;&lt;img src="img.png" alt="img.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.osohq.com/"&gt;Oso&lt;/a&gt; provides &lt;a href="https://www.osohq.com/cloud/authorization-service"&gt;Authorization as a Service&lt;/a&gt;, so every application decision must first query Oso. This means that if Oso goes down, our customers will also go down. We can&amp;rsquo;t afford a moment of downtime.&lt;/p&gt;
&lt;p&gt;However, we&amp;rsquo;re also growing quickly, and one of the side effects of growth is that architectural decisions that worked for us in the past quickly reach their expiration date. One example of this is when we hit a scaling limit and needed to quickly migrate from Amazon ECS Fargate to ECS on our own self-managed EC2 instances.&lt;/p&gt;
&lt;p&gt;We can&amp;rsquo;t overstate the Pandora&amp;rsquo;s box of complexity this opened up for us. From the lifecycle of our data to our deployment orchestration, this touched nearly every part of our stack. On top of this, because performance and data locality are critical for us, we are deployed globally in 12 different regions. We need to be extremely careful to do this correctly.&lt;/p&gt;
&lt;h2 id="how-the-migration-actually-worked"&gt;How the Migration Actually Worked&lt;/h2&gt;
&lt;p&gt;&lt;img src="img_1.png" alt="img_1.png"&gt;&lt;/p&gt;
&lt;p&gt;The most important thing to realize when using an infrastructure tool like Pulumi is that ultimately you&amp;rsquo;re dealing with real resources. It doesn&amp;rsquo;t matter how cleverly you refactor your Pulumi code; if something gets deleted that isn&amp;rsquo;t supposed to be deleted, or if at any point a critical component is missing, there will be an outage.&lt;/p&gt;
&lt;p&gt;At Oso, we were already using a Route 53 Traffic Policy to do latency-based per-region routing, so Route 53 was a convenient tool for us in this migration. The plan was to create an entirely new Oso infrastructure and include it in our traffic policy, but send 0% of our traffic to it. Then, we could run tests ourselves directly against the new infrastructure and gradually ramp up traffic until we were fully migrated.&lt;/p&gt;
&lt;p&gt;That is the first big lesson we took away from this. If we didn&amp;rsquo;t have a point in our infrastructure where we could easily reroute customer traffic from one place to the other, this transition would have been impossible without downtime.&lt;/p&gt;
&lt;h2 id="writing-reusable-pulumi-code"&gt;Writing Reusable Pulumi Code&lt;/h2&gt;
&lt;p&gt;&lt;img src="img_2.png" alt="img_2.png"&gt;&lt;/p&gt;
&lt;p&gt;The holy grail of infrastructure development is to achieve what we already have in application development: code reuse. Besides the benefit of reducing the burden of maintaining more code, it also reduces risk because we can concentrate our testing in one place and benefit from using code that has already been proven.&lt;/p&gt;
&lt;p&gt;In Osoʼs case, because our migration involved creating a copy of our entire infrastructure with the new changes, code reuse was vital. Even the smallest reusable component meant fewer new bugs and less time fixing bugs we had already fixed in the old infrastructure.&lt;/p&gt;
&lt;p&gt;In Pulumi, you can use the import and function system of the underlying programming language, so that part of managing libraries is not an issue. The real challenge lies in how to actually write the business logic of libraries in Pulumi because writing Pulumi code is not like writing normal code.&lt;/p&gt;
&lt;p&gt;Take a look at this example of creating an ECS service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&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;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Serice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;api&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This line of code might seem trivial, but a lot is happening here, and it&amp;rsquo;s important to understand Pulumi&amp;rsquo;s programming model on a deeper level to know how to work with it properly.&lt;/p&gt;
&lt;p&gt;The first thing to note is the first argument: &lt;code&gt;api&lt;/code&gt;. This is the &lt;a href="https://www.pulumi.com/docs/iac/concepts/resources/names/"&gt;logical resource name&lt;/a&gt; in Pulumi. The subsequent arguments are more about how to actually create the resource, and we wonʼt talk about that here. This logical resource is how Pulumi identifies this resource in the internal state.&lt;/p&gt;
&lt;p&gt;The internal state is the core of the Pulumi engine. It maps the “logical resource name&amp;quot; we provide to the actual resource that Pulumi either created or plans to create. The code example above, at some point deep inside the Pulumi engine, turns into something that roughly looks 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="p"&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;&amp;#34;aws:ecs:Service&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="nt"&gt;&amp;#34;api&amp;#34;&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 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="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="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the key to understanding why writing Pulumi code is not like writing normal code. There is one &amp;ldquo;state namespace&amp;rdquo; per Pulumi execution, which effectively means that there is one global object that we are adding resources to. So the &lt;code&gt;new aws.ecs.Service&lt;/code&gt; function call wasn&amp;rsquo;t just creating a service object that we can pass around; it was registering the service with the global list of resources we plan to deploy.&lt;/p&gt;
&lt;p&gt;After we understand that calls to the Pulumi library have side effects on this global state, we can start to write better Pulumi libraries. For example, say we have this function call:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;deployWebStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;enabled&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;Do you know which resources this will create? Is it safe to call &lt;code&gt;deployWebStack&lt;/code&gt; again with the same arguments? What would happen if we deleted this line of code? These are all the familiar perils of global state, and something we must proactively contend with to write a good Pulumi library.&lt;/p&gt;
&lt;p&gt;Here are some rules that we recommend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;DO use &lt;a href="https://www.pulumi.com/docs/iac/concepts/resources/components/"&gt;Pulumi Component Resources&lt;/a&gt; in libraries that create resources.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This encourages a more declarative object-based approach to resources, which fits better with Pulumi&amp;rsquo;s model and also causes these objects to show up in the Pulumi output.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do NOT conditionally create resources inside library functions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the function is called, all the resources should be created, and if it is not, the resources should not be created. Conditionals should be at the top level, because that&amp;rsquo;s where you are deciding what is deployed in a particular region or component.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do NOT check the current infrastructure state inside library functions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This should be passed in as arguments; otherwise, the library function will have non-deterministic behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do NOT attempt to implement idempotent resource creation functions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;See the previous rule. The only way to implement a function that creates a resource only if it hasn&amp;rsquo;t been created yet is to check if it has been created, leading to non-determinism.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DO write functional code as inputs to resources.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can still write normal code in some cases, as long as it doesn&amp;rsquo;t call into Pulumi. For example, the code that generates the list of environment variables for an app can be a normal function.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DO have a strategy for resource naming.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Because any function might register new resources, and because you may even have naming conflicts on the cloud provider side, having a coherent strategy for naming resources is helpful. We don&amp;rsquo;t have anything prescriptive here, because it&amp;rsquo;s really a matter of preferences and specific situations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DO favor assigning resources to named objects and variables over getter functions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rather than implementing a function or some kind of &amp;ldquo;getter&amp;rdquo; to refer to another resource, save that resource object in a variable at some scope where it can be referenced by the other resources that need it. This can even be in the global scope to match the reality that all the resources end up in one resource namespace in the end.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;rsquo;s a lot of advice here, but it really all comes down to keeping in mind what Pulumi is actually doing with your code and remembering that calls into the Pulumi library are actually registering resources in the global state.&lt;/p&gt;
&lt;h2 id="infrastructure-snapshot-testing-for-zero-diff-refactors"&gt;Infrastructure Snapshot Testing for Zero Diff Refactors&lt;/h2&gt;
&lt;p&gt;&lt;img src="img_3.png" alt="img_3.png"&gt;&lt;/p&gt;
&lt;p&gt;Now that we know what we want to change in our infrastructure, and we know how to write our Pulumi code in a reusable way, we&amp;rsquo;re faced with a familiar software engineering challenge: How can we safely refactor our existing code to get to where we want to be? To answer this question, we can step back from the Pulumi specifics for a moment and think about refactoring in general.&lt;/p&gt;
&lt;p&gt;One method that is amazing for refactoring code is Snapshot Testing. With Snapshot Testing, you run the legacy code that you want to change, save the output, and create a unit test around that. Then, when you make a code change, you can detect if your code changes result in any unintended changes in behavior.&lt;/p&gt;
&lt;p&gt;We can imitate this with Pulumi by taking advantage of the fact that we can see a &lt;code&gt;preview&lt;/code&gt; of the changes Pulumi will make before we deploy them. While we don&amp;rsquo;t have direct access to Pulumi&amp;rsquo;s internal data model in the code itself to make this &amp;ldquo;snapshot,&amp;rdquo; the &lt;code&gt;preview&lt;/code&gt; can give us this comparison. If we make changes to our infrastructure, as long as &lt;code&gt;pulumi preview&lt;/code&gt; shows no changes, we are 100% sure that the refactor we made is safe and won&amp;rsquo;t cause any issues. Because of this, we at Oso internally call this method a &amp;ldquo;zero diff refactor.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This is a relatively simple concept, but it is necessary to make this migration possible. In our original infrastructure, most of our resources were deployed in one monolithic Pulumi project, and resources were created at the top level rather than in libraries. The 12-region deployment also made running Pulumi on this project very slow, and we wanted a clean separation between the old and new infrastructure. Using snapshot testing to refactor allowed us to build and reuse our in-house infrastructure libraries with 100% confidence and let us focus on the parts that held the real risk.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This migration did not go smoothly. We constantly found problems and unexpected assumptions that our code was making about the underlying infrastructure.&lt;/p&gt;
&lt;p&gt;But no one outside Oso noticed. We had a clear migration plan, factored out common Pulumi code, and used zero-diff refactors. Putting this all together meant we could benefit from the leverage that Pulumi has the potential to provide. This allowed us to make major changes with confidence that they wouldn&amp;rsquo;t break our system.&lt;/p&gt;
&lt;p&gt;In the early days of Oso, people were often surprised by how much we were able to accomplish with such a small core team of engineers. Part of the secret to this is using the tools we have available to gain maximum leverage, but this isn&amp;rsquo;t the entire story. Knowing how to use them safely allows us to actually benefit from that leverage in practice.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in exploring Pulumi further, here are several ways to get involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Discover how Pulumi Crosswalk for AWS simplifies &amp;ldquo;day one&amp;rdquo; tasks in our &lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;AWS guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Register for one of our upcoming &lt;a href="https://www.pulumi.com/resources/#upcoming"&gt;workshops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;But most importantly, &lt;a href="https://app.pulumi.com/signup"&gt;try Pulumi&lt;/a&gt; today!&lt;/li&gt;
&lt;/ul&gt;</description><author>Shaun Verch</author><category>aws</category><category>refactoring</category><category>infrastructure-as-code</category></item><item><title>DevSecOps Game-Changer: Security Automation That Delivers Business Results</title><link>https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/</link><pubDate>Thu, 21 Nov 2024 07:16:30 +0000</pubDate><guid>https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/devsecops-strategy-security-automation-tivity-health/index.png" /&gt;
&lt;p&gt;Organizations are under constant pressure to deliver new products and features faster than ever. But speed alone isn’t enough—businesses must also navigate the complex challenges of ensuring security and managing infrastructure costs effectively.&lt;/p&gt;
&lt;p&gt;Enter DevSecOps - the strategic integration of security practices into the DevOps workflow. By automating security processes, organizations can achieve improved speed, scalability, and business impact, all while ensuring security remains a priority.&lt;/p&gt;
&lt;p&gt;Tivity Health, a leading health and fitness solutions provider, has embraced this DevSecOps approach using Pulumi, a modern infrastructure as code (IaC) platform. During PulumiUP 2024, David Giambruno, Tivity Health&amp;rsquo;s VP of Engineering and DevOps, shared how, by leveraging Pulumi, he led the transformation that continuously drives remarkable results in speed, cost savings, and security.&lt;/p&gt;
&lt;h2 id="on-this-article"&gt;On this article:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/#the-beginning-from-data-center-to-the-cloud"&gt;The Beginning: From Data Center to the Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/#embracing-pulumi-the-power-of-automation-productivity-and-security"&gt;Embracing Pulumi: The Power of Automation, Productivity, and Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/#driving-business-impact-through-security-automation"&gt;Driving Business Impact Through Security Automation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/#fostering-devops-culture-through-cross-functional-collaboration"&gt;Fostering DevOps Culture Through Cross-Functional Collaboration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/#lessons-learned-navigating-the-cultural-shift"&gt;Lessons Learned: Navigating the Cultural Shift&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/devsecops-strategy-security-automation-tivity-health/#the-future-of-devsecops-and-pulumi-at-tivity-health"&gt;The Future of DevSecOps and Pulumi at Tivity Health&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-beginning-from-data-center-to-the-cloud"&gt;The Beginning: From Data Center to the Cloud&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.tivityhealth.com/"&gt;Tivity Health&lt;/a&gt;&amp;rsquo;s journey began with a strategic decision to transition from a traditional data center environment to a cloud-native architecture. Rather than opting for a &amp;ldquo;lift and shift&amp;rdquo; approach, they made the bold choice to go directly to a cloud-native model, embracing the principles of DevSecOps along the way.&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/35vAiKdDux4?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 need to &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/deployments/"&gt;automate infrastructure deployment&lt;/a&gt; and management was fundamental. Giambruno explained, &amp;ldquo;&lt;em&gt;If you can&amp;rsquo;t automate it, we don&amp;rsquo;t need it.&lt;/em&gt;&amp;rdquo; This philosophy drove the team to seek out a solution that would not only streamline their operations but also empower their developers to focus on building innovative products for their customers.&lt;/p&gt;
&lt;h2 id="embracing-pulumi-the-power-of-automation-productivity-and-security"&gt;Embracing Pulumi: The Power of Automation, Productivity, and Security&lt;/h2&gt;
&lt;p&gt;Tivity Health&amp;rsquo;s search for the right tool led them to Pulumi, a unified platform for all the infrastructure needs that allows teams to use general programming languages, such as TypeScript, Python, Java, and Go, to define and manage their cloud infrastructure. Giambruno and his team immediately recognized Pulumi&amp;rsquo;s ability to deliver on their key requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automation&lt;/strong&gt;: &lt;a href="https://www.pulumi.com/docs/iac/"&gt;Pulumi&amp;rsquo;s infrastructure as code (IaC)&lt;/a&gt; approach enabled Tivity Health to automate the deployment and management of its cloud environments, reducing the time and effort required for these tasks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Productivity&lt;/strong&gt;: using Pulumi&amp;rsquo;s general-purpose programming languages allowed developers to define, deploy, and manage infrastructure within their existing tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: Pulumi&amp;rsquo;s security features allowed Tivity Health to integrate &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/crossguard/"&gt;security practices&lt;/a&gt; into its infrastructure deployment processes, reducing the risk of security breaches and ensuring compliance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost Optimization&lt;/strong&gt;: Pulumi&amp;rsquo;s ability to automate the spin-up and teardown of cloud environments on demand has led to significant cost reductions for Tivity Health.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Giambruno describes Pulumi as a &amp;ldquo;three-dimensional&amp;rdquo; tool, offering a versatile set of capabilities that have been instrumental in transforming Tivity Health&amp;rsquo;s operations. &amp;ldquo;&lt;em&gt;The ability to use those dimensions in lots of different ways to do the automation is what really makes a difference to the teams&lt;/em&gt;,&amp;rdquo; he says.&lt;/p&gt;
&lt;h2 id="driving-business-impact-through-security-automation"&gt;Driving Business Impact Through Security Automation&lt;/h2&gt;
&lt;p&gt;By adopting Pulumi and DevSecOps automation, Tivity Health realized significant business benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Speed and Agility&lt;/strong&gt;: By automating its infrastructure deployment and management processes, Tivity Health has dramatically &lt;a href="https://www.pulumi.com/case-studies/unity/"&gt;reduced the time&lt;/a&gt; required to spin up new environments or change existing ones. &amp;ldquo;&lt;em&gt;We run it through automation and boom, it&amp;rsquo;s out, it&amp;rsquo;s done&lt;/em&gt;,&amp;rdquo; Giambruno says. This newfound speed and agility have empowered Tivity Health&amp;rsquo;s developers to focus on building products and features rather than getting bogged down in infrastructure-related tasks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost Optimization&lt;/strong&gt;: Tivity Health&amp;rsquo;s cloud-native approach and Pulumi&amp;rsquo;s automation capabilities have resulted in &lt;a href="https://www.pulumi.com/case-studies/lemonade/"&gt;significant cost savings&lt;/a&gt;. The company estimates that its annual cloud spend has decreased from $9.5 million in its data center days to just $2 million—a staggering 79% reduction. These cost savings have allowed Tivity Health to redirect resources towards more strategic initiatives that drive business growth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved Security and Compliance&lt;/strong&gt;: Tivity Health&amp;rsquo;s DevSecOps strategy, anchored by Pulumi, has strengthened its security posture and &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/crossguard/compliance-ready-policies/"&gt;compliance efforts&lt;/a&gt;. By integrating security directly into its infrastructure workflows, Tivity Health has improved its security posture. Automation ensures that security measures are enforced consistently across their cloud environments, reducing risks and improving compliance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="fostering-devops-culture-through-cross-functional-collaboration"&gt;Fostering DevOps Culture Through Cross-Functional Collaboration&lt;/h2&gt;
&lt;p&gt;Using Pulumi provided more than technical benefits. It also enabled better collaboration between the development, security, and operations teams. By providing a common language and framework for infrastructure management, Pulumi has helped break down silos and align these traditionally disparate groups towards a shared goal of delivering secure, high-quality products faster.&lt;/p&gt;
&lt;h2 id="lessons-learned-navigating-the-cultural-shift"&gt;Lessons Learned: Navigating the Cultural Shift&lt;/h2&gt;
&lt;p&gt;Transitioning to DevSecOps and cloud-native practices required a cultural shift at Tivity Health. Giambrono acknowledges that this cultural shift was not without its challenges. He emphasizes the importance of addressing the human element of change, offering the following advice for organizations embarking on a similar journey:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start with a proof of concepts&lt;/strong&gt;: Giambruno recommends beginning with a small-scale proof of concepts to demonstrate the capabilities and benefits of the new technologies and processes to help alleviate fears and build confidence among team members.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Empower developers and make them feel safe&lt;/strong&gt;: By involving developers in the process and ensuring they feel comfortable with the new tools and workflows, Tivity Health gained user buy-in and overall support for the new DevSecOps approach.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure buy-in from business and financial stakeholders&lt;/strong&gt;: Address the concerns of business and financial stakeholders early on, such as the impact on costs and the ability to deliver tangible results. This is crucial for securing the necessary support and resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Celebrate successes and build momentum&lt;/strong&gt;: Giambruno highlights the importance of celebrating the team&amp;rsquo;s achievements along the way, even when there are bumps on the road, as this helps build enthusiasm and keep the momentum going.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-future-of-devsecops-and-pulumi-at-tivity-health"&gt;The Future of DevSecOps and Pulumi at Tivity Health&lt;/h2&gt;
&lt;p&gt;As Tivity Health continues to refine its DevSecOps strategy, Giambruno is optimistic about the future. He envisions a world where the company&amp;rsquo;s ability to deploy new products and features rapidly will give Tivity Health a significant competitive edge, allowing it to better serve its customers and drive business growth.&lt;/p&gt;
&lt;p&gt;Looking ahead, Giambruno is particularly enthusiastic about the potential of Pulumi&amp;rsquo;s AI-powered capabilities, which he believes will further streamline and optimize the company&amp;rsquo;s infrastructure management processes. &amp;ldquo;&lt;em&gt;I&amp;rsquo;m super looking forward to the tests we&amp;rsquo;re going to do, like when we acquire someone and then taking them in, &amp;lsquo;Borg-ing&amp;rsquo; them into our automation and seeing how much we can take out of their operating cost as fast as possible&lt;/em&gt;,&amp;rdquo; he says.&lt;/p&gt;
&lt;p&gt;Tivity Health&amp;rsquo;s journey with Pulumi is a powerful example of how the right tool, DevSecOps strategy, and automation can drive tangible business results. By focusing on automation, security, and collaboration, organizations can achieve faster, cheaper, and better cloud deployments—putting them on the path to long-term success in an increasingly competitive, cloud-native world.&lt;/p&gt;
&lt;p&gt;To learn more about Pulumi and how it can transform your software development and infrastructure management:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get started with &lt;a href="https://www.pulumi.com/tutorials/"&gt;Pulumi Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Attend an &lt;a href="https://www.pulumi.com/resources/#upcoming"&gt;upcoming workshop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Try out the &lt;a href="https://www.pulumi.com/product/neo/"&gt;Pulumi Neo&lt;/a&gt; code assistant to accelerate your infrastructure as code journey&lt;/li&gt;
&lt;/ul&gt;</description><author>Sara Huddleston</author><category>security</category><category>devsecops</category><category>devops</category><category>infrastructure-as-code</category><category>platform-engineering</category><category>infrastructure-lifecycle-management</category><category>developer-experience</category></item><item><title>Unified and Programmatic Approach to Infrastructure Management at BMW Using Pulumi</title><link>https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/</link><pubDate>Thu, 03 Oct 2024 09:52:10 +0000</pubDate><guid>https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/index.png" /&gt;
&lt;p&gt;In the ever-evolving world of automotive technology, BMW has been at the forefront of innovation, seamlessly integrating software into the heart of their vehicles. As cars become increasingly complex, with a growing emphasis on connectivity, over-the-air upgrades, and brand-specific user experiences, the need for a robust and scalable software development approach has become paramount.&lt;/p&gt;
&lt;p&gt;Enter the BMW Software Factory, a platform that aims to empower the company&amp;rsquo;s developers and provide them with a superior development experience. At the core of this initiative is the adoption of Pulumi, a modern infrastructure as code (IaC) solution that has transformed the way BMW manages its software ecosystem.&lt;/p&gt;
&lt;h2 id="on-this-article"&gt;On this article:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/#the-challenges-of-a-sprawling-software-landscape"&gt;The Challenges of a Sprawling Software Landscape&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/#the-evolution-of-bmws-software-development-toolchain"&gt;The Evolution of BMW&amp;rsquo;s Software Development Toolchain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/#embracing-pulumi-streamlining-infrastructure-management"&gt;Embracing Pulumi: Streamlining Infrastructure Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/#the-benefits-of-pulumi-accelerating-development-and-improving-maintainability"&gt;The Benefits of Pulumi: Accelerating Development and Improving Maintainability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/#the-future-of-bmws-software-factory-embracing-the-cloud"&gt;The Future of BMW&amp;rsquo;s Software Factory: Embracing the Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/unified-programmatic-approach-infrastructure-management-bmw-using-pulumi/#conclusion-unlocking-the-future-of-automotive-software"&gt;Conclusion: Unlocking the Future of Automotive Software&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-challenges-of-a-sprawling-software-landscape"&gt;The Challenges of a Sprawling Software Landscape&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/HIliBBo4c-g?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;&lt;a href="https://www.bmwgroup.com/en/news/general/2023/BMWGroupIT.html"&gt;BMW&amp;rsquo;s software journey&lt;/a&gt; has been a testament to the exponential growth of automotive technology. What started with simple engine controllers has evolved into a complex network of electronic control units (ECUs) scattered throughout the vehicle. As the software footprint continues to expand, BMW recognized the need for a unified and efficient approach to software development and deployment.&lt;/p&gt;
&lt;p&gt;The company&amp;rsquo;s initial efforts involved introducing a platform called &amp;ldquo;Code Craft,&amp;rdquo; which provided a comprehensive stack of services to support the software development lifecycle. This stack included a GitHub Enterprise application for source code management, a Gerrit system for Android-based developments, a continuous integration (CI) pipeline, artifact stores, build caching, and various other tools and services.&lt;/p&gt;
&lt;p&gt;However, as the demand for software-driven features grew, the complexity of managing this sprawling ecosystem became increasingly challenging. BMW found itself grappling with the need to scale its infrastructure, navigate network limitations across multiple data centers, and adapt to the ever-changing landscape of cloud computing.&lt;/p&gt;
&lt;h2 id="the-evolution-of-bmws-software-development-toolchain"&gt;The Evolution of BMW&amp;rsquo;s Software Development Toolchain&lt;/h2&gt;
&lt;p&gt;BMW&amp;rsquo;s journey to streamline its software development process has been gradual and iterative. The company&amp;rsquo;s initial approach involved using &lt;a href="https://www.pulumi.com/docs/iac/concepts/vs/chef-puppet-etc/"&gt;Ansible&lt;/a&gt; for deployment and a custom-built deployment scripting solution for its OpenShift cluster.&lt;/p&gt;
&lt;p&gt;As the complexity of the platform increased, BMW turned to Helm and Kubernetes to manage its containerized services. However, as the company ventured into the public cloud, the limitations of these tools became apparent. The team recognized the need for a more comprehensive and scalable solution to manage their infrastructure as code.&lt;/p&gt;
&lt;p&gt;At this critical juncture, BMW discovered Pulumi. This modern IaC solution offered a unique advantage – the ability to leverage a full-fledged programming language, Python, to define and manage their infrastructure. This shift proved to be a game-changer, allowing BMW to leverage its expertise in Python and benefit from the rich ecosystem of libraries and tools available in the Python community.&lt;/p&gt;
&lt;h2 id="embracing-pulumi-streamlining-infrastructure-management"&gt;Embracing Pulumi: Streamlining Infrastructure Management&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/case-studies/bmw/"&gt;BMW&amp;rsquo;s adoption of Pulumi&lt;/a&gt; was a strategic move that aimed to address the growing complexity of its software ecosystem. By transitioning from a patchwork of tools to a unified IaC solution, the company was able to streamline its infrastructure management and improve developer productivity.&lt;/p&gt;
&lt;h3 id="shared-modules-promoting-reusability-and-best-practices"&gt;Shared Modules: Promoting Reusability and Best Practices&lt;/h3&gt;
&lt;p&gt;One of BMW&amp;rsquo;s key initiatives was developing a shared modules library, which allowed the team to abstract the complexity of various infrastructure components, such as databases, and provide a consistent and user-friendly interface for their developers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By leveraging &lt;a href="https://www.pulumi.com/docs/iac/languages-sdks/python/"&gt;Pulumi&amp;rsquo;s Python&lt;/a&gt; bindings, BMW was able to create reusable modules that encapsulated best practices and sensible defaults, making it easier for developers to provision and manage infrastructure resources.
BMW used Pydantic, a data validation library, to define schema-based configurations for its infrastructure. These were then integrated into its IDEs, providing developers with auto-completion and validation support.&lt;/li&gt;
&lt;li&gt;This approach not only accelerated the development process but also ensured that the infrastructure deployed across the organization adhered to consistent security and compliance standards.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="transformation-features-protecting-legacy-services"&gt;Transformation Features: Protecting Legacy Services&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;As part of their software factory, BMW also faced the challenge of integrating legacy services that did not natively support modern authentication and authorization mechanisms, such as &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/access-management/oidc/provider/"&gt;OpenID Connect (OIDC)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;To address this, BMW leveraged Pulumi&amp;rsquo;s transformation features to seamlessly inject an &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/access-management/oidc/client/#exchanging-oidc-tokens"&gt;OAuth2&lt;/a&gt; proxy into their deployments, providing a secure and consistent way to protect these services without requiring extensive modifications to the underlying applications.&lt;/li&gt;
&lt;li&gt;By encapsulating this functionality within a shared module, BMW was able to apply the OAuth2 proxy to multiple services, ensuring a consistent and secure access control layer across their software ecosystem.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="policy-enforcement-ensuring-compliance-and-security"&gt;Policy Enforcement: Ensuring Compliance and Security&lt;/h3&gt;
&lt;p&gt;One key benefit of &lt;a href="https://www.pulumi.com/product/infrastructure-as-code/"&gt;Pulumi&amp;rsquo;s IaC approach&lt;/a&gt; is the ability to define and enforce policies across the organization, ensuring that infrastructure deployments adhere to security and compliance standards.
BMW has leveraged &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/crossguard/"&gt;Pulumi&amp;rsquo;s policy-as-code&lt;/a&gt; capabilities to implement mandatory checks, such as ensuring that all S3 buckets are encrypted at rest, preventing the deployment of non-compliant resources.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By integrating these policy checks into their deployment workflows, BMW has shifted security and compliance concerns to the left, addressing issues early in the development process and reducing the risk of costly post-deployment &lt;a href="https://www.pulumi.com/blog/remediation-policies/"&gt;remediations&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-benefits-of-pulumi-accelerating-development-and-improving-maintainability"&gt;The Benefits of Pulumi: Accelerating Development and Improving Maintainability&lt;/h2&gt;
&lt;p&gt;BMW&amp;rsquo;s adoption of Pulumi has yielded significant benefits, transforming the way the company approaches software development and infrastructure management.&lt;/p&gt;
&lt;h3 id="accelerated-development-with-shared-modules"&gt;Accelerated Development with Shared Modules&lt;/h3&gt;
&lt;p&gt;The implementation of shared modules has been a game-changer for BMW. It allows developers to leverage pre-built and tested infrastructure components without having to reinvent the wheel. This has resulted in a significant acceleration of the development process, as teams can focus on building their applications rather than grappling with the complexities of infrastructure provisioning.&lt;/p&gt;
&lt;h3 id="improved-maintainability-and-consistency"&gt;Improved Maintainability and Consistency&lt;/h3&gt;
&lt;p&gt;By centralizing infrastructure management within the shared modules, BMW has ensured that best practices and security standards are consistently applied across the organization. This has not only improved the overall maintainability of the software ecosystem but has also reduced the risk of security and compliance violations.&lt;/p&gt;
&lt;h3 id="leveraging-pythons-ecosystem"&gt;Leveraging Python&amp;rsquo;s Ecosystem&lt;/h3&gt;
&lt;p&gt;BMW&amp;rsquo;s decision to leverage Pulumi&amp;rsquo;s Python bindings has been a strategic advantage, as the company was able to tap into the rich ecosystem of Python libraries and tools. This has enabled the team to seamlessly integrate Pulumi with their existing Python-based toolchain, including dependency management, testing frameworks, and code quality tools, further enhancing the development experience.&lt;/p&gt;
&lt;h3 id="streamlined-cloud-migration"&gt;Streamlined Cloud Migration&lt;/h3&gt;
&lt;p&gt;As BMW continues to expand its use of public cloud services, Pulumi has played a crucial role in simplifying the &lt;a href="https://www.pulumi.com/migrate/"&gt;migration process&lt;/a&gt;. By providing a consistent IaC approach across on-premises and cloud environments, Pulumi has enabled BMW to manage its infrastructure in a unified manner, reducing the complexity and overhead associated with &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/deployments/"&gt;multi-cloud deployments&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-future-of-bmws-software-factory-embracing-the-cloud"&gt;The Future of BMW&amp;rsquo;s Software Factory: Embracing the Cloud&lt;/h2&gt;
&lt;p&gt;Looking ahead, BMW&amp;rsquo;s Software Factory is poised to take the next step in its evolution, with plans to transition away from the self-hosted backend and embrace cloud-native services. This strategic move aims to further improve &lt;a href="https://www.pulumi.com/blog/software-developer-experience-devex-devx-devops-culture/"&gt;developer productivity&lt;/a&gt; and reduce the internal effort required to maintain the underlying infrastructure.&lt;/p&gt;
&lt;p&gt;By leveraging the expertise and reliability of cloud service providers, BMW can focus on delivering innovative software features to their customers while the cloud providers handle the operational aspects of running the necessary services.&lt;/p&gt;
&lt;p&gt;As BMW continues to push the boundaries of automotive software, Pulumi&amp;rsquo;s role in its Software Factory will only become more crucial. By providing a scalable and flexible IaC solution, Pulumi empowers BMW&amp;rsquo;s developers to innovate with confidence and be secure in the knowledge that their infrastructure is managed consistently and in alignment with the company&amp;rsquo;s &lt;a href="https://www.pulumi.com/resources/security-automation-faster-cheaper-better/"&gt;security and compliance standards&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="conclusion-unlocking-the-future-of-automotive-software"&gt;Conclusion: Unlocking the Future of Automotive Software&lt;/h2&gt;
&lt;p&gt;BMW&amp;rsquo;s journey with Pulumi in the Software Factory showcases the power of modern IaC solutions in navigating the complexities of the automotive software landscape. By embracing a unified and programmatic approach to infrastructure management, BMW has accelerated development, improved maintainability, and ensured compliance across its sprawling software ecosystem.&lt;/p&gt;
&lt;p&gt;As the automotive industry continues to evolve, with cars becoming increasingly software-driven, the lessons learned by BMW can serve as a blueprint for other organizations looking to streamline their software development and deployment processes. By leveraging the capabilities of Pulumi and other cutting-edge technologies, the future of automotive software is poised to be more efficient, secure, and responsive to the ever-changing needs of both manufacturers and consumers.&lt;/p&gt;
&lt;p&gt;To learn more about Pulumi and how it can transform your software development and infrastructure management:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get started with &lt;a href="https://www.pulumi.com/tutorials/"&gt;Pulumi Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Attend an &lt;a href="https://www.pulumi.com/resources/#upcoming"&gt;upcoming workshop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Try out the &lt;a href="https://www.pulumi.com/product/neo/"&gt;Pulumi Neo&lt;/a&gt; code assistant to accelerate your infrastructure as code journey&lt;/li&gt;
&lt;/ul&gt;</description><author>Sara Huddleston</author><category>infrastructure-as-code</category><category>cloud-management</category><category>infrastructure-lifecycle-management</category><category>platform-engineering</category><category>developer-experience</category><category>ansible</category><category>containers</category><category>pulumi-deployments</category></item><item><title>AWS CDK vs Pulumi: Why SST Chose Pulumi</title><link>https://www.pulumi.com/blog/aws-cdk-vs-pulumi-why-sst-switched/</link><pubDate>Wed, 25 Sep 2024 07:32:40 +0000</pubDate><guid>https://www.pulumi.com/blog/aws-cdk-vs-pulumi-why-sst-switched/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/aws-cdk-vs-pulumi-why-sst-switched/index.png" /&gt;
&lt;p&gt;Cloud computing tools evolve, and so must the frameworks developers rely on. For SST (Serverless Stack), AWS CDK was a great starting point—but it had limitations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CDK tied infrastructure to AWS.&lt;/li&gt;
&lt;li&gt;Debugging was frustrating due to CloudFormation templates.&lt;/li&gt;
&lt;li&gt;Multi-cloud was nearly impossible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The solution? Pulumi. In this post, we’ll explore why SST moved to Pulumi, what challenges they overcame, and what this means for developers building modern cloud applications.&lt;/p&gt;
&lt;p&gt;TL;DR: Pulumi lets SST offer a faster, more flexible, and provider-agnostic infrastructure experience.&lt;/p&gt;
&lt;h2 id="in-this-article"&gt;In This Article:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/from-cdk-pulumi-evolution-of-sst/#the-beginnings-of-sst"&gt;The Beginnings of SST&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/from-cdk-pulumi-evolution-of-sst/#cdk-and-cloudformation-limitations"&gt;CDK and CloudFormation Limitations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/from-cdk-pulumi-evolution-of-sst/#a-provider-agnostic-solution-discovering-pulumi"&gt;A Provider-agnostic Solution: Discovering Pulumi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/from-cdk-pulumi-evolution-of-sst/#transitioning-to-pulumi"&gt;Transitioning to Pulumi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/from-cdk-pulumi-evolution-of-sst/#the-benefits-of-pulumi"&gt;The Benefits of Pulumi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/from-cdk-pulumi-evolution-of-sst/#the-future-of-sst-with-pulumi"&gt;The Future of SST with Pulumi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-beginnings-of-sst"&gt;The Beginnings of SST&lt;/h2&gt;
&lt;p&gt;Application developers have witnessed the rapid evolution of cloud computing and the growing need for developers to have direct access to powerful cloud resources. However, the traditional tools and approaches to Infrastructure as Code (IaC) have often been geared more toward DevOps teams, leaving application developers feeling disconnected from the infrastructure side of their projects.&lt;/p&gt;
&lt;p&gt;This was the driving force behind the creation of &lt;a href="https://sst.dev/"&gt;SST (Serverless Stack)&lt;/a&gt;, a framework that aims to bridge the gap between application developers and infrastructure management. In the early days, SST was built on top of AWS&amp;rsquo;s Cloud Development Kit (CDK), which allowed developers to define their infrastructure using TypeScript or Python. While this was a step in the right direction, they soon realized that the limitations of CDK and the underlying AWS CloudFormation were holding them back from truly empowering application developers.&lt;/p&gt;
&lt;h2 id="cdk-and-cloudformation-limitations"&gt;CDK and CloudFormation Limitations&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/LXxJ9XMXC6o?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;As the SST&amp;rsquo;s team continued to work with CDK and CloudFormation, they encountered several challenges that led them to reevaluate their approach. One key issue was the disconnect between application developers&amp;rsquo; thinking and working and traditional IaC tools&amp;rsquo; operating methods.&lt;/p&gt;
&lt;p&gt;With CDK and &lt;a href="https://www.pulumi.com/docs/iac/concepts/vs/cloud-templates/cloudformation/#what-is-cloudformation"&gt;CloudFormation&lt;/a&gt;, the infrastructure code is essentially a code generator, producing an intermediary format (such as YAML or JSON) that is then executed to deploy the resources. This means that the actual code you write as a developer is not the same as the code running during the deployment process. This can lead to several problems, such as difficulty debugging, lack of visibility into the deployment process, and challenges in extending or customizing the deployment workflow.&lt;/p&gt;
&lt;p&gt;Additionally, as the SST&amp;rsquo;s team expanded its focus beyond the AWS ecosystem and started exploring other cloud providers and even on-premises infrastructure, it found that the &lt;a href="https://www.pulumi.com/docs/iac/concepts/vs/cloud-template-transpilers/aws-cdk/#what-is-aws-cdk"&gt;AWS-centric nature of CDK&lt;/a&gt; and CloudFormation was becoming a limitation. It needed a more flexible and provider-agnostic solution that would allow it to deploy and manage infrastructure across a wide range of platforms.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;AWS CDK&lt;/th&gt;
&lt;th&gt;Pulumi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-Cloud Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ AWS-Only&lt;/td&gt;
&lt;td&gt;✅ AWS, Azure, GCP, On-Prem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Hard due to CloudFormation&lt;/td&gt;
&lt;td&gt;✅ Real-time debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ TypeScript, Python&lt;/td&gt;
&lt;td&gt;✅ Any programming language&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Slower due to CloudFormation&lt;/td&gt;
&lt;td&gt;✅ Faster direct execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Hard to trace errors&lt;/td&gt;
&lt;td&gt;✅ Clear deployment state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Limited to AWS ecosystem&lt;/td&gt;
&lt;td&gt;✅ Custom providers &amp;amp; workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ CloudFormation state file&lt;/td&gt;
&lt;td&gt;✅ Pulumi-managed state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secrets Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ AWS Secrets Manager only&lt;/td&gt;
&lt;td&gt;✅ Cross-cloud secret support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="a-provider-agnostic-solution-discovering-pulumi"&gt;A Provider-agnostic Solution: Discovering Pulumi&lt;/h2&gt;
&lt;p&gt;It was during this time that the SST team discovered Pulumi, a modern IaC platform that takes a fundamentally different approach to infrastructure management. Instead of generating an intermediary format, Pulumi treats the infrastructure code as a first-class program executed directly during deployment.&lt;/p&gt;
&lt;p&gt;This paradigm shift had several important implications for SST and its users:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Visibility and Extensibility&lt;/strong&gt;: With Pulumi, the infrastructure code is the same code that is running during deployment, which means there is much greater visibility into the deployment process and the ability to extend or customize it as needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-Cloud Capabilities&lt;/strong&gt;: Pulumi&amp;rsquo;s provider-agnostic approach allows SST&amp;rsquo;s team to easily work with a wide range of cloud and on-premises platforms, giving their users the flexibility to deploy their infrastructure wherever it makes the most sense for their application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simplified Mental Model&lt;/strong&gt;: For application developers, the Pulumi model of &amp;ldquo;your code is the deployment&amp;rdquo; aligns much more closely with their existing mental models and workflows, making it easier for them to adopt and work with IaC tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="transitioning-to-pulumi"&gt;Transitioning to Pulumi&lt;/h2&gt;
&lt;p&gt;Transitioning SST from CDK to Pulumi was not a trivial undertaking. Still, they knew it was a necessary step to truly fulfill their mission of empowering application developers with powerful infrastructure management capabilities.&lt;/p&gt;
&lt;p&gt;One key challenge they faced was re-implementing the higher-level components and abstractions they had built on top of CDK. These components were designed to simplify the infrastructure management experience for their users, and they wanted to ensure that they could provide a similar level of abstraction and ease of use with Pulumi.&lt;/p&gt;
&lt;p&gt;Additionally, they had to carefully consider how to handle the various edge cases and complex deployment scenarios their users encountered with the CDK-based version of SST. They wanted to ensure that the Pulumi-based version would not only match the functionality of the previous version but also improve upon it and address some of the limitations they had encountered.&lt;/p&gt;
&lt;h2 id="the-benefits-of-pulumi"&gt;The Benefits of Pulumi&lt;/h2&gt;
&lt;p&gt;As they worked through the transition to Pulumi, they realized the significant benefits that Pulumi offered to both the SST team as the framework developers and the users.&lt;/p&gt;
&lt;p&gt;&lt;img src="sst-with-pulumi-infrastructure-as-code-deployments.png" alt="A quote from the Founding Engineer at SST: &amp;ldquo;With Pulumi, the deployment process feels like a natural extension of writing code - it&amp;rsquo;s intuitive and powerful and capable of advanced things that traditional IaC tools can&amp;rsquo;t handle.&amp;rdquo;"&gt;&lt;/p&gt;
&lt;h3 id="improved-visibility-and-debugging"&gt;Improved Visibility and Debugging&lt;/h3&gt;
&lt;p&gt;One of Pulumi&amp;rsquo;s most immediate and tangible benefits was the &lt;a href="https://www.pulumi.com/product/pulumi-insights/"&gt;improved visibility and debugging capabilities&lt;/a&gt; it provided. With the infrastructure code being the same as the deployment code, they could easily trace issues back to the source and understand exactly what was happening during the deployment process.&lt;/p&gt;
&lt;p&gt;This starkly contrasted the CDK/CloudFormation approach, where the intermediary format (CloudFormation templates) often obscured the underlying logic and made it much more difficult to diagnose and resolve problems.&lt;/p&gt;
&lt;h3 id="extensibility-and-customization"&gt;Extensibility and Customization&lt;/h3&gt;
&lt;p&gt;Pulumi&amp;rsquo;s design also allowed the SST team to easily extend and customize the deployment process to meet the specific needs of their users. They could leverage Pulumi&amp;rsquo;s built-in extensibility features, such as custom providers and dynamic components, to integrate with a wide range of cloud and on-premises services and implement complex deployment workflows tailored to our users&amp;rsquo; requirements.&lt;/p&gt;
&lt;p&gt;This level of customization was much more challenging with the CDK/CloudFormation approach, where they often had to resort to hacky workarounds or custom Lambda functions to achieve the desired functionality.&lt;/p&gt;
&lt;h3 id="multi-cloud-capabilities"&gt;Multi-Cloud Capabilities&lt;/h3&gt;
&lt;p&gt;As mentioned earlier, one key driver for their transition to Pulumi was the need to support a wider range of cloud and on-premises platforms. With Pulumi&amp;rsquo;s provider-agnostic approach, the SST team was able to easily add support for new providers, allowing their users to deploy and manage infrastructure across a diverse set of environments.&lt;/p&gt;
&lt;p&gt;This flexibility has been particularly valuable for their users, who may have workloads or requirements that span multiple cloud providers or even on-premises infrastructure. With SST built on Pulumi, they can now manage all of their infrastructure through a single, consistent interface without having to juggle multiple tools or approaches.&lt;/p&gt;
&lt;h3 id="simplified-mental-model"&gt;Simplified Mental Model&lt;/h3&gt;
&lt;p&gt;One of Pulumi&amp;rsquo;s most significant benefits for SST users is the simplified mental model it provides. By treating the infrastructure code as a first-class program, Pulumi aligns much more closely with how application developers think and work.&lt;/p&gt;
&lt;p&gt;Instead of having to navigate the complexities of intermediary formats, deployment pipelines, and the separation between infrastructure code and deployment code, SST users can now focus on writing their infrastructure logic in the same programming languages they use for their application code. This makes it much easier for them to understand, maintain, and extend their infrastructure as their needs evolve.&lt;/p&gt;
&lt;h2 id="the-future-of-sst-with-pulumi"&gt;The Future of SST with Pulumi&lt;/h2&gt;
&lt;p&gt;With Pulumi&amp;rsquo;s foundation in place, they can now focus on further enhancing the developer experience and expanding the capabilities of their framework. Some of the key areas they are exploring include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Deeper Integration with Application Frameworks&lt;/strong&gt;: By leveraging Pulumi&amp;rsquo;s flexibility, they can create even tighter integrations between SST and the application frameworks and libraries that their users rely on, making managing infrastructure seamless alongside their application code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expanded Provider Support&lt;/strong&gt;: the SST team will continue to add support for a wide range of cloud and on-premises providers, ensuring that their users can deploy and manage their infrastructure wherever it makes the most sense for their needs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved Deployment Workflows&lt;/strong&gt;: Building on Pulumi&amp;rsquo;s extensibility, they can create &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/deployments/"&gt;more advanced deployment workflows&lt;/a&gt; that address the specific needs of application developers, such as faster deployment times, better rollback capabilities, and more granular control over the deployment process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Observability and Monitoring&lt;/strong&gt;: By treating the infrastructure code as a first-class program, they can provide their users with &lt;a href="https://www.pulumi.com/product/pulumi-insights/"&gt;better visibility&lt;/a&gt; into the deployment process and more robust monitoring and observability capabilities, helping them to identify and resolve issues quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As the SST team continues to evolve with Pulumi at its core, they aim to deliver an even more powerful and user-friendly infrastructure management experience for developers. This will empower teams to focus on building great applications while effortlessly managing the underlying infrastructure.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re interested in exploring Pulumi further, here are several ways to get involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Discover how Pulumi Crosswalk for AWS simplifies “day one” tasks in our &lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;AWS guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Watch our on-demand workshop &lt;a href="https://www.pulumi.com/resources/getting-started-with-iac-pulumi-aws/"&gt;Getting Stated with Infrastructure as Code on AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Register for one of our upcoming &lt;a href="https://www.pulumi.com/resources/#upcoming"&gt;Platform Engineering or DevOps workshops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;But most importantly, &lt;a href="https://app.pulumi.com/signup"&gt;try Pulumi&lt;/a&gt; today!&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-sst"&gt;What is SST?&lt;/h3&gt;
&lt;p&gt;SST is a framework that makes building modern full-stack applications on your infrastructure easy.&lt;/p&gt;
&lt;h3 id="what-is-ssts-ion"&gt;What is SST&amp;rsquo;s Ion?&lt;/h3&gt;
&lt;p&gt;Ion is the code name for a new engine for deploying SST applications. The constructs (or components) are deployed using Pulumi instead of CDK and CloudFormation (CFN). Once Ion is stable, it will be released as SST v3.&lt;/p&gt;
&lt;h3 id="does-sst-use-mostly-terraform-or-pulumi"&gt;Does SST use mostly Terraform or Pulumi?&lt;/h3&gt;
&lt;p&gt;SST leverages Pulumi behind the scenes for its providers and deployment engine while also bridging Terraform providers through Pulumi.&lt;/p&gt;
&lt;h3 id="how-does-sst-make-money"&gt;How does SST make money?&lt;/h3&gt;
&lt;p&gt;SST (Serverless Stack) makes money primarily through its managed service, called SST Console. The Console is optional and includes a free tier. In short, SST primarily relies on SaaS (Software as a Service) revenue through its managed platform and potentially supplemental income streams like enterprise services and consulting.&lt;/p&gt;</description><author>Sara Huddleston</author><category>cloudformation</category><category>aws-cdk</category><category>case-studies</category><category>developer-experience</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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.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/concepts/vs/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>How a Bank Modernized Its Software Engineering With Infrastructure as Code Automation</title><link>https://www.pulumi.com/blog/how-a-bank-modernized-its-software-engineering-with-infrastructure-as-code-automation/</link><pubDate>Thu, 03 Aug 2023 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-a-bank-modernized-its-software-engineering-with-infrastructure-as-code-automation/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-a-bank-modernized-its-software-engineering-with-infrastructure-as-code-automation/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.74fadd1b94bae866bccf29a780f184a71c5cfc34c8677be70da8fe2ab0309b9e.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 Dennis Sauvé 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://www.watrust.com"&gt;Washington Trust Bank&lt;/a&gt;, the largest independently-owned full-service commercial bank in the Northwest, has served personal, private, commercial and wealth management clients throughout the region since 1902. It has assets exceeding $11 billion and currently has 42 branches and offices in Idaho, Oregon, and Washington.&lt;/p&gt;
&lt;p&gt;As an FDIC-governed financial institution, it is imperative for the bank to maintain secure, reliable, and compliant cloud resources to protect clients’ personal data. On the other hand, it also aimed to create more agile development teams as it modernized its software development and infrastructure. &lt;a href="https://github.com/dengsauve"&gt;Dennis Sauvé&lt;/a&gt;, the bank&amp;rsquo;s first DevOps Engineer, recognized &lt;a href="https://www.pulumi.com/what-is/what-is-infrastructure-as-code/"&gt;Infrastructure as Code (IaC)&lt;/a&gt; as the solution to these challenges.&lt;/p&gt;
&lt;p&gt;Embracing an Infrastructure as Code approach would allow them to automate building and deploying their cloud infrastructure, eliminate infrastructure provisioning as a bottleneck, and empower developers to self-service infrastructure and increase productivity.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/Q63ZaX340M4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen&gt;&lt;/iframe&gt;
&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;The main challenge was managing Azure infrastructure in an efficient and secure way that kept pace with the bank&amp;rsquo;s rapid growth. Previously, they ran on-premise infrastructure and deployed with BAT and Powershell scripts. Now, they had migrated to Azure and wanted to increase development speed through CI/CD for cloud infrastructure.&lt;/p&gt;
&lt;p&gt;In addition, cloud resources&amp;rsquo; security and reliability were paramount. They needed a way for the infrastructure and information security teams to work together to implement security best practices for infrastructure, including how to prevent insecure resources from being provisioned.&lt;/p&gt;
&lt;p&gt;The bank decided to adopt IaC to create reliable and rapid deployments while ensuring that its cloud infrastructure stayed compliant and secure.&lt;/p&gt;
&lt;h2 id="why-washington-trust-bank-chose-pulumi"&gt;Why Washington Trust Bank Chose Pulumi&lt;/h2&gt;
&lt;p&gt;The bank evaluated several options, including &lt;a href="https://www.pulumi.com/docs/concepts/vs/terraform/"&gt;Terraform&lt;/a&gt; and Azure Bicep. However, Pulumi stood out due to its versatility, support for multiple clouds and &lt;a href="https://www.pulumi.com/blog/introducing-azure-native-v2/"&gt;native Azure support&lt;/a&gt;, and the ability to write IaC in the bank&amp;rsquo;s preferred language, TypeScript. The ease of using Pulumi with GitHub, its ability to be used in conjunction with Azure CLI, and its comprehensive &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/"&gt;support for CI/CD&lt;/a&gt; through GitHub Actions were also compelling factors.&lt;/p&gt;
&lt;p&gt;Dennis praised Pulumi, saying, &amp;ldquo;&lt;em&gt;Pulumi was exactly the Swiss Army Knife versatility we were looking for. We&amp;rsquo;ve only uncovered more of what we love about Pulumi as our relationship has evolved.&lt;/em&gt;&amp;rdquo;&lt;/p&gt;
&lt;h2 id="benefits-of-using-pulumi"&gt;Benefits of Using Pulumi&lt;/h2&gt;
&lt;p&gt;Pulumi helped Washington Trust Bank improve efficiency by maintaining its infrastructure in code, promoting the security of its infrastructure, and accelerating development speeds. It automated deployment of web services for its development teams, data management tools for its business insights team, and orchestrated its entire hub-and-spoke cloud architecture.&lt;/p&gt;
&lt;h3 id="improved-development-speed-with-reusable-infrastructure-packages-and-cicd"&gt;Improved Development Speed with Reusable Infrastructure Packages and CI/CD&lt;/h3&gt;
&lt;p&gt;Pulumi&amp;rsquo;s automation capabilities played a transformational role. By freeing the infrastructure team from handling manual deployments, it could focus on improving developer productivity. The team created reusable and repeatable &lt;a href="https://www.pulumi.com/docs/concepts/resources/components/"&gt;infrastructure components&lt;/a&gt; built around strict IT security standards in the form of Frazure, an internal NPM package.&lt;/p&gt;
&lt;p&gt;Frazure allows the bank’s developers to easily provision out-of-the-box resources for cloud services and covers the necessary Azure resource groups, vnets, and other resources. The package encapsulates best practices, such as how resource groups and their virtual networks can be created, including subnet IP spaces, peerings to and from the hub virtual network, user-defined routes and more.&lt;/p&gt;
&lt;p&gt;To deploy, developers open a pull request which kicks off a &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/deployments/ci-cd-integration-assistant/"&gt;CI/CD pipeline in GitHub Actions&lt;/a&gt; and provisions testing environments and tests. Once the PR is reviewed and approved, the changes are automatically deployed to staging and live environments. This automated process enabled the bank to accelerate the building, testing, and deployment of new applications, translating into rapid value delivery to their customers.&lt;/p&gt;
&lt;h3 id="safeguards-for-infrastructure"&gt;Safeguards for Infrastructure&lt;/h3&gt;
&lt;p&gt;Pulumi also gave the bank total confidence in being able to rapidly recover its cloud infrastructure in the case of a failure in Azure. The integration of Pulumi with GitHub Actions provided the team with a precise delta of changes to be made to the environment. Coupled with mandatory reviews from InfoSec and infrastructure, this significantly reduced the likelihood of deploying unapproved changes, thereby enhancing the reliability of their deployment processes.&lt;/p&gt;
&lt;h3 id="policy-as-code-guardrails-with-pulumi-crossguard"&gt;Policy as Code Guardrails with Pulumi CrossGuard&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/docs/using-pulumi/crossguard/"&gt;Pulumi CrossGuard&lt;/a&gt; adds an extra layer of security and control and is used in conjunction with Azure Policies, which are used for auditing purposes. CrossGuard prevents the deployment of undesired, insecure, or expensive resources during the preview and deployment stage, thus preventing developers from even reaching Azure to provision resources. Custom error messages give developers context on why their deployment was not allowed.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/docs/using-pulumi/crossguard/configuration/"&gt;Pulumi Cloud Policy Packs&lt;/a&gt; allow them to group and deploy many policies simultaneously. The Policy Packs prevent specified resources from being deployed into staging and live environments. For example, one policy requires all SQL databases to use TLS 1.2 by default and another ensures all storage buckets have public access disabled by default. These capabilities helped bolster the security of the bank&amp;rsquo;s cloud infrastructure.&lt;/p&gt;
&lt;h2 id="financial-services-cloud-modernization-with-pulumi"&gt;Financial services cloud modernization with Pulumi&lt;/h2&gt;
&lt;p&gt;The DevOps and development teams at Washington Trust Bank rely on Pulumi as a key tool in their cloud modernization, while maintaining high security and quality standards. Pulumi played a vital role in streamlining their cloud infrastructure management, enabling their developers to be more productive, and improving the security and reliability of its cloud infrastructure.&lt;/p&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, 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>azure</category><category>fintech</category><category>case-studies</category><category>enterprise</category><category>crossguard</category><category>policy-as-code</category></item><item><title>How Skai Migrated to Amazon Keyspaces with Pulumi</title><link>https://www.pulumi.com/blog/how-skai-migrated-to-aws-keyspaces-with-pulumi/</link><pubDate>Tue, 16 May 2023 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-skai-migrated-to-aws-keyspaces-with-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-skai-migrated-to-aws-keyspaces-with-pulumi/index.png" /&gt;
&lt;blockquote&gt;
&lt;p&gt;Danny Zalkind is the Senior Director of Infrastructure Engineering for Skai, an award-winning intelligent marketing platform. He brings his 15 years of experience of managing tech teams to his current role where he&amp;rsquo;s dedicated to allow Skai R&amp;amp;D to efficiently produce and serve software. You can find him on &lt;a href="https://www.linkedin.com/in/danny-zalkind-01602b56/"&gt;Linkedin&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As Skai continues its journey towards &lt;a href="https://www.pulumi.com/blog/kenshoo-migrates-to-aws-with-pulumi/"&gt;fully migrating to the cloud using Pulumi&lt;/a&gt;, we&amp;rsquo;ve taken another large bite out of the migration pie, moving our most critical data to AWS on top of &lt;a href="https://aws.amazon.com/keyspaces/"&gt;Amazon Keyspaces&lt;/a&gt;, an Apache Cassandra–compatible database service.&lt;/p&gt;
&lt;p&gt;This blog post will dive into how we used Pulumi and AWS Lambda to perform the migration.&lt;/p&gt;
&lt;h2 id="offloading-a-piece-of-the-pie-to-aws"&gt;Offloading a piece of the pie to AWS&lt;/h2&gt;
&lt;p&gt;First, some background on what we do. Skai helps marketers work smarter and faster with a unified platform to reach and convert shoppers. An important service we provide to our clients is optimization of their digital marketing campaign efficiency. We store billions of events per day to enable clients to continuously improve the value they get from their ad spend.&lt;/p&gt;
&lt;p&gt;Our on-premises setup included a 100-node Apache Cassandra cluster to be able to withstand the scale of billions of events, some stored for prolonged periods of time. Maintaining and scaling this setup was a high effort from our Data Infra teams. It was clear to us it was time to offload that piece of the pie to our cloud provider.&lt;/p&gt;
&lt;h2 id="why-amazon-keyspaces"&gt;Why Amazon Keyspaces&lt;/h2&gt;
&lt;p&gt;When looking for a solution, we were looking for a service that would mark the following checkboxes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fully serverless to allow us focus on our business instead of server maintenance.&lt;/li&gt;
&lt;li&gt;Scalability should be automatic and easy to perform.&lt;/li&gt;
&lt;li&gt;High availability and multi-region built-in.&lt;/li&gt;
&lt;li&gt;Cost effective pay-as-you-go and commitment-based model.&lt;/li&gt;
&lt;li&gt;Extensive support and rich documentation.&lt;/li&gt;
&lt;li&gt;Fully automatable using Pulumi, our main Infrastructure as Code framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="migration-method"&gt;Migration method&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at how we set up the migration. We decided to migrate the data using a dual-write method, writing new data to both our on-prem Cassandra cluster and Keyspaces. Any historical data needed could be fetched from Snowflake, our data lake solution, using our Apache Spark pipeline.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-skai-migrated-to-aws-keyspaces-with-pulumi/skai-migration-diagram.png" alt="Skai data-migration diagram"&gt;&lt;/p&gt;
&lt;p&gt;Finally, to make sure that the data is valid and we have no data discrepancies on the Keyspaces side, we added a Lambda function to periodically sample the data and compare it to the data in our on-prem cluster.&lt;/p&gt;
&lt;p&gt;We made a conscious decision to decouple the application code from the provisioning of the infrastructure, letting Pulumi handle all resource creation and configuration, while the application team used our standard CI/CD pipeline to deliver their code to the service owning the Keyspaces data.&lt;/p&gt;
&lt;h2 id="lets-start-provisioning"&gt;Let&amp;rsquo;s start provisioning&lt;/h2&gt;
&lt;p&gt;Keyspaces requires creating a logical entity called a keyspace, which groups related tables relevant for one or more applications. After that, we iterate over our config and create each table with the relevant columns and the preferred provisioning mode &amp;mdash; either &amp;ldquo;on-demand&amp;rdquo; or &amp;ldquo;provisioned,&amp;rdquo; depending on our workload pattern. A provisioned workload in pre-allocated and thus significantly (x7) lower in hourly price, suitable mainly for use cases where the workload is quite predictable with no spikes.&lt;/p&gt;
&lt;p&gt;Finally we populate AWS tags in order to be able track costs and performance metrics.&lt;/p&gt;
&lt;p&gt;The following Python code illustrates the process:&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="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_aws&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;aws&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pulumi_komponents.account_provider.account_provider&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_account_provider&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;# Define resource tagging&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="nf"&gt;__populate_tags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aws_config&lt;/span&gt;&lt;span class="p"&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 class="n"&gt;component_type&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;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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="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;Project&amp;#34;&lt;/span&gt;&lt;span class="p"&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;get_project&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;Environment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws_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;account&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;Type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;component_type&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;tags&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;def&lt;/span&gt; &lt;span class="nf"&gt;pulumi_program&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_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 class="s2"&gt;&amp;#34;aws&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;keyspaces_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 class="s2"&gt;&amp;#34;keyspaces&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;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keyspaces_config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;require_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tags&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;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_account_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aws_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="c1"&gt;# Define keyspace&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;keyspace&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keyspace&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_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;keyspaces-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;keyspaces_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;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&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;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;keyspaces_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;name&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;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;__populate_tags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aws_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;keyspace&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&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;pulumi&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;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;# Define and iterate over keyspace tables&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keyspaces_config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;require_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tables&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_ddl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&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;columns&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;partition_keys&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;clustering_keys&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;static_columns&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;table_ddl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;columns&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;popitem&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;columns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableSchemaDefinitionColumnArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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 class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&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;for&lt;/span&gt; &lt;span class="n"&gt;par&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;table_ddl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;partition_keys&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;partition_keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableSchemaDefinitionPartitionKeyArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;par&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;for&lt;/span&gt; &lt;span class="n"&gt;cl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;table_ddl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;clustering_keys&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_by&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;popitem&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;clustering_keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableSchemaDefinitionClusteringKeyArgs&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_by&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s_col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;table_ddl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;static_columns&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&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;static_columns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableSchemaDefinitionStaticColumnArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s_col&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="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table_ddl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;throughput&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mode&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="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&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_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;keyspaces-table-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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="n"&gt;keyspace_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;keyspace&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;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;table_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;schema_definition&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableSchemaDefinitionArgs&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;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;columns&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;partition_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;partition_keys&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;clustering_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;clustering_keys&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;static_columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;static_columns&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;# Set capacity mode and units for read and write per Table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;capacity_specification&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableCapacitySpecificationArgs&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;throughput_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&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;read_capacity_units&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;table_ddl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;throughput&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rcu&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PROVISIONED&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;else&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="n"&gt;write_capacity_units&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;table_ddl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;throughput&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;wcu&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PROVISIONED&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;else&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="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;# Set general table properties such as TTL and recovery method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;default_time_to_live&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="n"&gt;ttl&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableTtlArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ENABLED&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;point_in_time_recovery&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;keyspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TablePointInTimeRecoveryArgs&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;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ENABLED&amp;#34;&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;# Populate AWS resource tagging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;__populate_tags&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_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="s2"&gt;&amp;#34;table&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="s2"&gt;&amp;#34;Table-type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;unarchived&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;if&lt;/span&gt; &lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;_unarchived&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="k"&gt;else&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;standard&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;tags&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;pulumi&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;keyspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;h2 id="comparison-lambda"&gt;Comparison Lambda&lt;/h2&gt;
&lt;p&gt;Next we define and create a Lambda function that samples and compares data between the data newly ingested into Keyspaces, our on-premise Cassandra cluster, and our data lake Snowflake.&lt;/p&gt;
&lt;p&gt;We created an abstract class implementation of AWS Lambda to standardize our various Lambda implementations and keep the code readable. This class returns all needed arguments, configuration, layers, etc. Here is some Python code that shows our abstraction, named &lt;code&gt;AwsFunction&lt;/code&gt;:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AwsFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractAwsFunction&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 class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AwsFunctionArgs&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="n"&gt;resource_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AwsLambda:Function&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&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;# Set Lambda zip artificate path&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="nf"&gt;_create_function&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="o"&gt;-&amp;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;lambda_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&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;code_archive_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/tmp/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.zip&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;with&lt;/span&gt; &lt;span class="n"&gt;zipfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZipFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_archive_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;w&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;zip_obj&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;zip_sub_folders&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;root_source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functions_absolute_path&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;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;code_archive_path&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;selected_sub_folders&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ZIP_COMMON_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code_directory_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;zip_obj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;zip_obj&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;# Get iam role, kms keys and layers&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_function_role&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kms_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_create_kms&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;layers_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__build_layers&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;# Return function outputs&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="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&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_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;generate_resource_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&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 class="s2"&gt;&amp;#34;function&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;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_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;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&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;package_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;package_type&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;architectures&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;architecture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_value&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;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handler&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;memory_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory_size&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;reserved_concurrent_executions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concurrent_executions&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;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runtime&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;code&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;FileArchive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_archive_path&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;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&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;layers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;layers_arn&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;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_populate_tags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;lambda&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;function&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;kms_key_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kms_key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&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;environment&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;lambda_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FunctionEnvironmentArgs&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;variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environment_variables&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_dict&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;vpc_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vpc_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="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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kms_key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;layers&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;Now we instantiate the abstract class as part of our &amp;ldquo;building block&amp;rdquo; component class, create the function, triggers and invocation:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AwsLambda&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 class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AwsLambdaArgs&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="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AwsLambda&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Define the function nased on the abstract class&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_function&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;AwsFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&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="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;package_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Zip&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;else&lt;/span&gt; &lt;span class="n"&gt;AwsFunctionContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&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;# Add triggers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;triggers&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_triggers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&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;triggers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LAMBDA_TYPES_TO_DEF&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;trigger&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;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&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;# Add async invocation config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;FunctionEventInvokeConfig&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;generate_resource_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;event-invoke-config&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;function_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&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;destination_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FunctionEventInvokeConfigDestinationConfigArgs&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;on_failure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FunctionEventInvokeConfigDestinationConfigOnFailureArgs&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;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_failure_arn&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;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_failure_arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&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="n"&gt;on_success&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FunctionEventInvokeConfigDestinationConfigOnSuccessArgs&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;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_success_arn&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;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_success_arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&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="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;qualifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qualifier&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;maximum_event_age_in_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum_event_age_in_seconds&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;maximum_retry_attempts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_function_args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum_retry_attempts&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;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&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;Now we&amp;rsquo;re ready to call our class to create our comparison Lambda:&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="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pulumi_komponents.awslambda&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AwsLambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AwsLambdaArgs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pulumi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Config&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;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="n"&gt;AwsLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AwsLambdaArgs&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note: This only a partial code example, meant to demonstrate the way we use object-oriented paradigms to create reusable building-blocks of code to be consumed by various platform and R&amp;amp;D teams to provision AWS resources.&lt;/p&gt;
&lt;h2 id="putting-it-all-together"&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;Once we have our Keyspaces setup in place and the confidence that our dual-write process is writing the data correctly, we are now able to wait enough time to accumulate the data on the AWS side, using a feature toggle in our application to switch our backend systems to work with AWS Keyspaces.&lt;/p&gt;
&lt;h2 id="outcome"&gt;Outcome&lt;/h2&gt;
&lt;p&gt;Following the successful migration, we have gained 50% reduction in backend process runtime, increased event processing throughput by 400% and freed up 30% of our team&amp;rsquo;s time to do other things, instead of maintaining cluster infrastructure.&lt;/p&gt;
&lt;p&gt;I hope this post provides readers with useful techniques and ideas for migrating large-scale databases to the public cloud.&lt;/p&gt;</description><author>Danny Zalkind</author><category>aws</category><category>cassandra</category><category>data-warehouse</category><category>migration</category><category>guest-post</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>How Elkjøp Nordic enables self-service infrastructure for developers</title><link>https://www.pulumi.com/blog/how-elkjop-nordic-enables-developers-to-self-serve-infrastructure/</link><pubDate>Fri, 01 Jul 2022 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-elkjop-nordic-enables-developers-to-self-serve-infrastructure/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-elkjop-nordic-enables-developers-to-self-serve-infrastructure/index.png" /&gt;
&lt;p&gt;At PulumiUP 2022, Tomas Jansson, software architect at Elkjøp Nordic, gave a presentation on how to enable developers to self-service infrastructure by using Pulumi’s &lt;a href="https://www.pulumi.com/automation/"&gt;Automation API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Elkjøp Nordic is the leading consumer electronics retailer in the Nordics. The company sells consumer electronics, mobile phones, computers, white goods, domestic appliances, and services linked to these products both directly to consumers and to businesses. It is an omnichannel retailer and serves customers both online and through more than 400 stores. Elkjøp has retail outlets in Norway, Sweden, Finland and Denmark, and franchise operations in Greenland and Faroe Islands.&lt;/p&gt;
&lt;p&gt;To support the company’s modernization strategy, Tomas developed a self-service infrastructure platform that would enable any developer to easily provision cloud infrastructure that uses approved resources and security practices. The goal was to empower teams at Elkjøp to increase their development velocity by using the cloud, while maintaining guardrails that uphold security and compliance.&lt;/p&gt;
&lt;p&gt;Watch the full presentation below or read on to learn the highlights of his presentation:&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/aoa_O-rh5KE?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="the-problem-balancing-agility-and-governance"&gt;The Problem: Balancing Agility and Governance&lt;/h2&gt;
&lt;p&gt;To achieve his goals, Tomas knew he had to solve the problem of balancing freedom with control. On the one hand, he wanted to foster the creativity of his developers and encourage innovation. He wanted his team to feel they were trusted and had the room to create the best solutions they could. On the other hand, he knew he had to maintain some control. Elkjøp needed to keep its applications secure. It also needed good governance so that teams can keep track of who is making changes. Tomas also wanted to limit the impact of deploying untested code. Code that was still being developed needed to be confined to the developer’s own environment. Finding the right balance between independence and control was paramount. Tomas believed this would result in great software and in developers who would enjoy their work.&lt;/p&gt;
&lt;h2 id="the-solution-a-self-service-infrastructure-platform"&gt;The Solution: A Self-Service Infrastructure Platform&lt;/h2&gt;
&lt;p&gt;Tomas’ solution was to build a self-service infrastructure platform. Self-service infrastructure makes it possible for developers to easily access the infrastructure they need while limiting access to resources they don’t need. In effect, self-service infrastructure is a walled garden. Within the garden, developers have all the freedom they need. However, nothing they do affects what’s outside the wall. Code that causes unintended changes only affects them and no one else.&lt;/p&gt;
&lt;p&gt;Tomas built an application that provides a “walled garden” of self-service infrastructure. Developers can create the environments they need, including an Azure resource group and a Kubernetes namespace, all through a graphical user interface. Developers can define whether the environment is for development, test or production. They can also assign values, such as which team members should have admin privileges to the associated GitHub repositories or who can contribute. From there, the platform handles provisioning and configuring all of these resources.&lt;/p&gt;
&lt;p&gt;Here is an example that shows how a developer can create a repository simply by selecting a few parameters from a drop-down list.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-elkjop-nordic-enables-developers-to-self-serve-infrastructure/create-repo.png" alt="Creating a new repository"&gt;&lt;/p&gt;
&lt;p&gt;Tomas determined that the best way to implement self-service infrastructure was with Pulumi and its &lt;a href="https://www.pulumi.com/automation/"&gt;Automation API&lt;/a&gt;. By using Pulumi, he could use standard programming languages to define infrastructure while using familiar software tools like IDEs and test frameworks. Pulumi also natively supports Azure and Kubernetes with 100% coverage of each provider’s APIs and same-day access to new features and updates. Finally, the Pulumi Automation API would enable him to program infrastructure automation directly into his application.&lt;/p&gt;
&lt;p&gt;The Pulumi Automation API is a programmatic interface for running Pulumi programs without the Pulumi CLI. Automation API encapsulates the functionality of the CLI (&lt;code&gt;pulumi up&lt;/code&gt;, &lt;code&gt;pulumi preview&lt;/code&gt;, &lt;code&gt;pulumi destroy&lt;/code&gt;, &lt;code&gt;pulumi stack init&lt;/code&gt;, etc.) but with more flexibility since infrastructure operations can be controlled programmatically.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-elkjop-nordic-enables-developers-to-self-serve-infrastructure/automation-api-diagram.png" alt="Deploying with the Pulumi CLI and Automation API"&gt;&lt;/p&gt;
&lt;h2 id="how-the-self-service-infrastructure-platform-was-built"&gt;How the self-service infrastructure platform was built&lt;/h2&gt;
&lt;p&gt;Here is the workflow for the Elkjøp self-service infrastructure platform.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-elkjop-nordic-enables-developers-to-self-serve-infrastructure/elkjop-platform-diagram.png" alt="The Elkjøp self-service infrastructure platform"&gt;&lt;/p&gt;
&lt;p&gt;The self-service platform is a web application that runs the Pulumi Automation API on the server side. The application uses Pulumi to provision and configure every environment resource such as Azure resource groups, Kubernetes namespaces, service principals, and Kubernetes service accounts. Each environment comes with a Git repository that&amp;rsquo;s used by developers to manage their deployments. The Git repositories are the locus of each environment because they store everything needed to deploy changes such as infrastructure code and environment credentials.&lt;/p&gt;
&lt;p&gt;Using the Pulumi Automation API, the application creates an Azure resource group, a team in both Azure AD and GitHub, and a new GitHub repository. The application then grants administrative access to the GitHub repo to members of the AD group. In addition to creating a Git repository for each environment, the application uses stack references for each Azure environment to inject the associated credentials into the GitHub Secret Store. These credentials are scoped to each GitHub environment so that developers can only work in their own environment. If a team has multiple environments, each environment has its own credentials so developers can’t, for example, deploy from a test environment to a production environment. Secrets are managed through the &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/"&gt;Pulumi Service&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Every new repository comes with Pulumi programs that developers can use to deploy their application. GitHub Actions initiate the deployment. When an action triggers, Pulumi reads the credentials that were generated by the self-service application, allowing Pulumi to deploy new Kubernetes or Azure resources to the environment.&lt;/p&gt;
&lt;h2 id="watch-the-demo"&gt;Watch the demo&lt;/h2&gt;
&lt;p&gt;To illustrate how Elkjøp’s self-service platform works, Tomas presented a simplified demo application that’s based on Elkjøp’s production platform. The code for the application is available at &lt;a href="https://github.com/mastoj/pulumi-automation-demo"&gt;https://github.com/mastoj/pulumi-automation-demo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The demo is a TypeScript application that includes an API that provisions Azure and GitHub resources. Pulumi Automation API code is used to deploy new stacks by calling handler code, which is a Pulumi program that defines resources and outputs. The handler code can create Azure resource groups or GitHub repositories, and stack references are used so that outputs generated by the Azure resources can be stored in GitHub (e.g., resource group credentials).&lt;/p&gt;
&lt;p&gt;Ready to dive into how this works in detail? Watch the demo in the video below starting around 10:20.&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/aoa_O-rh5KE?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="next-steps"&gt;Next steps&lt;/h2&gt;
&lt;p&gt;After watching the &lt;a href="https://youtu.be/aoa_O-rh5KE?list=PLyy8Vx2ZoWlpcvhSsUXdT5CXjRwAaM_My&amp;amp;t=620"&gt;demo&lt;/a&gt; of a self-service infrastructure platform, you can try it out for yourself by checking out the &lt;a href="https://github.com/mastoj/pulumi-automation-demo"&gt;demo code&lt;/a&gt;. You can also learn more about &lt;a href="https://www.pulumi.com/automation/"&gt;Pulumi Automation API&lt;/a&gt; in our &lt;a href="https://www.pulumi.com/docs/using-pulumi/automation-api/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’re new to Pulumi, then follow our &lt;a href="https://www.pulumi.com/docs/get-started/"&gt;getting started guide&lt;/a&gt;.&lt;/p&gt;</description><author>George Huang</author><category>automation-api</category><category>aks</category><category>azure</category><category>infrastructure-as-code</category><category>kubernetes</category><category>typescript</category><category>pulumi-enterprise</category><category>cloud-engineering</category><category>github-actions</category></item><item><title>Cloud engineering fuels the next chapter of startup innovation</title><link>https://www.pulumi.com/blog/cloud-engineering-fuels-startup-innovation/</link><pubDate>Thu, 07 Oct 2021 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/cloud-engineering-fuels-startup-innovation/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/cloud-engineering-fuels-startup-innovation/index.png" /&gt;
&lt;p&gt;The story of how the cloud fuels startup innovation seems never ending. In the beginning, AWS birthed cloud computing with its first service, SQS, in 2004 and quickly released several additional services (like S3, EC2, and SimpleDB). From this innovation, startups flourished because they were able to build, experiment, and grow faster than before at much lower cost. Airbnb, Netflix, Zynga, and many more were born, and the rest is history.&lt;/p&gt;
&lt;p&gt;Today, a new generation of startups is flourishing because of the cloud, but this time with modern cloud architectures that are distributed, API-driven, and more resilient and scalable than ever. Today’s startups have to get to market even faster and rapidly innovate in order to delight customers and carve out market share. Most startups understand the benefits of adopting the modern cloud to help them achieve this goal. However, their ability to reap these benefits for competitive advantage depends on how well they can harness the modern cloud.&lt;/p&gt;
&lt;p&gt;Within the &lt;a href="https://www.pulumi.com/cloud-engineering/"&gt;cloud engineering&lt;/a&gt; community, we see several common patterns for harnessing the modern cloud. Some startups have teams of full-stack developers who need to deploy cloud infrastructure and applications safely and at high velocity. Others might have a few infrastructure or platform engineers who need to enable other developers to use cloud infrastructure easily on a self-serve basis. Many of these teams started off using domain-specific languages (DSLs) to manage infrastructure as code and quickly found that these languages were the limiting factor in achieving faster velocity. DSLs are cumbersome to use and don’t support the logic and expressiveness needed to build and manage modern architectures that are more complex in nature. DSLs are also a barrier to entry to most developers.&lt;/p&gt;
&lt;p&gt;In contrast, cloud engineering makes cloud infrastructure more accessible and easier to use for everyone with tried and true software engineering practices and tools. It embraces the reality that &lt;a href="https://www.pulumi.com/blog/future-of-cloud-engineering/"&gt;all software is now cloud software&lt;/a&gt;. A core practice is using a commonly used language like TypeScript, Python, Go, or C#. That’s one reason why more and more startups are choosing Pulumi to help them achieve competitive advantage through cloud infrastructure. It also doesn’t hurt that Pulumi was &lt;a href="https://insights.stackoverflow.com/survey/2021#top-paying-technologies-other-tools"&gt;rated the “Top Paying Technology” by developers&lt;/a&gt; in 2021.&lt;/p&gt;
&lt;p&gt;Cloud engineering is a central character in the next chapter of how the modern cloud fuels startup innovation. Read on for three mini-stories of cloud engineering in action with startups.&lt;/p&gt;
&lt;h2 id="panther-labs"&gt;Panther Labs&lt;/h2&gt;
&lt;p&gt;Panther Labs helps modern security teams build world-class detection and response pipelines using code and automation, developer-friendly workflows, and big data primitives. Its Platform Team is responsible for a large, complex serverless architecture on AWS. Because of the limitations of its legacy Infrastructure-as-Code (IaC) tool, the team was unable to manage and scale its cloud infrastructure with the speed and automation that the company needed to support its fast-growing business. After comparing different alternatives, Panther Labs decided to migrate to the Pulumi Cloud Engineering Platform. Pulumi increased the company’s deployment speeds by up to 10x, reduced the size of its infrastructure codebase by &amp;gt;50%, and enabled its developers to adopt cloud engineering best practices to deliver its cloud applications faster and more reliably.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/case-studies/panther-labs"&gt;&lt;img src="pulumi_v_cf.png" alt="Pulumi vs. DSL"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="results"&gt;Results&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Infrastructure teardowns were twice as fast, fresh deployments were four times as fast, and simple changes were more than ten times as fast.&lt;/li&gt;
&lt;li&gt;The infrastructure codebase was reduced by more than 50%.&lt;/li&gt;
&lt;li&gt;Developers could use a programming language and existing software development practices that they already knew.&lt;/li&gt;
&lt;li&gt;The ability to create reusable components made it easy to replicate and share components that always followed cloud engineering best practices.&lt;/li&gt;
&lt;li&gt;Central visibility across hundreds of single-tenant customer deployments, including a history of when changes were made and what changed for each account.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/case-studies/panther-labs"&gt;Read the full case study→&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="whylabs"&gt;WhyLabs&lt;/h2&gt;
&lt;p&gt;WhyLabs helps organizations run their AI applications with certainty by monitoring them for problems such as data drift and model degradation. Founded by alumni of AWS, the company began building their platform on AWS and used AWS-provided tools to manage their infrastructure. However, the WhyLabs team needed a platform that could handle the complexity of their modern cloud architectures and would set them up for success when they’re ready to adopt multi-cloud deployments. The team also wanted a platform that integrated with the modern development workflows used by WhyLabs’ developers, who have a software engineering-driven culture of tight collaboration and shared responsibility for infrastructure. By migrating to the Pulumi Cloud Engineering Platform, WhyLabs engineers can continuously and reliably ship new features faster than before for improved time-to-market. Pulumi enabled WhyLabs to adopt cloud engineering best practices out-of-the-box, including building infrastructure as code with general-purpose languages like TypeScript, versioning and reviewing infrastructure code through a code review process, and delivering infrastructure through its existing GitLab CI/CD pipeline using Pulumi’s integrations.&lt;/p&gt;
&lt;h3 id="results-1"&gt;Results&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Pulumi enabled WhyLabs engineers to continuously and reliably ship new features faster than before for improved time-to-market.&lt;/li&gt;
&lt;li&gt;Pulumi enabled WhyLabs to adopt standard programming languages (like TypeScript) to build infrastructure as code. It also allows them to use software engineering best practices like reusability and unit testing.&lt;/li&gt;
&lt;li&gt;Pulumi lowered the barrier to entry for developers to start working with infrastructure. A standard programming language means that every developer can quickly become involved in creating and maintaining the WhyLabs infrastructure.&lt;/li&gt;
&lt;li&gt;Pulumi enables WhyLabs to shift infrastructure left because testing and security controls are automated with Pulumi and GitLab integration. Peer reviews and accountability are built into the GitLab CI/CD pipeline.&lt;/li&gt;
&lt;li&gt;Pulumi’s integration with GitLab enables WhyLabs to deliver infrastructure through CI/CD pipelines just like with application code, which increases iteration frequency and accelerates time to market.&lt;/li&gt;
&lt;li&gt;Pulumi’s support for multiple cloud providers will help WhyLabs expand their platform in the future to meet customers’ needs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/case-studies/whylabs"&gt;Read the full case study→&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="fauna"&gt;Fauna&lt;/h2&gt;
&lt;p&gt;Fauna provides a flexible, developer-friendly, transactional database delivered as a secure and scalable cloud API with native GraphQL. It needed to provide a way for customers to use its databases while meeting data residency requirements, such as complying with GDPR or FIPS. This meant allowing customers to store data in specific geographies while maintaining low latency and high availability. To build this capability, Fauna’s engineers needed a repeatable, scalable, and efficient way to build, deploy, and manage databases on behalf of its customers. Every database is backed by a cluster of nodes across multiple regions and cloud providers. After evaluating CloudFormation and Terraform, Fauna found that these tools’ domain-specific languages and features would be insufficient for the project. Fauna chose Pulumi to build, deploy, and manage its multi-cloud infrastructure because Pulumi enabled cloud engineering best practices that met its requirements. This included the ability to use a familiar and powerful programming language like Python, integration with existing software development workflows and tools, and software engineering automation.&lt;/p&gt;
&lt;h3 id="results-2"&gt;Results&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Delivered its new multi-region, multi-cloud database feature by using familiar and existing software tools and practices that were enabled by Pulumi.&lt;/li&gt;
&lt;li&gt;Built and managed its complex cloud architecture more efficiently by using familiar languages like Python.&lt;/li&gt;
&lt;li&gt;Increased the reliability and quality of infrastructure using existing code review processes and running unit tests.&lt;/li&gt;
&lt;li&gt;Controlled access to its infrastructure using Pulumi’s SSO integration with its identity provider service.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://web.archive.org/web/20250516064352/https://fauna.com/blog/building-faunas-gdpr-compliant-distributed-and-scalable-database"&gt;Read Fauna&amp;rsquo;s blog post→&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Cloud engineering is enabling startups to build, deploy, and manage modern cloud infrastructure and applications faster and with more confidence. By applying standard software engineering languages, practices and tools to cloud infrastructure, startups can harness modern cloud architectures like containers or serverless to their full potential. To illustrate this, we shared three case studies of startups practicing cloud engineering. Pulumi’s Cloud Engineering Platform was built to enable any organization to adopt cloud engineering best practices out-of-the-box. If you’re starting your cloud engineering journey, try Pulumi for free today with this &lt;a href="https://www.pulumi.com/docs/get-started/"&gt;Getting Started guide&lt;/a&gt;.&lt;/p&gt;</description><author>George Huang</author><category>cloud-engineering</category><category>case-studies</category></item><item><title>Cloud Engineering on the Rise</title><link>https://www.pulumi.com/blog/cloud-engineering-on-the-rise/</link><pubDate>Tue, 03 Aug 2021 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/cloud-engineering-on-the-rise/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/cloud-engineering-on-the-rise/index.png" /&gt;
&lt;p&gt;One of the most fulfilling aspects of working at Pulumi is learning how customers and the community practice cloud engineering in their teams. It’s exciting to see how they use cloud engineering and Pulumi to implement best practices that enable leveraging the cloud to accelerate innovation and enable better business outcomes.&lt;/p&gt;
&lt;p&gt;Pulumi is born from the experiences and needs of teams practicing cloud engineering every day. When we &lt;a href="https://www.pulumi.com/blog/pulumi-3-0/"&gt;announced&lt;/a&gt; the Pulumi Cloud Engineering Platform in April, CEO &amp;amp; Founder Joe Duffy &lt;a href="https://www.youtube.com/watch?v=Zko70KVGcgo"&gt;talked about&lt;/a&gt; bringing cloud engineering to everyone. Over the past year we have seen significant growth in cloud engineering and usage of Pulumi across companies of all industries and sizes, and spanning a diverse spectrum of teams and engineering disciplines. We’re also seeing growing numbers of job postings with “cloud engineer” in the title or which have Pulumi as a requirement or desired skill set.&lt;/p&gt;
&lt;p&gt;Recently, we published several case studies about how teams are applying cloud engineering best practices. Cloud engineers apply standard software engineering practices and tools uniformly across infrastructure management, application development, and security to tame the complexity of delivering and managing modern cloud applications. We’ve published on our &lt;a href="https://www.pulumi.com/cloud-engineering/"&gt;website&lt;/a&gt; and in this &lt;a href="https://www.pulumi.com/blog/infrastructure-testing-concepts/"&gt;blog&lt;/a&gt; some of the key cloud engineering best practices that we see broadly across the community, and encourage you to read further to see three stories of cloud engineering in action.&lt;/p&gt;
&lt;p&gt;These three case studies highlight a small sampling of the many types of companies and teams representing cloud engineers. Some are platform teams at large organizations, like Atlassian, who are responsible for empowering the rest of their developer teams. We also see DevOps teams using cloud engineering, such as at Skai and SANS Institute. There are other types of teams, like full-stack engineers or SREs, which we will share in future posts. All of these teams share a software engineering mindset and make a leveraged impact on the business by increasing speed, automation, reliability, and agility through cloud infrastructure innovation.
&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/cloud-engineering-on-the-rise/atlassian-wordmark.png" alt="Atlassian"&gt;&lt;/p&gt;
&lt;!-- ## Atlassian --&gt;
&lt;p&gt;Atlassian’s Bitbucket DevSpeed team is responsible for improving developer productivity through better workflows and tooling. It used Pulumi to make it easier and faster for its developers to use cloud infrastructure and reduced developers&amp;rsquo; time spent on maintenance by 50%. DevSpeed originally needed a way to expand regional support for its deployment pipeline, which deploys instances on AWS for more than one hundred team members around the world. The team also wanted to build a self-service platform that provisions cloud infrastructure for Bitbucket’s developers, without having to learn a complex domain specific language (DSL). They moved from a legacy DSL-based infrastructure as code tool to the Pulumi Cloud Engineering Platform, which let them define and deploy infrastructure in general-purpose languages that Bitbucket developers already used, such as Python. Using a familiar language like Python also made it easy for them to add cross-regional support to their deployment pipeline. They also built a self-service dashboard that allows any developer to deploy and configure private Bitbucket instances for feature development.&lt;/p&gt;
&lt;h3 id="atlassians-key-practices"&gt;Atlassian&amp;rsquo;s Key Practices&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Builds infrastructure as code with Python.&lt;/li&gt;
&lt;li&gt;Makes infrastructure code readable and accessible to all developers in the organization by using a common language like Python.&lt;/li&gt;
&lt;li&gt;Created a self-service platform that deploys and configures private Bitbucket instances for any Bitbucket developer. Underneath, everything runs through a CI/CD pipeline with Pulumi, Bitbucket Pipelines, and Bamboo.&lt;/li&gt;
&lt;li&gt;Manages and deploys updates to all developer instances in the organization using features like the Pulumi Automation API.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="atlassians-results"&gt;Atlassian&amp;rsquo;s Results&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Allowed developers to use general-purpose programming languages like Python and familiar software tools to deliver and manage infrastructure.&lt;/li&gt;
&lt;li&gt;Enabled Bitbucket developers to easily provision approved cloud infrastructure using a self-service dashboard built with Pulumi.&lt;/li&gt;
&lt;li&gt;Reduced the time Bitbucket developers spent maintaining their instances from 8 hours per week to fewer than four.&lt;/li&gt;
&lt;li&gt;Reduced the size and complexity of its infrastructure code compared to its previous infrastructure tool while increasing the code’s clarity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/case-studies/atlassian"&gt;Read the full case study→&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/cloud-engineering-on-the-rise/sans-wordmark.png" alt="SANS Institute"&gt;&lt;/p&gt;
&lt;!-- ## SANS Institute --&gt;
&lt;p&gt;SANS Institute is the global leader in cybersecurity training. Using Pulumi, the organization adopted cloud engineering practices that enabled it to reduce deployment times by 70%. Its DevOps team needed a more consistent and reliable way to build, deploy, and manage the SANS virtual training environments used in labs. It had previously used legacy infrastructure tools which require inflexible DSLs that aren’t suited to modern cloud applications. The limitations of the DSLs meant the team needed to use AWS Lambda functions and Bash scripts to fill in gaps. The team needed a cleaner solution built for the modern cloud that would be more scalable and easier to manage. By adopting Pulumi, SANS increased delivery speed, quality, and consistency using cloud engineering practices such as building infrastructure as code with TypeScript and software tools, and deploying cloud applications with Git and automated delivery pipelines. It plans to use Pulumi to help other SANS business units modernize their practices and tools.&lt;/p&gt;
&lt;h3 id="sans-institutes-key-practices"&gt;SANS Institute&amp;rsquo;s Key Practices&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Builds infrastructure as code with TypeScript.&lt;/li&gt;
&lt;li&gt;Deploys infrastructure through a GitOps and CI/CD process while managing its Git branches using a GitFlow pattern.&lt;/li&gt;
&lt;li&gt;Production deployments can be initiated through an API call through a self-service platform that deploys an application instance.&lt;/li&gt;
&lt;li&gt;Manages infrastructure configuration through Policy as Code which checks for compliance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="sans-institutes-results"&gt;SANS Institute&amp;rsquo;s Results&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Reduced deployment times for game servers by up to 70%.&lt;/li&gt;
&lt;li&gt;Simplified their deployment process by eliminating the need to “glue together” several infrastructure as code tools, scripts, and functions. This let them remove a manual ticketing step.&lt;/li&gt;
&lt;li&gt;Enabled developers to use familiar programming languages and tools, which also helped make their code cleaner and - more uniform, resulting in streamlined pipelines.&lt;/li&gt;
&lt;li&gt;Helped to identify opportunities in other SANS departments where Pulumi can streamline operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/case-studies/sans-institute"&gt;Read the full case study→&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/cloud-engineering-on-the-rise/skai-logo.png" alt="Skai"&gt;&lt;/p&gt;
&lt;p&gt;Skai is an independent, global marketing platform for strategy, measurement, and best-of-breed activation across all of the world’s most influential digital channels. Skai possesses a highly technical engineering organization with over 350 software engineers, data experts, and DevOps engineers. Skai’s microservices run on AWS, but its core monolith service was hosted in a data center running private cloud infrastructure. Skai’s DevOps group needed to migrate the service to the cloud because demand increased. Skai’s DevOps team decided to transition from using its legacy infrastructure tool because its domain-specific language (DSL) wouldn’t be suited to the demands of the newly migrated modern cloud architectures. Instead, Skai sought a solution that enabled it to use familiar languages and software engineering best practices to manage its infrastructure. That’s why Skai chose the Pulumi Cloud Engineering Platform to build, deploy, and manage its infrastructure with Python.&lt;/p&gt;
&lt;h3 id="skais-key-practices"&gt;Skai&amp;rsquo;s Key Practices&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Builds infrastructure as code in Python.&lt;/li&gt;
&lt;li&gt;Leverages reusable infrastructure components.&lt;/li&gt;
&lt;li&gt;Multiple teams collaborate on cloud infrastructure, including an architect, application development teams, and a core platform/DevOps team.&lt;/li&gt;
&lt;li&gt;Teams use a “loosely-coupled, tightly-aligned” approach to create component classes for different infrastructure elements, with each team working on a different element.&lt;/li&gt;
&lt;li&gt;Uses Python code to integrate infrastructure processes with internal metadata and IT systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="skais-results"&gt;Skai&amp;rsquo;s Results&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Migrated a core monolith service with hundreds of terabytes of data from a data center to AWS.&lt;/li&gt;
&lt;li&gt;Refactored the service’s infrastructure from being managed with a legacy infrastructure configuration tool to Pulumi, using Python as the language of choice.&lt;/li&gt;
&lt;li&gt;Created modular infrastructure components that enable different teams to collaborate and work on different parts of the infrastructure in parallel.&lt;/li&gt;
&lt;li&gt;Streamlined the registration and deregistration process for new application instances by integrating Pulumi with its internal IT systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/kenshoo-migrates-to-aws-with-pulumi"&gt;Read the full case study→&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Cloud engineers are a fast-growing community made of tens of thousands of members and thousands of companies. They apply standard software engineering practices and tools uniformly across infrastructure management, application development, and security to tame the complexity of delivering and managing modern cloud applications. To illustrate cloud engineering in action, we shared three case studies of teams using cloud engineering in their organizations. Pulumi’s Cloud Engineering Platform was built to enable any organization to adopt cloud engineering best practices out-of-the-box. If you’re starting your cloud engineering journey, try Pulumi for free today with this &lt;a href="https://www.pulumi.com/docs/get-started/"&gt;Getting Started guide&lt;/a&gt;.&lt;/p&gt;</description><author>George Huang</author><category>cloud-engineering</category></item><item><title>How Webiny Built a Serverless Application Framework</title><link>https://www.pulumi.com/blog/how-webiny-built-a-serverless-application-framework/</link><pubDate>Thu, 03 Jun 2021 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-webiny-built-a-serverless-application-framework/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-webiny-built-a-serverless-application-framework/index.png" /&gt;
&lt;p&gt;Building an open-source framework for building serverless applications has many challenges, one of which is deploying cloud infrastructure resources. In this article, learn how Webiny uses Pulumi to enable its users to easily deploy and develop applications built on top of serverless cloud technologies.&lt;/p&gt;
&lt;h2 id="what-is-webiny"&gt;What is Webiny?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.webiny.com?utm_source=Pulumi&amp;amp;utm_medium=blog-post&amp;amp;utm_campaign=webiny-blog-promotion&amp;amp;utm_content=how-webiny-built-framework-with-pulumi&amp;amp;utm_term=W00646"&gt;Webiny&lt;/a&gt; is an open-source framework for building serverless applications, completely written in TypeScript.&lt;/p&gt;
&lt;p&gt;In other words, it is a framework that enables developers to architect, build, and deploy solutions on top of the serverless cloud infrastructure, like &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt;, &lt;a href="https://aws.amazon.com/dynamodb/"&gt;Amazon DynamoDB&lt;/a&gt;, &lt;a href="https://aws.amazon.com/s3/"&gt;Amazon S3&lt;/a&gt;, and others. Some examples of what you can build with Webiny today are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;full-stack applications&lt;/li&gt;
&lt;li&gt;websites&lt;/li&gt;
&lt;li&gt;REST and GraphQL APIs&lt;/li&gt;
&lt;li&gt;microservices&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the framework, Webiny also built a content management system called the &lt;a href="https://www.webiny.com/docs/infrastructure/pulumi-iac/iac-with-pulumi"&gt;Webiny Serverless CMS&lt;/a&gt;. It is free, open-source, and by default, included with every new Webiny project.&lt;/p&gt;
&lt;p&gt;Finally, it’s worth mentioning that, at the moment, Webiny exclusively works with &lt;a href="https://aws.amazon.com/"&gt;AWS&lt;/a&gt;. But, bringing Webiny to other cloud providers like &lt;a href="https://azure.microsoft.com/en-us/"&gt;Microsoft Azure&lt;/a&gt; and &lt;a href="https://cloud.google.com/"&gt;Google Cloud Platform (GCP)&lt;/a&gt; is definitely something we’ll be looking at in the future.&lt;/p&gt;
&lt;h2 id="building-the-framework---the-challenge"&gt;Building the Framework - The Challenge&lt;/h2&gt;
&lt;p&gt;Building a serverless framework certainly has its challenges. One of which is the deployment of cloud infrastructure, which, in the world of serverless, is one of the fundamental operations developers need to do, even while the application is still in development.&lt;/p&gt;
&lt;p&gt;Before the &lt;a href="https://www.webiny.com/blog/webiny-v5-the-big-update?utm_source=Pulumi&amp;amp;utm_medium=blog-post&amp;amp;utm_campaign=webiny-blog-promotion&amp;amp;utm_content=how-webiny-built-framework-with-pulumi&amp;amp;utm_term=W00648"&gt;version 5 release&lt;/a&gt;, Webiny relied on an infrastructure provisioning technology called Serverless Components (not to be confused with &lt;a href="https://www.serverless.com/"&gt;Serverless Framework&lt;/a&gt;). Using a concept of components, it is a system that enables developers to deploy ready-made blocks of cloud infrastructure resources configured for a specific use-case. Some examples of components are Website, REST API, a Full Stack Application, and more.&lt;/p&gt;
&lt;p&gt;At the time, it seemed that the overall developer experience and the simplicity of the Serverless Components product was the right choice for our users. So, we decided to go with it.&lt;/p&gt;
&lt;p&gt;But, over time, we noticed several things that, unfortunately, made us question our decision. And if I were to choose the top four, I’d mention the following.&lt;/p&gt;
&lt;h3 id="1-hard-to-customize"&gt;1. Hard to Customize&lt;/h3&gt;
&lt;p&gt;While the idea around components for different use-cases certainly sounded interesting, ultimately, it was not ideal for Webiny. We frequently received questions regarding further component configuration and customization, which was not easy to perform.&lt;/p&gt;
&lt;p&gt;“How do I configure a different database?”, “How do I set a VPC?”, “How do I set up a specific configuration parameter for my S3 bucket?” were just some of the questions we received. The answer almost always included us having to implement support for a particular feature. To some degree, this worked, but it was really us patching the holes, which we knew would only get us so far.&lt;/p&gt;
&lt;h3 id="2-using-yaml-instead-of-code"&gt;2. Using YAML Instead of Code&lt;/h3&gt;
&lt;p&gt;At Webiny, we believe &lt;a href="https://www.pulumi.com/cloud-engineering/"&gt;cloud engineering&lt;/a&gt; and infrastructure-as-code is the future, so, naturally, we wanted our users to use familiar code (ideally TypeScript) and development tools to define their cloud infrastructure. Within Serverless Components, the components are configured via YAML files, of which we were never really big fans, simply because of the fact that writing code instead of configurations gives more flexibility to developers.&lt;/p&gt;
&lt;h3 id="3-vendor-lock-in"&gt;3. Vendor Lock-In&lt;/h3&gt;
&lt;p&gt;Vendor lock-in was introduced at a later stage of the Serverless Components product development. Essentially, to deploy a component, the user is now forced to use a proprietary service that comes with it. And while, among other things, the service enables much faster deployments, from our perspective, we saw this as an additional point of friction for our users. Ideally, users should be able to set up Webiny with only an AWS account.&lt;/p&gt;
&lt;h3 id="4-lack-of-support-for-other-cloud-providers"&gt;4. Lack of Support for Other Cloud Providers&lt;/h3&gt;
&lt;p&gt;At the time, Serverless Components’ support for different cloud providers just wasn’t there. This would be a major roadblock for us as we get to multi-cloud integration.&lt;/p&gt;
&lt;h2 id="discovering-pulumi"&gt;Discovering Pulumi&lt;/h2&gt;
&lt;p&gt;Once we realized Serverless Components wasn’t an ideal solution for Webiny, we started looking for an alternative.&lt;/p&gt;
&lt;p&gt;The key features that we were looking for were the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;open-source&lt;/li&gt;
&lt;li&gt;use code (preferably TypeScript) to create cloud infrastructure resources&lt;/li&gt;
&lt;li&gt;cloud infrastructure code should be flexible, meaning, users should be able to make adjustments to it&lt;/li&gt;
&lt;li&gt;support for multiple cloud providers is a must&lt;/li&gt;
&lt;li&gt;no vendor lock-in&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, the search began, and soon enough, we discovered Pulumi. And as it turned out, it not only checked all the boxes we had initially listed but much more:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;different options when it comes to storing cloud infrastructure state files: a managed SaaS (&lt;a href="https://app.pulumi.com/signin"&gt;pulumi.com&lt;/a&gt;) with a console and self-hosted, (for example &lt;a href="https://aws.amazon.com/s3/"&gt;Amazon S3&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;ability to deploy cloud infrastructure into multiple environments using its concept of &lt;a href="https://www.pulumi.com/docs/concepts/stack/"&gt;stacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;advanced features like &lt;a href="https://www.pulumi.com/docs/using-pulumi/crossguard/get-started/"&gt;Policy as Code&lt;/a&gt; and &lt;a href="https://www.pulumi.com/docs/iac/cli/commands/pulumi_watch/"&gt;watch mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;great documentation&lt;/li&gt;
&lt;li&gt;a vibrant community of developers and a responsive team behind the product&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I probably forgot to mention a thing or two here, but most certainly, at this point, we were ready to make a move and start experimenting with Pulumi.&lt;/p&gt;
&lt;h2 id="integrating-pulumi-with-webiny"&gt;Integrating Pulumi with Webiny&lt;/h2&gt;
&lt;p&gt;For us, the integration of Pulumi with Webiny consisted of three steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;figure out how to integrate Pulumi&amp;rsquo;s programming model with Webiny&lt;/li&gt;
&lt;li&gt;integrate Pulumi CLI into Webiny CLI&lt;/li&gt;
&lt;li&gt;figure out the optimal way of handling cloud infrastructure state files&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s see how we tackled each of these three steps.&lt;/p&gt;
&lt;h3 id="integrating-pulumis-programming-model-with-webiny"&gt;Integrating Pulumi&amp;rsquo;s Programming Model With Webiny&lt;/h3&gt;
&lt;p&gt;In terms of &lt;a href="https://www.webiny.com/docs/key-topics/project-organization/project-applications-and-packages?utm_source=Pulumi&amp;amp;utm_medium=blog-post&amp;amp;utm_campaign=webiny-blog-promotion&amp;amp;utm_content=how-webiny-built-framework-with-pulumi&amp;amp;utm_term=W00649"&gt;project organization&lt;/a&gt;, every Webiny project consists of two key concepts: packages and project applications (or just applications).&lt;/p&gt;
&lt;p&gt;Packages are just regular NPM packages, or in other words, folders with a &lt;code&gt;package.json&lt;/code&gt; manifest file and some code. On the other hand, project applications are higher-level organizational units formed from one or more packages that form applications, as the name itself suggests. Applications consist of both application code and cloud infrastructure needed to run them.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-webiny-built-a-serverless-application-framework/project-organization.png" alt="Webiny Project Organization"&gt;&lt;/p&gt;
&lt;p&gt;A single Webiny project can contain multiple project applications, where each one has its own independent set of necessary cloud infrastructure resources that need to be deployed.&lt;/p&gt;
&lt;p&gt;As an example, a default Webiny project includes three project applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API&lt;/strong&gt; - essentially, represents your GraphQL HTTP API&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Admin Area&lt;/strong&gt; - the Admin Area (React) application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Website&lt;/strong&gt; - the public website, a (React) application with static site generation (SSG) in the cloud&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we were to compare these by the complexity of the necessary cloud infrastructure to deploy, the Admin Area is the simplest. It only relies on a single &lt;a href="https://aws.amazon.com/s3/"&gt;Amazon S3&lt;/a&gt; bucket and an &lt;a href="https://aws.amazon.com/cloudfront/"&gt;Amazon Cloudfront&lt;/a&gt; distribution. On the other hand, the API project application is the most complex one as it needs to deploy multiple &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt; functions, &lt;a href="https://aws.amazon.com/dynamodb/"&gt;Amazon DynamoDB&lt;/a&gt; tables, Amazon S3 buckets, and more.&lt;/p&gt;
&lt;p&gt;Ultimately, we decided that each project application should be a &lt;a href="https://www.pulumi.com/docs/concepts/projects/#projects"&gt;Pulumi project&lt;/a&gt;. With this approach, we gave developers the ability to both define and deploy respective cloud infrastructures independently. And, with the concept of &lt;a href="https://www.pulumi.com/docs/concepts/stack/"&gt;stacks&lt;/a&gt;, they are also able to deploy them into multiple environments, which we’ll show in a moment.&lt;/p&gt;
&lt;h3 id="pulumi-cli-and-webiny-cli"&gt;Pulumi CLI and Webiny CLI&lt;/h3&gt;
&lt;p&gt;Once we understood how to use Pulumi concepts with Webiny&amp;rsquo;s project organization, the next step was integrating the &lt;a href="https://www.pulumi.com/docs/cli/"&gt;Pulumi CLI&lt;/a&gt; with the &lt;a href="https://www.webiny.com/docs/key-topics/webiny-cli?utm_source=Pulumi&amp;amp;utm_medium=blog-post&amp;amp;utm_campaign=webiny-blog-promotion&amp;amp;utm_content=how-webiny-built-framework-with-pulumi&amp;amp;utm_term=W00650"&gt;Webiny CLI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And although the Pulumi CLI is great, we still wanted to keep it super simple for the user and make the overall developer experience as straightforward and unified as possible. For starters, we didn’t want our users to install the Pulumi CLI manually. We wanted it to happen automatically.&lt;/p&gt;
&lt;p&gt;We’ve created our version of the &lt;a href="https://github.com/webiny/webiny-js/tree/v5.28.0/packages/pulumi-sdk"&gt;Pulumi SDK&lt;/a&gt;, which lets us use the Pulumi CLI more programmatically. It also enables us to make the Pulumi CLI download experience as smooth as possible. Essentially, whenever a user runs a deployment-related command, all of the necessary Pulumi CLI binaries and plugins are downloaded and stored inside the project’s node_modules folder.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Although we could’ve saved us some time by using Pulumi’s &lt;a href="https://www.pulumi.com/docs/using-pulumi/automation-api/"&gt;Automation API&lt;/a&gt; (instead of creating the mentioned Pulumi SDK), at the time, the Automation API was still in preview and not generally available. And since the setup we already had was working well, we decided to keep it and hopefully revisit the Automation API integration in the future.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once we had that in place, we were ready for the next step, exposing a couple of fundamental deployment-related commands via the Webiny CLI. The following examples show us some of the commands users can use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Deploys a project application (internally runs &amp;#34;pulumi up&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;webiny deploy &lt;span class="o"&gt;{&lt;/span&gt;PROJECT_APPLICATION_FOLDER&lt;span class="o"&gt;}&lt;/span&gt; --env &lt;span class="o"&gt;{&lt;/span&gt;ENVIRONMENT&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;# Previews a project application deployment (internally runs &amp;#34;pulumi preview&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;webiny deploy &lt;span class="o"&gt;{&lt;/span&gt;PROJECT_APPLICATION_FOLDER&lt;span class="o"&gt;}&lt;/span&gt; --env dev --&lt;span class="o"&gt;{&lt;/span&gt;ENVIRONMENT&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;# Destroys cloud infrastructure resources previously deployed within&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 specified project application (internally runs &amp;#34;pulumi destroy&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;webiny destroy &lt;span class="o"&gt;{&lt;/span&gt;PROJECT_APPLICATION_FOLDER&lt;span class="o"&gt;}&lt;/span&gt; --env &lt;span class="o"&gt;{&lt;/span&gt;ENVIRONMENT&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;# Starts watching specified project application and continuously deploys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# changes to the cloud (internally runs &amp;#34;pulumi watch&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;webiny watch &lt;span class="o"&gt;{&lt;/span&gt;PROJECT_APPLICATION_FOLDER&lt;span class="o"&gt;}&lt;/span&gt; --env &lt;span class="o"&gt;{&lt;/span&gt;ENVIRONMENT&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;Note the &lt;code&gt;--env&lt;/code&gt; argument appended to each command. With it and Pulumi’s concept of stacks, users can easily deploy their project applications into multiple environments.&lt;/p&gt;
&lt;h3 id="cloud-infrastructure-state-files"&gt;Cloud Infrastructure State Files&lt;/h3&gt;
&lt;p&gt;The last piece of the puzzle was storing cloud infrastructure state files. Here we went with the following approach.&lt;/p&gt;
&lt;p&gt;For local development, users’ cloud infrastructure state files are stored locally within their Webiny project using the &lt;a href="https://www.pulumi.com/docs/iac/concepts/state-and-backends/#logging-into-the-local-filesystem-backend"&gt;Local Filesystem Backend&lt;/a&gt;, which we’ve seen worked great for developers.&lt;/p&gt;
&lt;p&gt;On the other hand, for ephemeral environments spawned in CI/CD or long-lived environments like staging or production, through our &lt;a href="https://www.webiny.com/docs/key-topics/ci-cd/cloud-infrastructure-state-files#using-different-backends"&gt;documentation&lt;/a&gt;, we advise our users to use centralized and remote storage by using backends like &lt;a href="https://www.pulumi.com/docs/iac/concepts/state-and-backends/#aws-s3"&gt;Amazon S3&lt;/a&gt; and even &lt;a href="https://www.pulumi.com/docs/iac/concepts/state-and-backends/#logging-into-the-pulumi-service-backend"&gt;Pulumi Cloud (pulumi.com)&lt;/a&gt;. Both backends have their pros and cons, and we let the users choose the one they want to use.&lt;/p&gt;
&lt;h2 id="show-me-the-code"&gt;Show Me the Code&lt;/h2&gt;
&lt;p&gt;Let’s take a look at how it all works in the actual code.&lt;/p&gt;
&lt;p&gt;As mentioned, by default, every Webiny project comes with three project applications: API, Admin Area, and Website, which are located in the &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;apps/admin&lt;/code&gt;, and &lt;code&gt;apps/website&lt;/code&gt; folders, respectively:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── api &lt;span class="c1"&gt;# API&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── pulumi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── Pulumi.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── tsconfig.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── webiny.application.ts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── apps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── admin &lt;span class="c1"&gt;# Admin Area&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── pulumi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── Pulumi.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── tsconfig.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ └── webiny.application.ts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── website &lt;span class="c1"&gt;# Website&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── pulumi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── Pulumi.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── tsconfig.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── webiny.application.ts
&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 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 we can see, every project application follows the same general organization. The two folders in each project are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;code&lt;/code&gt; - contains application code (one or more packages)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pulumi&lt;/code&gt; - contains cloud infrastructure (Pulumi) code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Furthermore, if we opened each of these &lt;code&gt;pulumi&lt;/code&gt; folders, we’d see different cloud infrastructure resources clearly defined via multiple TypeScript classes. As a simple example, if we were to open the &lt;a href="https://github.com/webiny/webiny-js/tree/v5.28.0/packages/cwp-template-aws/template/common/apps/admin/pulumi"&gt;&lt;code&gt;apps/admin/pulumi&lt;/code&gt;&lt;/a&gt; (Admin Area) folder, we’d find the following three files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/webiny/webiny-js/blob/v5.28.0/packages/cwp-template-aws/template/common/apps/admin/pulumi/app.ts"&gt;&lt;code&gt;app.ts&lt;/code&gt;&lt;/a&gt; - deploys an &lt;a href="https://aws.amazon.com/s3/"&gt;Amazon S3&lt;/a&gt; bucket that hosts the Admin Area (React) application&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/webiny/webiny-js/blob/v5.28.0/packages/cwp-template-aws/template/common/apps/admin/pulumi/cloudfront.ts"&gt;&lt;code&gt;cloudfront.ts&lt;/code&gt;&lt;/a&gt; - deploys an &lt;a href="https://aws.amazon.com/cloudfront/"&gt;Amazon CloudFront&lt;/a&gt; distribution for improved availability&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/webiny/webiny-js/blob/v5.28.0/packages/cwp-template-aws/template/common/apps/admin/pulumi/index.ts"&gt;&lt;code&gt;index.ts&lt;/code&gt;&lt;/a&gt; - the Pulumi entrypoint file, imports classes defined within separate files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This code organization makes it much easier for developers to grasp the overall cloud infrastructure. It also makes it easier for them to adjust the code to their needs eventually.&lt;/p&gt;
&lt;p&gt;A more complex example is the cloud infrastructure code in the API project application (&lt;a href="https://github.com/webiny/webiny-js/tree/v5.28.0/packages/cwp-template-aws/template/common/api"&gt;&lt;code&gt;api/pulumi&lt;/code&gt;&lt;/a&gt;), which deploys many different cloud infrastructure resources, like &lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/lambda/"&gt;AWS Lambda functions&lt;/a&gt;, &lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/lambda/"&gt;DynamoDB tables&lt;/a&gt;, &lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/elasticsearch/"&gt;ElasticSearch clusters&lt;/a&gt;, &lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/ec2/"&gt;VPCs&lt;/a&gt;, and more.&lt;/p&gt;
&lt;p&gt;But I will leave this up to you to check out, as pasting multiple chunks of code here might not be that productive.&lt;/p&gt;
&lt;p&gt;Instead, I wanted to focus on a couple of other interesting and useful features that Pulumi and the infrastructure-as-code approach enabled us to create.&lt;/p&gt;
&lt;h3 id="api-project-application---different-dev-and-prod-stacks"&gt;&lt;strong&gt;API&lt;/strong&gt; Project Application - Different &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt; Stacks&lt;/h3&gt;
&lt;p&gt;As mentioned, the API project application contains many different cloud infrastructure resources. But, as it turns out, when it comes to active development, we don’t have to unleash the full potential of the cloud, like we’re doing in staging and production environments.&lt;/p&gt;
&lt;p&gt;For example, it probably makes no sense to deploy an &lt;a href="https://aws.amazon.com/elasticsearch-service/"&gt;Amazon ElasticSearch&lt;/a&gt; cluster into multiple availability zones (AZs) in most cases. A single AZ is enough for development purposes.&lt;/p&gt;
&lt;p&gt;Another good example are &lt;a href="https://aws.amazon.com/vpc/"&gt;VPCs&lt;/a&gt; and potentially &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html"&gt;NAT Gateways&lt;/a&gt;. Again, for development purposes, in most cases, we could probably get away without these too.&lt;/p&gt;
&lt;p&gt;These extra resources make it slower to perform deployments from developers&amp;rsquo; machines and incur extra costs for an organization because developers are deploying more resources, some of which are charged hourly.&lt;/p&gt;
&lt;p&gt;So, with that in mind, we split the API project application’s cloud infrastructure into two stacks - the &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt;. And, as you might’ve already guessed, only the &lt;code&gt;prod&lt;/code&gt; variant will deploy absolutely all necessary cloud infrastructure resources.&lt;/p&gt;
&lt;p&gt;What is even more interesting is the fact that this can be achieved with a simple if statement, which we placed in the &lt;a href="https://github.com/webiny/webiny-js/blob/v5.28.0/packages/cwp-template-aws/template/ddb-es/api/pulumi/index.ts#L19-L25"&gt;&lt;code&gt;index.ts&lt;/code&gt;&lt;/a&gt; entrypoint file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="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;export&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="o"&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="c1"&gt;// WEBINY_ENV represents the environment which was passed upon running
&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 `webiny deploy` command. This is inserted automatically by the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Webiny CLI.
&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBINY_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;prod&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBINY_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;staging&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="c1"&gt;// Import &amp;#34;prod&amp;#34; resources config and initialize resources.
&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="k"&gt;await&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./prod&amp;#34;&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="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&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;// Import &amp;#34;dev&amp;#34; resources config and initialize resources.
&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="k"&gt;await&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./dev&amp;#34;&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="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&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;h4 id="automatic-tagging-of-cloud-infrastructure-resources"&gt;Automatic Tagging of Cloud Infrastructure Resources&lt;/h4&gt;
&lt;p&gt;Another useful feature is the automatic tagging of the deployed cloud infrastructure resources. In other words, every &lt;em&gt;taggable&lt;/em&gt; cloud infrastructure resource will be tagged with &lt;code&gt;WbyProjectName&lt;/code&gt; and &lt;code&gt;WbyEnvironment&lt;/code&gt; tags. For developers, this makes it much easier to see all of the deployed resources within their Webiny project.&lt;/p&gt;
&lt;p&gt;We created a &lt;code&gt;tagResources&lt;/code&gt; function, which essentially registers a global stack transformation via &lt;a href="https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/pulumi/runtime/#registerStackTransformation"&gt;&lt;code&gt;pulumi.runtime.registerStackTransformation&lt;/code&gt;&lt;/a&gt; function to achieve this.&lt;/p&gt;
&lt;p&gt;It is also applied in the same &lt;a href="https://github.com/webiny/webiny-js/blob/v5.28.0/packages/cwp-template-aws/template/common/apps/admin/pulumi/index.ts#L6-L11"&gt;&lt;code&gt;index.ts&lt;/code&gt;&lt;/a&gt; entrypoint file we saw in the previous section:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tagResources&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@webiny/cli-plugin-deploy-pulumi/utils&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;export&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="o"&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;// Add tags to all resources that support tagging.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;tagResources&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;WbyProjectName&lt;/span&gt;: &lt;span class="kt"&gt;process.env.WEBINY_PROJECT_NAME&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;string&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;WbyEnvironment&lt;/span&gt;: &lt;span class="kt"&gt;process.env.WEBINY_ENV&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;string&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="protect-feature"&gt;Protect Feature&lt;/h4&gt;
&lt;p&gt;Finally, to protect our users from accidental deletions of mission-critical cloud infrastructure resources, we’ve used Pulumi’s &lt;a href="https://www.pulumi.com/docs/concepts/resources/#protect"&gt;protect&lt;/a&gt; feature:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The protect option marks a resource as protected. A protected resource cannot be deleted directly. Instead, you must first set &lt;code&gt;protect: false&lt;/code&gt; and run &lt;code&gt;pulumi up&lt;/code&gt;. Then you can delete the resource by removing the line of code or by running &lt;code&gt;pulumi destroy&lt;/code&gt;. The default is to inherit this value from the parent resource and &lt;code&gt;false&lt;/code&gt; for resources without a parent.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, for Webiny users, we’ve made sure that this feature is automatically enabled for resources like &lt;a href="https://github.com/webiny/webiny-js/blob/v5.28.0/packages/cwp-template-aws/template/ddb/api/pulumi/prod/dynamoDb.ts#L27"&gt;DynamoDB tables&lt;/a&gt;, &lt;a href="https://github.com/webiny/webiny-js/blob/v5.28.0/packages/cwp-template-aws/template/ddb/api/pulumi/prod/cognito.ts#L70"&gt;Cognito User Pools&lt;/a&gt;, &lt;a href="https://github.com/webiny/webiny-js/blob/v5.28.0/packages/cwp-template-aws/template/ddb-es/api/pulumi/prod/elasticSearch.ts#L42"&gt;Elasticsearch Cluster&lt;/a&gt;, and similar.&lt;/p&gt;
&lt;h2 id="future-plans"&gt;Future Plans&lt;/h2&gt;
&lt;p&gt;With the &lt;a href="https://www.webiny.com/blog/webiny-v5-the-big-update?utm_source=Pulumi&amp;amp;utm_medium=blog-post&amp;amp;utm_campaign=webiny-blog-promotion&amp;amp;utm_content=how-webiny-built-framework-with-pulumi&amp;amp;utm_term=W00648"&gt;Webiny v5&lt;/a&gt;, we managed to introduce many significant improvements to both the Webiny Serverless Application Framework and Webiny Serverless CMS. However, we still have a lot of new cool things to unravel.&lt;/p&gt;
&lt;p&gt;Webiny aims to be a multi-cloud compatible solution, and this is something Pulumi will significantly assist us with because deploying to multiple cloud providers is supported.&lt;/p&gt;
&lt;p&gt;With that, we would also like to create various project templates, which would, for example, enable users to quickly start working on a new GraphQL or REST HTTP API, a simple React application, a microservice, and more. All of these require specific cloud infrastructure resources to be deployed, and again, this is where Pulumi will make our users&amp;rsquo; lives much easier.&lt;/p&gt;
&lt;p&gt;Finally, I already mentioned the &lt;a href="https://www.pulumi.com/automation/"&gt;Automation API&lt;/a&gt; as something that we might look into in the future, as it seems it’s something that can be seamlessly integrated with Webiny.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;For Webiny, choosing Pulumi as the default &lt;a href="https://www.pulumi.com/what-is/what-is-infrastructure-as-code/"&gt;infrastructure as code&lt;/a&gt; framework was certainly a big leap in the right direction.&lt;/p&gt;
&lt;p&gt;It enabled us to leave the complexities of cloud infrastructure deployments to a product and company whose primary focus is exactly that. And this is what ultimately helped us completely focus on what we care about the most - creating an awesome serverless application framework.&lt;/p&gt;</description><author>Adrian Smijulj</author><category>open-source</category><category>serverless</category><category>frameworks</category></item><item><title>Skai Migrates to AWS with Pulumi</title><link>https://www.pulumi.com/blog/kenshoo-migrates-to-aws-with-pulumi/</link><pubDate>Tue, 04 May 2021 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/kenshoo-migrates-to-aws-with-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/kenshoo-migrates-to-aws-with-pulumi/index.png" /&gt;
&lt;blockquote&gt;
&lt;p&gt;Danny Zalkind is the DevOps group manager for Skai, an award-winning intelligent marketing platform. He brings his 15 years of experience of managing tech teams to his current role where he&amp;rsquo;s dedicated to allow Skai R&amp;amp;D to efficiently produce and serve software. You can find him on &lt;a href="https://www.linkedin.com/in/danny-zalkind-01602b56/"&gt;Linkedin&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Skai is an independent, global marketing platform for strategy, measurement, and best-of-breed activation across all of the world’s most influential digital channels. Skai’s solution provides data-driven insights and optimization technology to help companies make informed decisions and scale performance across critical publishers.&lt;/p&gt;
&lt;p&gt;Skai possesses a highly technical engineering organization with over 350 software engineers, data experts, and DevOps engineers.&lt;/p&gt;
&lt;h2 id="why-skai-is-moving-to-aws"&gt;Why Skai is moving to AWS&lt;/h2&gt;
&lt;p&gt;The public cloud is not entirely new to us. Skai’s microservices have been running on AWS for a few years now. Our core monolith service is hosted in a data center running our private cloud infrastructure with its hundreds of Terabytes of data.&lt;/p&gt;
&lt;p&gt;As our business grew, we noticed that our private cloud infrastructure could not keep pace with our business needs. Adding new capacity to our private cloud could take months of purchasing hardware, preparation, shipping, and deployment.&lt;/p&gt;
&lt;p&gt;We decided to adopt infrastructure elastic enough to support the rate of innovation and growth that the business demanded.&lt;/p&gt;
&lt;p&gt;We sought out the best infrastructure provider to shift as many services as possible to the cloud. AWS is a clear choice because of its unmatched managed services: RDS for our MySQL databases, Lambda functions for our scheduled processes and various automation tasks, DMS for data migration from the datacenter, Keyspaces for our Cassandra cluster, and EKS for our container workloads.&lt;/p&gt;
&lt;p&gt;&lt;img src="image1.png" alt="Architecture"&gt;&lt;/p&gt;
&lt;h2 id="how-pulumi-helped-with-migration"&gt;How Pulumi helped with migration&lt;/h2&gt;
&lt;p&gt;Once the team selected a cloud platform, it was time to plan and execute the migration. Skai’s DevOps group was selected to lead the project.&lt;/p&gt;
&lt;h3 id="why-pulumi"&gt;Why Pulumi&lt;/h3&gt;
&lt;p&gt;Skai’s private cloud infrastructure was provisioned and deployed using a combination of Puppet and Fabric. The code was long, full of customizations, not readable, and challenging to troubleshoot.&lt;/p&gt;
&lt;p&gt;We were looking for a new solution that would:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support cloud-native and multi-cloud architectures&lt;/li&gt;
&lt;li&gt;Allow adding complex logic to the deployment process&lt;/li&gt;
&lt;li&gt;Support reusable code blocks for various use cases&lt;/li&gt;
&lt;li&gt;Allow each team to author different parts of the infrastructure&lt;/li&gt;
&lt;li&gt;Use a modern programming language&lt;/li&gt;
&lt;li&gt;And not create bottlenecks in the release process between the teams&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We immediately fell in love with Pulumi, with its programmatic design, extensive modules, and extensibility via dynamic providers.&lt;/p&gt;
&lt;h3 id="project-and-stack-design"&gt;Project and Stack design&lt;/h3&gt;
&lt;p&gt;When the Skai team decided to standardize on Pulumi, we designed our architecture to support our use case and development cycle.&lt;/p&gt;
&lt;p&gt;We decided to create a project for each AWS organization OUs and a stack for each instance of our application service.&lt;/p&gt;
&lt;p&gt;&lt;img src="image2.png" alt="Repositories"&gt;&lt;/p&gt;
&lt;p&gt;For example, a project would be “platform-production” and contain all stacks and resources of individual environments such as “app1234,” where the number is an environment identifier.&lt;/p&gt;
&lt;p&gt;This provided the optimal mix of releasing new code versions per workload type and placing them in the development lifecycle while making granular configuration changes per each instance more manageable.&lt;/p&gt;
&lt;h3 id="programming-language"&gt;Programming language&lt;/h3&gt;
&lt;p&gt;Pulumi supports several programming languages. For us, Python was an obvious choice due to its highly extensible nature, simplicity, and the team&amp;rsquo;s considerable amount of prior experience with Python. Since most existing automations were written in Python, this made adopting Pulumi very easy for the team.&lt;/p&gt;
&lt;h3 id="multi-team-collaboration"&gt;Multi-team collaboration&lt;/h3&gt;
&lt;p&gt;Our organization is structured in multiple teams, with each team responsible for different areas of our infrastructure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Core team in charge of shared infrastructure such as provisioning AWS accounts&lt;/li&gt;
&lt;li&gt;Multiple application teams are responsible for provisioning specific applications, and&lt;/li&gt;
&lt;li&gt;An architect who leads designs, builds proof of concepts, and writes code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It was essential to build our IaC projects with minimal coupling between the teams while keeping each team aligned with the same overarching project goals.&lt;/p&gt;
&lt;p&gt;To enable this “loosely-coupled, tightly-aligned” approach, we created component classes for different infrastructure elements, which we later referenced in different places in our end-to-end code.&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="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;from&lt;/span&gt; &lt;span class="nn"&gt;pulumi_aws&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pulumi_aws&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ebs&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;class&lt;/span&gt; &lt;span class="nc"&gt;KSAppServer&lt;/span&gt;&lt;span class="p"&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;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&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&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="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;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;KSAppServerArgs&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="p"&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;ResourceOptions&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;kenshoo:KS:KSAppServer&amp;#39;&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 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 class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app_instance&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;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ks-aws-ue-&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ks_id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-app&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;instance_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_size&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;vpc_security_group_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;security_groups_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subnet_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subnet&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;ami&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ami&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;user_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_data_content&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;key_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key_pair&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;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ks-aws-ue-&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ks_id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-app&amp;#34;&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;pulumi&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 class="n"&gt;delete_before_replace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&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;This practice enabled the application team to work on classes without being entirely dependent on the progress of the Core infrastructure team, which worked on classes of more low-level infrastructure such as VPCs and networking objects.&lt;/p&gt;
&lt;h3 id="what-about-those-internal-metadata-systems"&gt;What about those internal metadata systems?&lt;/h3&gt;
&lt;p&gt;One more challenge we faced is registering each new instance of our application with internal metadata and IT systems. In our data center setup, we relied on a set of Jenkins jobs, but now, we wanted to integrate these processes with our code and make sure that we always have a consistent state of registration. We decided to use Pulumi’s dynamic providers to achieve 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="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&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;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://krem.ourdomain.com/api/&amp;#39;&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;class&lt;/span&gt; &lt;span class="nc"&gt;KremMetadataRegistration&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="nf"&gt;create&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;ks_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;/onboarding/init_new_ks&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;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ks&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ks&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ks_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&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&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="nf"&gt;delete&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;ks_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;/config&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ks&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ks_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&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="s1"&gt;&amp;#39;id&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;/onboarding/offboarding&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;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we reference the dynamic provider in another class that we call from the end-to-end code of the whole provisioning flow.&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="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pulumi.dynamic&lt;/span&gt; &lt;span class="kn"&gt;import&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="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.metadata.krem_metadata_registration&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;KremMetadataRegistration&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;class&lt;/span&gt; &lt;span class="nc"&gt;RegistrationSchemaInputs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&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;ks_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Input&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;registered_service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bool&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;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;ks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registered_service&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ks&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ks_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ks&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="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registered_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registered_service&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;class&lt;/span&gt; &lt;span class="nc"&gt;SchemaProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ResourceProvider&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 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="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadatas&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="s1"&gt;&amp;#39;krem&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;KremMetadataRegistration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can streamline the registration and deregistration processes for multiple systems as part of our provisioning lifecycle, creating a more streamlined flow with this approach.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Moving a high-scale and mature application to the public cloud presented quite a few challenges. We have selected Pulumi as our IaC framework of choice due to its native programmatic nature, an endless number of modules, and the ability to customize the solution using our dynamic providers and custom logic.&lt;/p&gt;
&lt;p&gt;This post gave you a glimpse into how we designed and implemented provisioning of our application on AWS. Join us at the second part of the blog that follows our journey of automating data migration.&lt;/p&gt;</description><author>Danny Zalkind</author><category>aws</category><category>guest-post</category><category>migration</category></item><item><title>How Pinpoint Manages Kubernetes Costs and Deployments</title><link>https://www.pulumi.com/blog/pinpoint/</link><pubDate>Mon, 17 Aug 2020 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/pinpoint/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/pinpoint/index.png" /&gt;
&lt;p&gt;&lt;em&gt;This guest blog was contributed by Andrew Kunzel and Michael Goode of &lt;a href="https://www.pinpointhq.com/"&gt;Pinpoint&lt;/a&gt;. Andrew is the Director of Backend Engineering, and Michael is a Platform Operations Engineer.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At &lt;a href="https://www.pinpointhq.com/"&gt;Pinpoint&lt;/a&gt;, Kubernetes is the most powerful tool in our arsenal. It allows us to deploy and rapidly scale our applications with speed and efficiency that continues to delight our customers. In recent years, managed services like AWS EKS have made it easier than ever to leverage the power of Kubernetes in even the smallest of organizations. Yet even with these new conveniences, managing all of this infrastructure can be a daunting task. Right out of the gate, we knew that we wanted to avoid the burden of maintaining repositories full of home-brewed deployment scripts and domain-specific languages like YAML.&lt;/p&gt;
&lt;p&gt;Pulumi affords us the ability to streamline the development of our cloud infrastructure with open source libraries that integrate deeply into AWS service APIs and support many popular languages. This introduces powerful, high-level constructs like for-loops and strong type-checking alongside the more rigid declarative approach mandated by other available tools. And it’s not just limited to hardware! We also use Pulumi to substantially reduce the overhead required to deploy critical services that run on Kubernetes itself, which helps to consolidate our deployment configuration even further and makes updating these services a breeze.&lt;/p&gt;
&lt;p&gt;In this blog post, we’ll share some simple and intuitive ways in which Pulumi has simplified our approach to infrastructure-as-code.&lt;/p&gt;
&lt;h2 id="spot-instances"&gt;Spot Instances&lt;/h2&gt;
&lt;p&gt;As a startup, we are always budget-conscious. Every tool we use has to justify either the up-front subscription cost or development time, or both. To tamp down our AWS EC2 costs, we decided to leverage EC2 spot instances as worker nodes in our development Kubernetes cluster. Spot instances are highly cost-efficient because they use spare compute capacity within a region at a fraction of the on-demand instance price. When you request a spot instance, you must offer the maximum price you are willing to pay. This price can fluctuate based on demand in a particular region, but will always be less than or equal to the current on-demand price. Knowing this, we could use Pulumi’s JavaScript AWS library to get the current on-demand price for a given instance type and plug that directly into the spotPrice property of the node group configuration.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getOnDemandPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InstanceType&lt;/span&gt;&lt;span class="p"&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;Output&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="o"&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;return&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;output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProduct&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;filters&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;instanceType&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;value&lt;/span&gt;&lt;span class="o"&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;instanceType&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&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 class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;operatingSystem&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;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Linux&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 class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;location&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;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;US East (N. Virginia)&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 class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;preInstalledSw&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;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;NA&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 class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;usageType&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;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`BoxUsage:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;instanceType&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&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 class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;termType&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;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OnDemand&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 class="nx"&gt;serviceCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;AmazonEC2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&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;typeOnDemand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnDemand&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;skuTermCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;typeOnDemand&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&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;rateCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;typeOnDemand&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;skuTermCode&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;priceDimensions&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&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;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;typeOnDemand&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;skuTermCode&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;priceDimensions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rateCode&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;pricePerUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USD&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;price&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;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createNodeGroup&lt;/span&gt;&lt;span class="p"&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;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&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="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="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;amiId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ami&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;kubernetesVersion&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;instanceProfile&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nodeGroupInstanceProfile&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;minSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;maxSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&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;desiredCapacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&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;nodeRootVolumeSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&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;nodeAssociatePublicIpAddress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;nodeSubnetIds&lt;/span&gt;&lt;span class="o"&gt;:&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;privateSubnets&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;instanceType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;instanceType&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;spotPrice&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getOnDemandPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceType&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;And just like that, we can quickly provision spot instances on our cluster! Better yet, we don’t ever have to worry about setting a valid spot price or managing this process via custom scripts in a CI/CD pipeline. The entire deployment is easily managed within the same file.&lt;/p&gt;
&lt;h2 id="managing-third-party-services"&gt;Managing Third-Party Services&lt;/h2&gt;
&lt;p&gt;Pulumi also helps us to manage the services that we run on our Kubernetes clusters by allowing us to efficiently configure these services consistently that avoid drift and minimizes complexity. Pulumi is extremely flexible in this regard because it offers native support for de-facto tools like Helm and Kustomize as well as individual Kubernetes components like pods and deployments. You can even render raw YAML, which greatly simplifies integration with legacy systems or custom components.&lt;/p&gt;
&lt;h3 id="transformations-with-helm"&gt;Transformations with Helm&lt;/h3&gt;
&lt;p&gt;We prefer to use Helm for most of our deployments. It is an excellent tool for deploying third party services and customizing them with a useful but complicated templating system. These templates are great when they work, but if the chart&amp;rsquo;s maintainer doesn’t make something configurable, you’re usually unable to alter the manifest when it comes time to deploy your chart. You’d likely have to fork and manage a custom version of the chart yourself in the worst case. Luckily, Pulumi can override just about anything in a manifest by using a Transformation. This property allows you to manipulate a manifest&amp;rsquo;s content before it is rendered and applied to the cluster.&lt;/p&gt;
&lt;p&gt;As an example, we recently discovered that some versions of the Traefik Helm chart do not include an explicit namespace definition on certain templates. This caused a few resources to install into the default namespace; without Pulumi, we would have been unable to fix this issue without forking the chart or writing a post-install script to wrangle them back. Luckily, all we really had to do was create a function to set the object metadata and then pass that function directly into the ‘transformations’ property of the Traefik Helm chart:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;setNamespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&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;namespace&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="nx"&gt;obj&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;namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;platform-services&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="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;new&lt;/span&gt; &lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;traefik&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;repo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;traefik&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;chart&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;traefik&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;6.0.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;platform-services&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="nx"&gt;transformations&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;setNamespace&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;values&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;deployment&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;replicas&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&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 class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;provider&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;This will run the transformation on every manifest used by the chart, fixing our issue in only a few code lines.&lt;/p&gt;
&lt;p&gt;Being able to manage all of our infrastructure as code and enhance the behavior of standard Kubernetes deployment tools has dramatically improved our ability to deploy services and maintain our configuration. We look forward to expanding our use of Pulumi as we grow as an organization and empower our developers further.&lt;/p&gt;</description><author>Andrew Kunzel</author><author>Michael Goode</author><category>guest-post</category><category>kubernetes</category></item><item><title>Test and Optimize Elasticsearch with Pulumi</title><link>https://www.pulumi.com/blog/bigdata-boutique-guest-post/</link><pubDate>Thu, 05 Mar 2020 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/bigdata-boutique-guest-post/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/bigdata-boutique-guest-post/index.png" /&gt;
&lt;p&gt;&lt;strong&gt;Guest Article:&lt;/strong&gt; &lt;a href="https://twitter.com/synhershko"&gt;Itamar Syn-Hershko&lt;/a&gt;, Founder and CTO of &lt;a href="https://bigdataboutique.com/"&gt;BigData Boutique&lt;/a&gt; shows how they use Pulumi to benchmark Elasticsearch configurations across cloud providers. Pulumi enables BigData Boutique to test deployments in parallel and gather metrics to produce performant and cost-effective solutions for its customers.&lt;/p&gt;
&lt;p&gt;BigData Boutique can rapidly iterate through infrastructure configurations based on client requirements. Familiar programming languages significantly shortens the inner dev loop because developers and operators can use modern programming tools such as IDEs, test frameworks, and CI/CD. By increasing the velocity of infrastructure deployment across many clouds with repeatable and versioned code, Pulumi enables companies to build efficient revenue centers for their business.&lt;/p&gt;
&lt;h3 id="how-pulumi-drives-our-elasticsearch-capacity-planning-and-cost-optimization-service"&gt;How Pulumi Drives Our Elasticsearch Capacity Planning and Cost Optimization Service&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Capacity Planning and Cost Optimization of Elasticsearch clusters requires a special level of expertise and automation. Here is how we use Pulumi to launch long-running benchmarks to correctly identify the right configuration for our customers’ Big Data clusters.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At &lt;a href="https://bigdataboutique.com/"&gt;BigData Boutique&lt;/a&gt;, we are continually challenged by our customers—whether it’s complex Big Data challenges we are asked to solve, reducing costs for sophisticated systems holding mountains of data, improving performance for high-demand systems, or challenges faced in building a data-driven system.&lt;/p&gt;
&lt;p&gt;One of the main challenges our customers are facing with Big Data systems is deploying the right hardware at the right scale. They need to know how to correctly estimate the number of servers required to run their compute workloads and databases, so they don’t pay more than they should, while still keeping those services running without interruption and within the defined &lt;a href="https://en.wikipedia.org/wiki/Performance_indicator"&gt;KPI&lt;/a&gt;s.&lt;/p&gt;
&lt;p&gt;To support our customers and help them succeed in their Big Data projects, we provide an array of advanced services, and our unique cost-optimization service addresses this challenge directly. This article outlines how we use long-running benchmarks to correctly identify the right configuration for our customers’ BigData clusters by using the &lt;a href="https://www.pulumi.com/docs/"&gt;infrastructure as code tool Pulumi&lt;/a&gt; for automating the creation of complex infrastructures with conditionals and highly tunable configurations to orchestrate those benchmarks at scale.&lt;/p&gt;
&lt;h4 id="elasticsearch-cost-optimization"&gt;Elasticsearch Cost Optimization&lt;/h4&gt;
&lt;p&gt;Let’s take Elasticsearch as an example. &lt;a href="https://www.elastic.co/elasticsearch"&gt;Elasticsearch&lt;/a&gt; is a real-time search and analytics engine. It is built to ingest huge amounts of data and to allow one to search through and visualize them in real-time. But to be able to sustain the required amount of data on ingestion and the desired number of queries per second, the right hardware has to be used.&lt;/p&gt;
&lt;p&gt;To do this, we have to make some decisions: which disks to use (SSD disks? local, or network storage?), the number of servers (scaling up or out?), CPU and RAM ratios, index mappings, various cluster configurations, and more. These are not easy questions to answer, and that is where our &lt;a href="https://bigdataboutique.com/services/elasticsearch"&gt;Elasticsearch expertise&lt;/a&gt; comes into play. But answering those questions correctly with a high level of certainty also requires rigorous testing for validating assumptions.&lt;/p&gt;
&lt;p&gt;Whether our customer is running on-prem or on a cloud, our &lt;a href="https://bigdataboutique.com/services/elasticsearch/capacity-planning"&gt;Elasticsearch Capacity Planning Service&lt;/a&gt; exists for one purpose: to find the hardware solution with the optimum balance between cost and performance, and do it scientifically, so the answers are as accurate and as precise as possible.&lt;/p&gt;
&lt;h4 id="elasticsearch-sizing-process"&gt;Elasticsearch Sizing Process&lt;/h4&gt;
&lt;p&gt;Our process begins with a sample data request. We ask the customer for their data (or a large sample of it), existing index mappings, expected queries, and finally known KPIs and SLAs. Our goal in the initial discovery phase is to reduce the number of free variables as much as possible because the more such variables, the more permutations we will have to test. Ideally, we want very few dimensions of freedom &amp;mdash; usually in terms of hardware and configurations—so the number of permutations to test is on the order of dozens and not thousands.&lt;/p&gt;
&lt;p&gt;Once the initial framework has been decided, we run fully automated benchmarks to test some of the permutations our experience tells us are the most promising to deliver good results. The benchmarking process is smart enough to omit long tails and outliers, so the results are not going to lie as to the true nature of the configuration that was selected and tested.&lt;/p&gt;
&lt;p&gt;Between benchmark iterations, our team uses its years of experience to create more test configurations that optimize various configurations we decide are important based on the results of the previous benchmarks. After several iterations of benchmarking on various configurations, we send the preliminary findings to confirm our results and proposed direction to the customer. The purpose is to communicate the results and validate the trade-offs we decide on during the sizing procedure. Finally, we provide a summary session with our team of Elasticsearch experts and customer technical and business contacts to present the results and recommendations, and to explain the reasoning behind the analysis.&lt;/p&gt;
&lt;p&gt;Finding the best Elasticsearch configuration requires careful benchmarking and tech expertise. Yet trade-offs will come into play. For instance, it might seem that the only way to gain another 100ms of performance improvement for a certain specific query would be to spend another $500 per month on hardware. But there may be other options. Our Sizing Service takes the guesswork out of the process. We automate the entire benchmarking procedure in order to make it an exact science.&lt;/p&gt;
&lt;p&gt;To fully automate the benchmarking process, we use &lt;a href="https://www.pulumi.com/product/"&gt;Pulumi&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="using-pulumi-for-state-of-the-art-infrastructure-automation"&gt;Using Pulumi for State-of-the-Art Infrastructure Automation&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/"&gt;Pulumi&lt;/a&gt; is an infrastructure-as-code tool that allows us to define and run infrastructures using code, as opposed to doing this manually or using other tools that use configuration-based syntax. This lets us create complex infrastructures with conditionals and highly tunable configurations. The Pulumi program we created can be run numerous times with different parameters. Each result is a cluster that is built and configured differently, sometimes even on completely entirely different clouds.&lt;/p&gt;
&lt;p&gt;At BigData Boutique, we use that concept to launch multiple infrastructures in parallel, benchmark them, and show a report that highlights which config is better along with the various trade-offs for each. After the benchmark process, the cluster is destroyed, and the “provisioner” instance reports all logs, metrics, and the benchmark report, which we then analyze and visualize.&lt;/p&gt;
&lt;p&gt;Generating, visualizing, and analyzing dozens of reports in parallel gives us, the Elasticsearch Engineers, the ability to understand which knobs to turn and what configurations to change to make our customers succeed in their project without spending more than they are willing to.&lt;/p&gt;
&lt;h4 id="conclusion"&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;In sum, leveraging Pulumi helps us to create highly configurable infrastructures to orchestrate benchmarks at scale. As one of many advanced services that we offer, the &lt;a href="https://bigdataboutique.com/services/elasticsearch/capacity-planning"&gt;Elasticsearch Capacity Planning and Cost Optimization Service&lt;/a&gt; continues to meet our customers’ most important planning and optimizations needs in an as scientifically rigorous manner as possible. You can read more about it &lt;a href="https://bigdataboutique.com/services/elasticsearch/capacity-planning"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The original article was published on their &lt;a href="https://blog.bigdataboutique.com/2020/03/how-pulumi-drives-our-elasticsearch-capacity-planning-and-cost-optimization-service-jx8qlu"&gt;company blog&lt;/a&gt;.&lt;/p&gt;</description><author>Itamar Syn-Hershko</author><category>guest-post</category><category>testing</category><category>elasticsearch</category></item><item><title>Mapbox IOT-as-code with Pulumi Crosswalk for AWS</title><link>https://www.pulumi.com/blog/mapbox-iot-as-code-with-pulumi-crosswalk-for-aws/</link><pubDate>Wed, 12 Jun 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/mapbox-iot-as-code-with-pulumi-crosswalk-for-aws/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/mapbox-iot-as-code-with-pulumi-crosswalk-for-aws/index.png" /&gt;
&lt;h2 id="guest-author-chris-toomey-solution-architect-lead--mapbox"&gt;&lt;strong&gt;Guest Author: Chris Toomey, Solution Architect Lead @ Mapbox&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;With 8 billion+ connected IoT devices and 2 billion GPS-equipped
smartphones already online, logistics businesses are tracking assets at
every step in the supply chain. At this scale and complexity, it is
imperative to have a flexible way to ingest, process, and act upon this
data, without sacrificing security or best practices.&lt;/p&gt;
&lt;p&gt;To meet this need, &lt;a href="https://www.mapbox.com/"&gt;Mapbox&lt;/a&gt; has created an
Asset Tracking Solution that uses &lt;a href="https://www.pulumi.com/"&gt;Pulumi&lt;/a&gt;&amp;rsquo;s
open source JavaScript libraries (AWS, AWSX) available with
multi-language support with &lt;a href="https://www.pulumi.com/blog/introducing-pulumi-crosswalk-for-aws-the-easiest-way-to-aws/"&gt;Pulumi Crosswalk for AWS&lt;/a&gt;.
Pulumi Crosswalk for AWS is an open source framework that streamlines
creation, deployment and management of AWS services with built-in AWS
Best Practices and minimal lines of code in common programming
languages.&lt;/p&gt;
&lt;p&gt;In this blog, we will show snippets of the Javascript code that embraces
the power of Pulumi to program AWS service APIs to create the Mapbox
solution. To see the full architecture in action with a live bike race
across America, please refer to this
webinar recorded on June 13th 2019 and the Mapbox
&lt;a href="https://www.mapbox.com/solutions/asset-tracking/"&gt;asset tracking solution&lt;/a&gt;.
Also refer to this blog of the &lt;a href="https://blog.mapbox.com/team-haase-partners-with-mapbox-for-2019-race-across-america-bid-7803a3bdbe49"&gt;Race across America&lt;/a&gt;
showcased live during the webinar tomorrow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; &lt;a href="https://www.pulumi.com/docs/install/"&gt;Install Pulumi&lt;/a&gt;;
&lt;a href="https://nodejs.org/en/download/"&gt;Install Node.js&lt;/a&gt; and
&lt;a href="https://www.pulumi.com/docs/iac/get-started/aws/"&gt;Setup AWS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The diagram represents how Mapbox&amp;rsquo;s solution design on AWS services is
built with Pulumi AWS and AWSX libraries in Javascript. Data is ingested
in the Asset tracking IOT solution with a REST API. Mapbox uses its
stream processor to perform enrichments such as geofencing,
traffic-aware ETA calculations, and high-prevision elevation. This data,
exposed by the API is backed by a high performance database (DynamoDB)
to enable visualization in realtime on the map client.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/mapbox-iot-as-code-with-pulumi-crosswalk-for-aws/aws-architecture-iot.png" alt="AWS-architecture-iot"&gt;&lt;/p&gt;
&lt;p&gt;The fun part &amp;ndash; Pulumi Service console helps you map your cloud
architecture shown above as a connected set of DAG resources so you need
not remember what you deployed as your cloud environments scale:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/mapbox-iot-as-code-with-pulumi-crosswalk-for-aws/pulumi-saas-console-iot.jpg" alt="Pulumi Service"&gt;&lt;/p&gt;
&lt;p&gt;Lets now work through the solution flow with some sample snippets of
code. To get access to the complete piece of code, please connect with
Mapbox solution team &lt;a href="mailto:chris.toomey@mapbox.com"&gt;here&lt;/a&gt; and
Pulumi team &lt;a href="mailto:sales@pulumi.com"&gt;here&lt;/a&gt;. We will be happy to help you sample test this
solution on AWS!&lt;/p&gt;
&lt;h3 id="step-1-create-a-pulumi-project-using-an-aws-javascript-template"&gt;&lt;strong&gt;STEP 1: Create a Pulumi project using an AWS JavaScript template&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ brew install pulumi/tap/pulumi
$ mkdir asset-tracking &amp;amp;&amp;amp; cd asset-tracking
$ pulumi new aws-javascript
$ yarn install
$ ls
Pulumi.dev.yaml Pulumi.yaml index.js node_modules package-lock.json package.json
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="step-2-create-the-ingestion-rest-api-with-aws-services"&gt;&lt;strong&gt;STEP 2: Create the ingestion REST API with AWS Services&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;To visualize data the system has to accept large volumes of data from
multiple sources into the rest of the system. Mapbox uses Pulumi&amp;rsquo;s AWS &amp;amp;
AWSX libraries to build an IOT ingestion rule and forwards it to Kinesis
streams. A sample &lt;code&gt;index.js&lt;/code&gt; looks like this:&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="s2"&gt;&amp;#34;use strict&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;pulumi&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&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;aws&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/aws&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;awsx&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/awsx&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="c1"&gt;// Create Kinesis stream for ingestion
&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;ingestStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kinesis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ingestAssets&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;shardCount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;retentionPeriod&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;72&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 IoT Rule to push into Kinesis stream
&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;iotRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;iotRole&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;assumeRolePolicy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2012-10-17&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;Statement&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Allow&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;Principal&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;Service&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;iot.amazonaws.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="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;Action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sts:AssumeRole&amp;#34;&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iotRolePolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RolePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;iotRolePolicy&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;policy&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;interpolate&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 class="sb"&gt; &amp;#34;Version&amp;#34;: &amp;#34;2012-10-17&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt; &amp;#34;Statement&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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 class="sb"&gt; &amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt; &amp;#34;Action&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt; &amp;#34;kinesis:*&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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 class="sb"&gt; &amp;#34;Resource&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ingestStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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 class="sb"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sb"&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;role&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;iotRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;iotRule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TopicRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;iotTrigger&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;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Pass from IoT Core to Asset Tracking&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;iotAssetIngest&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;enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;kinesis&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;partitionKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;id&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;roleArn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;iotRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&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;streamName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ingestStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;sql&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;SELECT * FROM &amp;#39;topic&amp;#39;&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;sqlVersion&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2015-10-08&amp;#34;&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;Kinesis streams are used to ingest data into multiple AWS services
defined as IOT rules, Firehose and Lambda functions. Each ingest stream
has the right IAM role and policy to allow Kinesis to send the data into
these AWS services.&lt;/p&gt;
&lt;p&gt;The code above does not shown the Javascript code for Lambda functions
used by Mapbox streamprocessor and Firehose all created with Pulumi AWS
and AWSX libraries.&lt;/p&gt;
&lt;p&gt;To get this piece of code, please connect with Mapbox solution team
&lt;a href="mailto:chris.toomey@mapbox.com"&gt;here&lt;/a&gt; and Pulumi team
&lt;a href="mailto:sales@pulumi.com"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="step-3-create-a-consumption-api-for-the-map-client-to-consume-from-a-data-source"&gt;&lt;strong&gt;STEP 3: Create a consumption API for the map client to consume from a data source&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Once, data is flowing through the system we build another API to query
DynamoDB, transform that data, and provide it to our mapping client in
geojson. With Pulumi AWSX, we create a APIGateway endpoint API that can
query this data out of DynamoDB. The sample code in &lt;code&gt;index.js&lt;/code&gt; will look
like this:&lt;/p&gt;
&lt;p&gt;To get this piece of code, please connect with Mapbox solution team
&lt;a href="mailto:chris.toomey@mapbox.com"&gt;here&lt;/a&gt; and Pulumi team
&lt;a href="mailto:sales@pulumi.com"&gt;here&lt;/a&gt;.&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="c1"&gt;// Create DynamoDB Table
&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;assetTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;assetTable&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;attributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;id&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;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;S&amp;#34;&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="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ts&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;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;N&amp;#34;&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="nx"&gt;hashKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;id&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;rangeKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ts&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;ttl&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;attributeName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;expiration&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;enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;billingMode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PAY_PER_REQUEST&amp;#34;&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 API to read DynamoDB
&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;endpoint&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;awsx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apigateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;assetQuery&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;routes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;path&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="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;GET&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;eventHandler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&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="nx"&gt;cb&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;AWS&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;aws-sdk&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;ddb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DynamoDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DocumentClient&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;apiVersion&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2012-10-08&amp;#34;&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;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;assetTable&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="nx"&gt;value&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;params&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;TableName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tableName&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;ddb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&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;features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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;point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;turf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latitude&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;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&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 class="nx"&gt;point&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;featureCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;turf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featureCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;features&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;cb&lt;/span&gt;&lt;span class="p"&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="nx"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&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;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;from&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;featureCollection&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;utf8&amp;#34;&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;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;base64&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;isBase64Encoded&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;application/json&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 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="nx"&gt;stageName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once we&amp;rsquo;ve created the API, we start defining our routes and then
in-line we can define a Lambda function. It can use NPM modules and
Pulumi will handle bundling all the dependencies.&lt;/p&gt;
&lt;p&gt;This endpoint assumes we are querying Dynamo - and so we are going to
use the DynamoDB Document Client. Using that straightforward scan
syntax, we query the table and then start to parse our results. Since
every data point has a latitude and longitude as GeoJSON, the data is
transformed using &lt;a href="http://turfjs.org/"&gt;Turf.js&lt;/a&gt; before sending to the
client. Even more beautiful is how Pulumi handles all the heavy lifting
of packaging, deploying, and giving you the endpoint to query
immediately.&lt;/p&gt;
&lt;h3 id="step-4-hook-up-a-map"&gt;&lt;strong&gt;STEP 4: Hook up a map!&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now we have data coming and going from our Solution, we just have to
hook it up to a Mapbox map. This is actually quite simple now that
Pulumi has built us a REST API.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/mapbox-iot-as-code-with-pulumi-crosswalk-for-aws/map-client.jpg" alt="map-client"&gt;&lt;/p&gt;
&lt;p&gt;Following this
&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/live-geojson/"&gt;example&lt;/a&gt;,
we paste in the endpoint from Pulumi and pick our interval.&lt;/p&gt;
&lt;h3 id="additional-resources"&gt;Additional Resources&lt;/h3&gt;
&lt;p&gt;For more information from Mapbox check out the &lt;a href="https://blog.mapbox.com/"&gt;Mapbox Blog&lt;/a&gt;.
For more solutions from Pulumi please visit our &lt;a href="https://www.pulumi.com/blog/"&gt;Blog&lt;/a&gt;
or head over to the &lt;a href="https://github.com/pulumi/examples"&gt;Pulumi Examples repository&lt;/a&gt;.
For AWS solutions visit the AWS partner &lt;a href="https://aws.amazon.com/blogs/apn/"&gt;blog site&lt;/a&gt;.&lt;/p&gt;</description><author>Chris Toomey</author><category>javascript</category><category>serverless</category><category>aws</category><category>guest-post</category></item></channel></rss>