<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Javascript</title><link>https://www.pulumi.com/blog/tag/javascript/</link><description>Pulumi blog posts: Javascript.</description><language>en-us</language><pubDate>Thu, 20 Jan 2022 08:36:11 -0600</pubDate><item><title>Observable Infrastructure as Code</title><link>https://www.pulumi.com/blog/observable-infrastructure-as-code/</link><pubDate>Thu, 20 Jan 2022 08:36:11 -0600</pubDate><guid>https://www.pulumi.com/blog/observable-infrastructure-as-code/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/observable-infrastructure-as-code/index.png" /&gt;
&lt;p&gt;Pulumi community member &lt;a href="https://kay.is/"&gt;Kay Plößer&lt;/a&gt; spent some time digging into setting up observability of a Pulumi deployment using &lt;a href="https://www.honeycomb.io/"&gt;Honeycomb&lt;/a&gt;. Read more to find out all the details on configuring Honeycomb and Pulumi together, with a side dish of &lt;a href="https://www.pulumi.com/docs/using-pulumi/automation-api/"&gt;Automation API&lt;/a&gt;!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Last few weeks, I procrastinated doing real product-related things with setting up monitoring for my infrastructure deployment.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m using &lt;a href="https://dev.to/pulumidev"&gt;@pulumidev&lt;/a&gt;, a cloud-provider independent IaC framework that allows me to define my infrastructure in TypeScript and use whatever provider I need.&lt;/p&gt;
&lt;p&gt;I read something about &lt;a href="https://www.honeycomb.io/"&gt;Honeycomb&amp;rsquo;s&lt;/a&gt; observability approach to monitoring, and it got me thinking about my deployment code. Later, it will probably run with something like GitHub Actions, so it&amp;rsquo;s not on my machine when it executes. This means I need some insights in case things go wrong. But it would also be good to see how the deployment behaves over time. How do new resources affect deployment durations etc.?&lt;/p&gt;
&lt;p&gt;This is what a trace could look like in Honeycomb:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/observable-infrastructure-as-code/example-trace.png"&gt;&lt;img src="https://www.pulumi.com/blog/observable-infrastructure-as-code/example-trace.png" alt="Example Trace"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Why not setting things up before I move deployment from my development machine to the cloud?&lt;/p&gt;
&lt;p&gt;This article will show you how to make your Pulumi deployment observable with &lt;a href="https://dev.to/honeycomb_io"&gt;@honeycomb_io&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/"&gt;A Pulumi account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/install/"&gt;The Pulumi CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A Pulumi stack&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.honeycomb.io/"&gt;A Honeycomb account&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-pulumi-automation-api"&gt;The Pulumi Automation-API&lt;/h2&gt;
&lt;p&gt;This API is a programmatical interface to the Pulumi CLI. Think Puppeteer for Pulumi instead of Chrome.&lt;/p&gt;
&lt;p&gt;Since it controls the CLI, it can do everything the CLI can and more.&lt;/p&gt;
&lt;p&gt;For our use case, we need information about which resource is deployed to tell Honeycomb about it, and it will create nice trace graphs for us.&lt;/p&gt;
&lt;p&gt;To run a &lt;code&gt;pulumi up&lt;/code&gt; command with the automation API, we need the following code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@pulumi/pulumi/automation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createOrSelectStack&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;stackName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;development&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;workDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;@pulumi/pulumi&lt;/code&gt; package already includes the automation API, so it&amp;rsquo;s the only one we need.&lt;/p&gt;
&lt;p&gt;We use the &lt;code&gt;LocalWorkspace&lt;/code&gt; object to load our stack. This leaves the wholes stack intact; we can still deploy/destroy it with the CLI.&lt;/p&gt;
&lt;p&gt;Then we call &lt;code&gt;stack.up()&lt;/code&gt; and wait. The outputs generated from the API aren&amp;rsquo;t as pretty as the ones from the CLI, but they contain the same information.&lt;/p&gt;
&lt;p&gt;Also, the &lt;code&gt;up&lt;/code&gt; command of the API allows us to catch all events that are happening during the deployment. This is the data we&amp;rsquo;re after.&lt;/p&gt;
&lt;h2 id="the-deployment-events"&gt;The Deployment Events&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;up&lt;/code&gt; method of the &lt;code&gt;stack&lt;/code&gt; object takes a config object. We can use the &lt;code&gt;onEvent&lt;/code&gt; attribute to listen to all the changes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we log out every event. If we look at the output, we notice there are different types of events.&lt;/p&gt;
&lt;p&gt;-The &lt;code&gt;preludeEvent&lt;/code&gt; fires at the start with information about the stack config.
-The &lt;code&gt;resourcePreEvent&lt;/code&gt; fires before a resource is deployed, and the &lt;code&gt;resOutputsEvent&lt;/code&gt; fires after it has been deployed.
-The &lt;code&gt;diagnosticEvent&lt;/code&gt; contains eventual errors that could happen.
-The &lt;code&gt;summaryEvent&lt;/code&gt; includes info about what has been done and if e everything was successful.&lt;/p&gt;
&lt;p&gt;The event structure is a bit strange. The events don&amp;rsquo;t have a type attribute we could &lt;code&gt;switch&lt;/code&gt; over, but an attribute called like the type of the event.&lt;/p&gt;
&lt;p&gt;The following code shows how to get the right event, and where the data hides in that event:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;preludeEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preludeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resourcePreEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourcePreEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resOutputsEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resOutputsEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;diagnosticEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diagnosticEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;summaryEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summaryEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we run things like this, we get all the information we need to send to Honeycomb. The next step is to tell Honeycomb what&amp;rsquo;s up.&lt;/p&gt;
&lt;h2 id="sending-data-to-honeycomb"&gt;Sending Data to Honeycomb&lt;/h2&gt;
&lt;p&gt;Honeycomb provides us with two libraries. The &lt;a href="https://docs.honeycomb.io/getting-data-in/javascript/beeline-nodejs/"&gt;Node.js Beeline&lt;/a&gt; and &lt;a href="https://docs.honeycomb.io/getting-data-in/javascript/libhoney/"&gt;Libhoney&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Libhoney is a low-level Honeycomb client that allows sending raw events to their service API. The Beeline is an abstraction on top of Libhoney to help with instrumentation and traces.&lt;/p&gt;
&lt;p&gt;Usually, it would be best if you used the Beeline because it&amp;rsquo;s easy to set up and works with Node.js&amp;rsquo;s internal HTTP module and the Express framework out of the box.&lt;/p&gt;
&lt;p&gt;In this case, we aren&amp;rsquo;t building an HTTP server, so the Beelines instrumentation doesn&amp;rsquo;t help much.&lt;/p&gt;
&lt;p&gt;So for our deployment example, I will stick to Libhoney.&lt;/p&gt;
&lt;h2 id="honeycomb-events--traces"&gt;Honeycomb Events &amp;amp; Traces&lt;/h2&gt;
&lt;p&gt;Honeycomb uses events to figure out what&amp;rsquo;s happening in your system. Suppose the events have special attributes, like &lt;code&gt;trace.trace_id&lt;/code&gt; and &lt;code&gt;trace.span_id&lt;/code&gt;, Honeycomb can correlate them. This way, it can tell you things like, &amp;ldquo;The trace for your request took 300ms and consisted of 100ms authentication and 200ms database access.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So you can pump all your information to Honeycomb via events. Sometimes it doesn&amp;rsquo;t need to be correlated; you only want to tell Honeycomb that your server uses 20% of memory. But in our case, we want to tie up all events related to one deployment into a trace, so we can figure out what we deploy, how long it takes, and if something goes wrong, what resource was responsible.&lt;/p&gt;
&lt;h2 id="linking-up-pulumi--honeycomb"&gt;Linking up Pulumi &amp;amp; Honeycomb&lt;/h2&gt;
&lt;p&gt;We need to initialize Libhoney and send the correct &amp;ldquo;Honeycomb event&amp;rdquo; for every &amp;ldquo;Pulumi event.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;But let&amp;rsquo;s start with getting one event into Honeycomb: the &lt;code&gt;pulumi up&lt;/code&gt; event.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Libhoney&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;libhoney&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@pulumi/pulumi/automation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Libhoney&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;writeKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;HONEYCOMB_API_KEY&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;example-iac&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;traceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`trace`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`trace-root-span`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createOrSelectStack&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;stackName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;development&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;workDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendNow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;up&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Pulumi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We define a &lt;code&gt;traceId&lt;/code&gt;, a &lt;code&gt;rootSpanId&lt;/code&gt;, and a &lt;code&gt;startTimestamp&lt;/code&gt; at the beginning, and then we send it off when everything is done.&lt;/p&gt;
&lt;p&gt;An event with &lt;code&gt;trace.trace_id&lt;/code&gt; will be grouped up with all other events with the same trace ID. In this case, we only have one event.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; is usually the action you perform and want to measure, and the &lt;code&gt;service_name&lt;/code&gt; is, well, the service that performs this action. In this case, we run &lt;code&gt;pulumi up&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the end, we also send a &lt;code&gt;duration_ms&lt;/code&gt; attribute to track how long things take.&lt;/p&gt;
&lt;p&gt;The result will look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/observable-infrastructure-as-code/trace-result.png"&gt;&lt;img src="https://www.pulumi.com/blog/observable-infrastructure-as-code/trace-result.png" alt="Honeycomb trace graph"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Not very interesting, but at least we know it run without crashing and how long it took.&lt;/p&gt;
&lt;h2 id="getting-more-details"&gt;Getting More Details&lt;/h2&gt;
&lt;p&gt;The next step is to get the details. What were the config parameters? How long did different parts of the deployment take?&lt;/p&gt;
&lt;p&gt;For this, we need to look into two events, the &lt;code&gt;preludeEvent&lt;/code&gt; and the &lt;code&gt;summaryEvent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Until now, we only send one Honeycomb event for the Pulumi up command.&lt;/p&gt;
&lt;p&gt;Now, we will send three events for the stack.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;init&lt;/code&gt; event will contain the time it took to get to the actual deployment of resources and the config parameters of the stack&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;run&lt;/code&gt; event will contain the duration all resources took to do their thing.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;finish&lt;/code&gt; event will contain the duration of shutting down the automation API and the summary data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s look at the updated example code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;traceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`trace`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`trace`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stackFinishStartTimestamp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initStartTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runStackSpanId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`stack`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stackRunStartTimestamp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LocalWorkspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createOrSelectStack&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;stackName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;development&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;workDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;preludeEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initStartTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;init&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Stack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.parent_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`stack`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;initStartTimestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preludeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;stackRunStartTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;summaryEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stackRunStartTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;run&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Stack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.parent_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;runStackSpanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;stackRunStartTimestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;stackFinishStartTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stackFinishStartTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;finish&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Stack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.parent_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`stack`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;stackFinishStartTimestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;up&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Pulumi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootSpanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First, we need to sprinkle variables on the code that capture the start times of our three events.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;init&lt;/code&gt; event starts before the call to the up command and takes until the &lt;code&gt;preludeEvent&lt;/code&gt; is fired by the automation API.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;run&lt;/code&gt; event starts right after the &lt;code&gt;init&lt;/code&gt; event finishes and takes until the &lt;code&gt;summaryEvent&lt;/code&gt; fires.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;finish&lt;/code&gt; event starts right after &lt;code&gt;run&lt;/code&gt; finishes and stops right before the parent event for Pulumi up is finished.&lt;/p&gt;
&lt;p&gt;These events all get the &lt;code&gt;trace.parent_id&lt;/code&gt; of the Pulumi up event, so they get nested in the Honeycomb view later.&lt;/p&gt;
&lt;p&gt;Also, the &lt;code&gt;init&lt;/code&gt; event gets the &lt;code&gt;event.preludeEvent.config&lt;/code&gt; object, so we can see how the stack was configured inside Honeycomb.&lt;/p&gt;
&lt;p&gt;If we run that example, we should see something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/observable-infrastructure-as-code/second-result.png"&gt;&lt;img src="https://www.pulumi.com/blog/observable-infrastructure-as-code/second-result.png" alt="Honeycomb span with details"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="getting-resource-deployment-durations"&gt;Getting Resource Deployment Durations&lt;/h2&gt;
&lt;p&gt;The resources have additional details for us. As a &lt;code&gt;name&lt;/code&gt; for the event, we will use the operation (create, update, delete) and as &lt;code&gt;service_name&lt;/code&gt; we use the name of the resource in the Pulumi definition.&lt;/p&gt;
&lt;p&gt;To get the parent of a resource, we have to save the URNs of every resource for later. But since we have to keep the start times anyway, we can do this in one step when the &lt;code&gt;resourcePreEvent&lt;/code&gt; is fired.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at the example code. This time I removed some boilerplate code and the other events to make things a bit more readable.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resourceSpans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resourcePreEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourcePreEvent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;resourceSpans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;spanId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`span`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;startTimestamp&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;resOutputsEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resOutputsEvent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serviceName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;::&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;spanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resourceSpans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentUrn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;old&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resourceSpans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parentUrn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spanId&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;runStackSpanId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newEvent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.trace_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;traceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.parent_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;trace.span_id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;spanId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;duration_ms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTimestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;update&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;diffs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hnyEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, what&amp;rsquo;s going on here?&lt;/p&gt;
&lt;p&gt;First, the resources are deployed by their hierarchy, but siblings can be deployed in a different order. That&amp;rsquo;s why we need to save their &lt;code&gt;spanId&lt;/code&gt; and &lt;code&gt;startTimestamp&lt;/code&gt; when we get a &lt;code&gt;resourcePreEvent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I used a &lt;code&gt;Map&lt;/code&gt; for this and keyed it with the URN of the resource because URNs are used for parent-child relationships and are part of all events we care about here.&lt;/p&gt;
&lt;p&gt;Later, when the &lt;code&gt;resOutputsEvent&lt;/code&gt; fires, it&amp;rsquo;s time to wrap it all up and send it to Honeycomb.&lt;/p&gt;
&lt;p&gt;The event has the URN to find the related &lt;code&gt;spanId&lt;/code&gt; and &lt;code&gt;startTimestamp&lt;/code&gt;. We can also use the URN in the parent attribute of our resource to fetch the &lt;code&gt;spanId&lt;/code&gt; of our resource&amp;rsquo;s parent. This way, our spans are correctly nested in Honeycomb later.&lt;/p&gt;
&lt;p&gt;If the operation for the resource is an &lt;code&gt;update&lt;/code&gt;, we also add the &lt;code&gt;diffs&lt;/code&gt; to that event. This way, we can see in Honeycomb what inputs have changed since the last deployment.&lt;/p&gt;
&lt;p&gt;The final trace should look a bit like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/observable-infrastructure-as-code/final-trace.png"&gt;&lt;img src="https://www.pulumi.com/blog/observable-infrastructure-as-code/final-trace.png" alt="Final Honeycomb trace"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="getting-errors"&gt;Getting Errors&lt;/h2&gt;
&lt;p&gt;The last step, getting deployment errors, I leave as an exercise for the reader.&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;ll give you a few hints!&lt;/p&gt;
&lt;p&gt;First, the interesting event is the &lt;code&gt;diagnosticEvent&lt;/code&gt;, which comes with a &lt;code&gt;severity&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt; attribute. If you only need errors, you have to filter out &lt;code&gt;debug&lt;/code&gt; and &lt;code&gt;info&lt;/code&gt; severity.&lt;/p&gt;
&lt;p&gt;Also, the event includes an &lt;code&gt;urn&lt;/code&gt; that can be used to correlate the error to a Honeycomb span later (&lt;code&gt;resourceSpans&lt;/code&gt; etc.). If the errors isn&amp;rsquo;t related to an URN, you can simply use the run stack event as target.&lt;/p&gt;
&lt;p&gt;If you add an &lt;code&gt;error&lt;/code&gt; field to a Honeycomb event, it will automatically counted to your errors in Honeycomb.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;IaC observability becomes rather simple when using Pulumi and Honeycomb.&lt;/p&gt;
&lt;p&gt;Pulumi&amp;rsquo;s automation API allows us to control the CLI with JavaScript, and Honeycomb&amp;rsquo;s Libhoney is low-level enough to instrument every part of our deployment process.&lt;/p&gt;
&lt;p&gt;This way we can keep track which resources are slowing our deployment down, or which was responsible for a failed deployment.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This article was originally posted at &lt;a href="https://dev.to/fllstck/observable-infrastructure-as-code-52ha"&gt;https://dev.to/fllstck/observable-infrastructure-as-code-52ha&lt;/a&gt;. Reposted here with permission.&lt;/em&gt;&lt;/p&gt;</description><author>Kay Plößer</author><category>community</category><category>javascript</category><category>observability</category><category>automation-api</category></item><item><title>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><item><title>Get Started with Docker on AWS Fargate using Pulumi</title><link>https://www.pulumi.com/blog/get-started-with-docker-on-aws-fargate-using-pulumi/</link><pubDate>Tue, 30 Apr 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/get-started-with-docker-on-aws-fargate-using-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/get-started-with-docker-on-aws-fargate-using-pulumi/index.png" /&gt;
&lt;div class="note note-warning"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-warning-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;Some of the code in this post is out of date. See the &lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;AWS guides&lt;/a&gt; for an updated overview and examples.&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Update: Check out the &lt;a href="https://www.pulumi.com/case-studies/learning-machine/"&gt;Learning Machine Case Study&lt;/a&gt; where provisioning went from 3 weeks to 1 hour with Pulumi and AWS.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;ldquo;The impact of serverless capabilities was also transformative for the Learning Machine business. Pulumi enabled a rapid shift to Amazon ECS, AWS Fargate and AWS Lambda — the net effect of which was a 67% reduction in AWS charges. This enabled the team to spend less time focused on maintaining existing infrastructure and more time deploying new applications on AWS and adding new customers.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Pulumi is the foundational technology that allowed us to transform our organization,” said Hughes. The entire DevOps process was streamlined and in addition to realizing better productivity and higher quality, the team has new insight into their SaaS offering that they never thought possible.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;a href="https://docs.docker.com/get-started/"&gt;Docker Getting Started tutorial&lt;/a&gt; shows how to develop,
build, and run a modern containerized application, from a single custom
Docker container published to the Docker Hub, to a scaled out service
with load balancing. But there are challenges: it requires you to
program in YAML, run (or script) many CLI commands, and manage your own
Swarm or Kubernetes cluster. There is an easier way. By using Pulumi&amp;rsquo;s
infrastructure as code, we can build a custom Docker image, publish it
to a private AWS container registry, and spin up an AWS Fargate load
balanced service running that container, all in 28 lines of TypeScript
code and a single &lt;code&gt;pulumi up&lt;/code&gt; command. The result leverages the best of
what AWS has to offer, with the entire platform at your fingertips, with
a single approach. In this article, we&amp;rsquo;ll see how.&lt;/p&gt;
&lt;h2 id="defining-everything-in-code"&gt;Defining Everything in Code&lt;/h2&gt;
&lt;p&gt;The Docker Getting Started tutorial goes from a simple developer desktop
scenario to a fully scaled out service in production. There is a lot of
ceremony, however: YAML, many CLI commands, running your own Swarm
cluster by hand, etc. This is complicated and doesn&amp;rsquo;t lend itself to
repeatable deployments across many environments.&lt;/p&gt;
&lt;p&gt;What if we could just write 28 lines of TypeScript instead, and
accomplish the same end result, while leveraging the latest innovations
in the AWS platform, natively?&lt;/p&gt;
&lt;p&gt;In fact, we can. This article will show you how Pulumi&amp;rsquo;s infrastructure
as code solution enables exactly this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;*&lt;strong&gt;Note:&lt;/strong&gt; Everything we&amp;rsquo;re about to define will be in a single
TypeScript file, &lt;code&gt;index.ts&lt;/code&gt;. Other languages are available, including
using plain Node.js JavaScript. Pulumi also supports other clouds,
including Azure and GCP, however to fully exploit the power of the
underlying AWS platform, these examples will leverage native
capabilities like VPCs, ALBs, and
Fargate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To cut to the chase, here is the entire program. We&amp;rsquo;ll spend the rest of
the post walking through it line by line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;awsx&lt;/span&gt; &lt;span class="kr"&gt;from&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;// Step 1: Create an ECS Fargate cluster.
&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;cluster&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;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cluster&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;// Step 2: Define the Networking for our service.
&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;alb&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;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApplicationLoadBalancer&lt;/span&gt;&lt;span class="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;net-lb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt;: &lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;securityGroup&lt;/span&gt;: &lt;span class="kt"&gt;cluster.securityGroup&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;web&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;: &lt;span class="kt"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt;: &lt;span class="kt"&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Step 3: Build and publish a Docker image to a private ECR registry.
&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;img&lt;/span&gt; &lt;span class="o"&gt;=&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;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;app-img&amp;#34;&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&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;// Step 4: Create a Fargate service task that can scale out.
&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;appService&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;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FargateService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;app-svc&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;cluster&lt;/span&gt;&lt;span class="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;taskDefinitionArgs&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;container&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;image&lt;/span&gt;: &lt;span class="kt"&gt;img&lt;/span&gt;&lt;span class="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;cpu&lt;/span&gt;: &lt;span class="kt"&gt;102&lt;/span&gt; &lt;span class="cm"&gt;/*10% of 1024*/&lt;/span&gt;&lt;span class="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;memory&lt;/span&gt;: &lt;span class="kt"&gt;50&lt;/span&gt; &lt;span class="cm"&gt;/*MB*/&lt;/span&gt;&lt;span class="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;portMappings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt; &lt;span class="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;desiredCount&lt;/span&gt;: &lt;span class="kt"&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="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;// Step 5: Export the Internet address for the service.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&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;h2 id="step-1-create-a-cluster"&gt;Step 1. Create a Cluster&lt;/h2&gt;
&lt;p&gt;The opening stanza imports Pulumi&amp;rsquo;s
&lt;a href="https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/awsx/"&gt;open source AWSX NPM package&lt;/a&gt;, &lt;code&gt;@pulumi/awsx&lt;/code&gt;. It
contains high level AWS best practices and patterns, and leverages real
languages to eliminate boilerplate YAML templating:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;awsx&lt;/span&gt; &lt;span class="kr"&gt;from&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To provision an entire &lt;a href="https://aws.amazon.com/ecs/"&gt;Elastic Container Service (ECS)&lt;/a&gt; cluster, AWS&amp;rsquo;s native container
orchestration technology, we just new one up in a single line of code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cluster&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;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cluster&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;This will use the default &lt;a href="https://aws.amazon.com/vpc/"&gt;Virtual Private Cloud (VPC)&lt;/a&gt; in our account. If we want to
customize this aspect along with the auto-scaling policies, we can
easily override the defaults by passing the appropriate arguments.&lt;/p&gt;
&lt;h2 id="step-2-define-the-networking"&gt;Step 2. Define the Networking&lt;/h2&gt;
&lt;p&gt;Next we need to think about the networking for our desired application.
First, we will need a load balancer, as we&amp;rsquo;re going to create a
scaled-out application and want a fixed address. Second, we will want to
expose port 80 to the Internet, for incoming traffic. This leads to
these two lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alb&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;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApplicationLoadBalancer&lt;/span&gt;&lt;span class="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;net-lb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt;: &lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;securityGroups&lt;/span&gt;: &lt;span class="kt"&gt;cluster.securityGroups&lt;/span&gt; &lt;span class="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;web&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;web&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;: &lt;span class="kt"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt;: &lt;span class="kt"&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;An &lt;a href="https://aws.amazon.com/elasticloadbalancing/"&gt;Elastic Application Load Balancer (ALB)&lt;/a&gt; in AWS is a
&lt;a href="https://aws.amazon.com/blogs/aws/new-aws-application-load-balancer/"&gt;fully featured Layer 7 load balancer&lt;/a&gt;,
with advanced features around SSL termination, content based routing,
and HTTP/2. The AWSX package enables you to whip together simple routing
when appropriate, while still having the ability to dig deeper into the
advanced capabilities as you scale your workload to handle more
sophisticated scenarios. Simple things simple, hard things possible!&lt;/p&gt;
&lt;h2 id="step-3-build-and-publish-the-container-image"&gt;Step 3. Build and Publish the Container Image&lt;/h2&gt;
&lt;p&gt;The next line declares a Docker image:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&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;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;app-img&amp;#34;&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This single line is quite the work horse, and does a lot. Let&amp;rsquo;s unpack
it a bit.&lt;/p&gt;
&lt;p&gt;First and foremost, this allocates a private &lt;a href="https://aws.amazon.com/ecr/"&gt;Elastic Container Registry (ECR)&lt;/a&gt; repository for your Docker
image. This is AWS&amp;rsquo;s hosted Docker registry solution, privately
encapsulated inside your AWS account.&lt;/p&gt;
&lt;p&gt;But there is more: this also builds the &lt;code&gt;Dockerfile&lt;/code&gt; underneath &lt;code&gt;./app&lt;/code&gt;
and publishes the result to said registry, each time you run
&lt;code&gt;pulumi up&lt;/code&gt;! Any &lt;code&gt;Dockerfile&lt;/code&gt; will work. This means you get a single
command line to both provision and update your infrastructure, in
addition to publishing and rolling out your images.&lt;/p&gt;
&lt;h2 id="step-4-create-a-scale-out-service"&gt;Step 4. Create a Scale-Out Service&lt;/h2&gt;
&lt;p&gt;Next up, let&amp;rsquo;s deploy our published image as a containerized service,
behind our load balanced network on port 80, using our &lt;a href="https://aws.amazon.com/fargate/"&gt;ECS Fargate&lt;/a&gt; cluster. Fargate is a managed
container orchestrator that lets us skip the messy details of installing
and managing Swarm on our own. This effectively replaces
&lt;a href="https://docs.docker.com/get-started/08_using_compose/"&gt;the &lt;strong&gt;docker-compose.yml&lt;/strong&gt; from the Docker Getting Started tutorial&lt;/a&gt;, with a similarly
simple sequence of code, and which gives us full access to the AWS
platform:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appService&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;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FargateService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;app-svc&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;cluster&lt;/span&gt;&lt;span class="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;taskDefinitionArgs&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;container&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;image&lt;/span&gt;: &lt;span class="kt"&gt;img&lt;/span&gt;&lt;span class="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;cpu&lt;/span&gt;: &lt;span class="kt"&gt;128&lt;/span&gt; &lt;span class="cm"&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;memory&lt;/span&gt;: &lt;span class="kt"&gt;50&lt;/span&gt; &lt;span class="cm"&gt;/*MB*/&lt;/span&gt;&lt;span class="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;portMappings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt; &lt;span class="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;desiredCount&lt;/span&gt;: &lt;span class="kt"&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="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice that &lt;code&gt;img&lt;/code&gt; just refers to the Docker image built in Step 3, and
the &lt;code&gt;portMappings&lt;/code&gt; structure references our load balancer&amp;rsquo;s listener
defined in Step 2. Pulumi tracks all the dependencies so that it knows
how to perform deployment activities in the right order. The task
definition structure here exposes
&lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/userguide/fargate-task-defs.html"&gt;all of the underlying AWS ECS capabilities&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="step-5-deploy-the-app-and-kick-the-tires"&gt;Step 5. Deploy the App and Kick the Tires!&lt;/h2&gt;
&lt;p&gt;On to the final step! Let&amp;rsquo;s deploy our infrastructure and application,
and kick the tires.&lt;/p&gt;
&lt;p&gt;The last line of code exposes the load balancer address so it&amp;rsquo;s easy to
access:&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;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The net result is that, with this project defined with the 28 lines of
code above, we can simply run &lt;code&gt;pulumi up&lt;/code&gt;; this command will show us a
preview of the changes it will make and then, once confirmed, deploy
everything to our AWS account, and hand back our service&amp;rsquo;s hostname:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/get-started-with-docker-on-aws-fargate-using-pulumi/docker-fargate-cli.png" alt="DockerFargateCLI"&gt;&lt;/p&gt;
&lt;p&gt;After deploying everything, you&amp;rsquo;ve got a fully functioning application.
To give it a try, let&amp;rsquo;s grab the URL exported by our program from the
stack output command, and &lt;code&gt;curl&lt;/code&gt; it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl http://$(pulumi stack output url)
&amp;lt;h3&amp;gt;Hello World!&amp;lt;/h3&amp;gt;
&amp;lt;b&amp;gt;Hostname:&amp;lt;/b&amp;gt; ip-172-31-39-18.ec2.internal&amp;lt;br/&amp;gt;
&amp;lt;b&amp;gt;Visits:&amp;lt;/b&amp;gt; &amp;lt;i&amp;gt;cannot connect to Redis, counter disabled&amp;lt;/i&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or open it in our web browser:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ open http://$(pulumi stack output url)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/get-started-with-docker-on-aws-fargate-using-pulumi/docker-fargate-browser.png" alt="DockerFargateBrowser"&gt;&lt;/p&gt;
&lt;p&gt;As an added bonus, the entire time we are doing these deployments, the
Pulumi Service is keeping track of deployment state, to ensure
everything works reliably and in a team setting, similar to how Git
works with GitHub. For example, I can see a history of my deployments:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/get-started-with-docker-on-aws-fargate-using-pulumi/docker-fargate-history.png" alt="DockerFargateHistory"&gt;&lt;/p&gt;
&lt;h2 id="congratulations"&gt;Congratulations!&lt;/h2&gt;
&lt;p&gt;In this article, you&amp;rsquo;ve seen how to take 28 lines of code, and turn
them, plus a Dockerfile, into a fully functioning AWS microservice,
including the infrastructure and application resources. No YAML
required, no complex sequence of CLI commands, and no manual cluster
installation.&lt;/p&gt;
&lt;p&gt;Pulumi&amp;rsquo;s &amp;ldquo;everything is code&amp;rdquo; approach means deploying everything can be
done with a CLI command, unlocking the power of the entire Docker
platform with a great inner development loop, that works from the
desktop all the way to production. This entire flow can be
&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/"&gt;easily integrated into your favorite CI/CD pipeline&lt;/a&gt;,
including GitOps workflows.&lt;/p&gt;
&lt;p&gt;If we want to augment our service with other AWS resources &amp;ndash; like S3
buckets, Aurora or RDS databases, Lambda functions, CloudWatch
dashboards, etc &amp;ndash; they can be provisioned and managed the same was
created our clusters and load balancers: just &lt;code&gt;new&lt;/code&gt; them up!&lt;/p&gt;
&lt;p&gt;Remember that all of this not only works for native AWS ECS containers,
but also Azure and GCP cloud providers, in addition to Kubernetes!&lt;/p&gt;
&lt;p&gt;Want to go deeper?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-hello-fargate"&gt;Check out this example on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/get-started/"&gt;Get Started with your favorite cloud and scenario now&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><author>Joe Duffy</author><category>javascript</category><category>aws</category><category>containers</category><category>typescript</category></item><item><title>Testing Your Infrastructure as Code with Pulumi</title><link>https://www.pulumi.com/blog/testing-your-infrastructure-as-code-with-pulumi/</link><pubDate>Wed, 17 Apr 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/testing-your-infrastructure-as-code-with-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/testing-your-infrastructure-as-code-with-pulumi/index.png" /&gt;
&lt;div class="note note-warning"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-warning-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;Some parts of this blog post are out-of-date. Please refer to our &lt;a href="https://www.pulumi.com/docs/iac/concepts/testing/"&gt;Testing Guide&lt;/a&gt; for the updated overview and tutorials.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Using Pulumi and general purpose languages for &lt;a href="https://www.pulumi.com/what-is/what-is-infrastructure-as-code/"&gt;infrastructure as code&lt;/a&gt;
comes with many benefits: leveraging existing skills and knowledge,
eliminating boilerplate through abstraction, and using the same
ecosystem of tools like IDEs and linters that your team already knows
and loves. In general, these are all attributes of software engineering,
which not only make us more productive, but also improve the quality of
our code. It&amp;rsquo;s only natural, therefore, that using general purpose
languages unlocks another important software engineering practice:
&lt;strong&gt;testing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this article, we will see the many ways in which Pulumi lets us test
our infrastructure as code.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/testing-your-infrastructure-as-code-with-pulumi/InfraTesting.png" alt="Testing Infrastructure"&gt;&lt;/p&gt;
&lt;h2 id="why-testyour-infrastructure"&gt;Why Test Your Infrastructure?&lt;/h2&gt;
&lt;p&gt;Before jumping into the details, it&amp;rsquo;s worth asking: Why test your
infrastructure, anyway? There are many reasons to do this, however here
are a few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unit testing individual functions or pieces of logic in your program&lt;/li&gt;
&lt;li&gt;Ensuring that the program&amp;rsquo;s desired infrastructure state meets
certain constraints&lt;/li&gt;
&lt;li&gt;Catching common mistakes, like forgetting to encrypt your storage
buckets, or exposing virtual machines unprotected to the open
Internet&lt;/li&gt;
&lt;li&gt;Ensuring that the provisioned infrastructure has certain desired
attributes&lt;/li&gt;
&lt;li&gt;Doing runtime testing of the application logic that is running
inside of your compute infrastructure, to ensure that it is healthy
after your infrastructure has been provisioned&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As we see, there&amp;rsquo;s a broad spectrum of &lt;a href="https://www.pulumi.com/blog/how-to-test-infrastructure-as-code/"&gt;infrastructure testing&lt;/a&gt;
options available. Pulumi has mechanisms available for testing at each point
along this spectrum. Let&amp;rsquo;s dig in and see how.&lt;/p&gt;
&lt;h2 id="unit-testing"&gt;Unit Testing&lt;/h2&gt;
&lt;p&gt;Pulumi programs are authored in a general purpose language like
JavaScript, Python, TypeScript, or Go. The full power of each language
is available, including access to tools and libraries for that runtime,
including testing frameworks. Pulumi is also multi-cloud, meaning the
full underlying platform for each cloud is available too, and therefore
available for testing.&lt;/p&gt;
&lt;p&gt;(Despite being multi-language and multi-cloud, for the purposes of this
article let&amp;rsquo;s assume we are using JavaScript, with Mocha for testing,
and targeting AWS. Python &lt;code&gt;unittest&lt;/code&gt;, Go&amp;rsquo;s test framework, or any of
your favorite unit test frameworks in your language of choice would do,
and of course Pulumi works great with Azure, Google Cloud, and
Kubernetes, among others.)&lt;/p&gt;
&lt;p&gt;As we&amp;rsquo;ve seen, there are several reasons you may want to test your
infrastructure code. One of them is basic unit testing. Because your
code may have functions &amp;ndash; say, to compute CIDR blocks, dynamically
compute names and tags, etc. &amp;ndash; you probably want to test them. Doing
this is the same as writing ordinary unit tests against any program in
your language as choice.&lt;/p&gt;
&lt;p&gt;Going one step further, you may want to test the way your program
allocates resources. To illustrate, let&amp;rsquo;s imagine we want to create a
simple EC2-based webserver, but want to ensure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instances have a &lt;code&gt;Name&lt;/code&gt; tag.&lt;/li&gt;
&lt;li&gt;Instances must not use an inline &lt;code&gt;userData&lt;/code&gt; script &amp;ndash; we must use
an AMI (image).&lt;/li&gt;
&lt;li&gt;Instances must not have SSH open to the Internet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our running example is loosely based on
&lt;a href="https://github.com/pulumi/examples/tree/master/aws-js-webserver"&gt;our aws-js-webserver example&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;index.js:&lt;/strong&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="kd"&gt;let&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&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;group&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;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SecurityGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;web-secgrp&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;ingress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fromPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cidrBlocks&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;0.0.0.0/0&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fromPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cidrBlocks&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;0.0.0.0/0&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="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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;userData&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="sb"&gt;`#!/bin/bash echo &amp;#34;Hello, World!&amp;#34; &amp;gt; index.html nohup python -m SimpleHTTPServer 80 &amp;amp;`&lt;/span&gt;&lt;span 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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;server&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;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;web-server-www&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;instanceType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;t2.micro&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;securityGroups&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;group&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="c1"&gt;// reference the group object above
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="nx"&gt;ami&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ami-c55673a0&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;// AMI for us-east-2 (Ohio),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt; &lt;span class="c1"&gt;// start a simple web server
&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicIp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicIp&lt;/span&gt;&lt;span class="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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicHostName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicDns&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 is a basic Pulumi program: it simply allocates an EC2 security
group and instance. Notice, however, that we are violating all three of
the rules stated above &amp;ndash; let&amp;rsquo;s write some tests!&lt;/p&gt;
&lt;h3 id="writing-the-tests"&gt;Writing the Tests&lt;/h3&gt;
&lt;p&gt;The overall structure and scaffolding of our tests will look like any
ordinary Mocha testing:&lt;/p&gt;
&lt;h4 id="ec2testsjs"&gt;&lt;strong&gt;ec2tests.js&lt;/strong&gt;&lt;/h4&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;assert&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;assert&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mocha&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;mocha&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="kd"&gt;let&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;infra&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;./index&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Infrastructure&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;infra&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="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;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;#server&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&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;// TODO(check 1): Instances have a Name tag.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// TODO(check 2): Instances must not use an inline userData script.
&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;infra&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;#group&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&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;// TODO(check 3): Instances must not have SSH open to the Internet.
&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 let&amp;rsquo;s implement our first test: ensuring that instances have a
&lt;code&gt;Name&lt;/code&gt; tag. To verify this we simply need to grab hold of the EC2
instance object, and check the relevant &lt;code&gt;tags&lt;/code&gt; property:&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;// check 1: Instances have a Name tag.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;must have a name tag&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&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;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&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;urn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tags&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="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;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Missing a name tag on server &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;urn&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 class="k"&gt;else&lt;/span&gt; &lt;span class="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;done&lt;/span&gt;&lt;span class="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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This looks like a normal test, with a few noteworthy pieces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since we&amp;rsquo;re querying resource state before we&amp;rsquo;ve done a deployment,
our tests are always effectively running in &amp;ldquo;plan&amp;rdquo; (or &amp;ldquo;preview&amp;rdquo;)
mode. Thus, there are many properties whose values will simply not
resolve yet or will be undefined. This includes any output
properties computed by your cloud provider. That&amp;rsquo;s fine for these
tests &amp;ndash; we&amp;rsquo;re checking for valid inputs anyway. We&amp;rsquo;ll revisit this
later when it comes to integration testing.&lt;/li&gt;
&lt;li&gt;Because all Pulumi resource properties are &amp;ldquo;outputs&amp;rdquo; &amp;ndash; since many
of them are computed asynchronously &amp;ndash; we need to use the apply
method to get access to the values. This is a lot like promises and
the &lt;code&gt;then&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;Because we are using multiple properties, mostly just so we can show
the resource URN in the error message, we need to use the
&lt;code&gt;pulumi.all&lt;/code&gt; function to join all of them.&lt;/li&gt;
&lt;li&gt;Finally, since these outputs are resolved asynchronously, we need to
use Mocha&amp;rsquo;s built-in asynchronous test capability, with the &lt;code&gt;done&lt;/code&gt;
callback, or by returning a promise.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After we&amp;rsquo;ve gotten through that setup, we get access to the raw inputs
as plain JavaScript values. The &lt;code&gt;tags&lt;/code&gt; property is a map, so we just
make sure it is (1) not falsey, and (2) not missing an entry for the
&lt;code&gt;Name&lt;/code&gt; key in the map. This is very basic, but we can check anything!&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s write our second check. It&amp;rsquo;s even easier:&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;// check 2: Instances must not use an inline userData script.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;must not use userData (use an AMI instead)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&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;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userData&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;urn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userData&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;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Illegal use of userData on server &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;urn&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 class="k"&gt;else&lt;/span&gt; &lt;span class="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;done&lt;/span&gt;&lt;span class="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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally, let&amp;rsquo;s write our third check. It&amp;rsquo;s a bit more complex
because we&amp;rsquo;re searching for ingress rules associated with a security
group &amp;ndash; of which there may be many &amp;ndash; and CIDR blocks within those
ingress rules &amp;ndash; of which there may also be many. But it&amp;rsquo;s still easy:&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;// check 3: Instances must not have SSH open to the Internet.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;must not open port 22 (SSH) to the Internet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&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;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ingress&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;urn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ingress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromPort&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidrBlocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.0.0.0/0&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;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0) on group &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;urn&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 class="k"&gt;else&lt;/span&gt; &lt;span class="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;done&lt;/span&gt;&lt;span class="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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it &amp;ndash; now let&amp;rsquo;s actually run the tests!&lt;/p&gt;
&lt;h3 id="running-the-tests"&gt;Running the Tests&lt;/h3&gt;
&lt;p&gt;For the most part, we can run the tests in the usual way in your test
framework of choice. There is one thing specific to Pulumi we need to
cover first, however.&lt;/p&gt;
&lt;p&gt;Usually you run your Pulumi programs with the pulumi CLI. That
configures the language runtime, monitors execution from the engine so
that resource operations can be recorded and turned into a plan, and so
on. There is one problem, however: When running under the watchful eye
of your test framework, this connection to the CLI, and Pulumi&amp;rsquo;s engine,
will not exist.&lt;/p&gt;
&lt;p&gt;To work around this problem, we simply have to mock three pieces of
information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your project name, which can be supplied with the
&lt;code&gt;PULUMI_NODEJS_PROJECT&lt;/code&gt; environment variable (or more generally
&lt;code&gt;PULUMI_&amp;lt;LANGUAGE&amp;gt;_PROJECT&lt;/code&gt; in other languages).&lt;/li&gt;
&lt;li&gt;Your stack name, which can be supplied with the
&lt;code&gt;PULUMI_NODEJS_STACK&lt;/code&gt; environment variable (or more generally
&lt;code&gt;PULUMI_&amp;lt;LANGUAGE&amp;gt;_STACK&lt;/code&gt; in other languages).&lt;/li&gt;
&lt;li&gt;Your stack configuration variables. These can be supplied using the
&lt;code&gt;PULUMI_CONFIG&lt;/code&gt; environment variable, and its format is a JSON map
of configuration key/value pairs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The program will emit some warning messages indicating the connection to
the CLI/engine is not available at runtime. This is important to know,
because your program won&amp;rsquo;t actually be deploying anything &amp;ndash; which can
come as a surprise if that&amp;rsquo;s not what you meant to do! To tell Pulumi
that&amp;rsquo;s what you intended, you can set &lt;code&gt;PULUMI_TEST_MODE&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Imagine we want a project name of &lt;code&gt;my-ws&lt;/code&gt;, stack name of &lt;code&gt;dev&lt;/code&gt;, and AWS
region of &lt;code&gt;us-west-2&lt;/code&gt;. The command line to run your Mocha tests would
therefore be:&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="nv"&gt;PULUMI_TEST_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;PULUMI_NODEJS_STACK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;my-ws&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;PULUMI_NODEJS_PROJECT&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 class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;PULUMI_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{ &amp;#34;aws:region&amp;#34;: &amp;#34;us-west-2&amp;#34; }&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mocha tests.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this will tell us that we have three failing tests, as we had
hoped!&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; Infrastructure
    #server
1) must have a name tag      2) must not use userData (use an AMI instead)
    #group
3) must not open port 22 (SSH) to the Internet
  0 passing (17ms)
  3 failing
 1) Infrastructure
       #server
 must have a name tag:
Error: Missing a name tag on server
urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www
 2) Infrastructure
       #server
 must not use userData (use an AMI instead):
Error: Illegal use of userData on server
urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www
 3) Infrastructure
       #group
 must not open port 22 (SSH) to the Internet:
Error: Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0) on group  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s fix our program to comply:&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&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;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&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;group&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;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SecurityGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;web-secgrp&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;ingress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fromPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cidrBlocks&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;0.0.0.0/0&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="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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;server&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;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;web-server-www&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;tags&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;Name&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;web-server-www&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;instanceType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;t2.micro&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;securityGroups&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;group&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="c1"&gt;// reference the group object above
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="nx"&gt;ami&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ami-c55673a0&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;// AMI for us-east-2 (Ohio),
&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicIp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicIp&lt;/span&gt;&lt;span class="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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicHostName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicDns&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;And then rerun our tests:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; Infrastructure
    #server
      ✓ must have a name tag
      ✓ must not use userData (use an AMI instead)
    #group
      ✓ must not open port 22 (SSH) to the Internet
 3 passing (16ms)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;They all passed &amp;ndash; huzzah! &lt;strong&gt;✓✓✓&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="deployment-testing"&gt;Deployment Testing&lt;/h2&gt;
&lt;p&gt;This style of testing is powerful and lets us do white box testing of
the innards of how our infrastructure code works. This style of testing
is somewhat limited in what we can check, however. The tests run on an
in-memory representation of Pulumi&amp;rsquo;s deployment plan &amp;ndash; before actually
performing the deployment &amp;ndash; and so cannot test anything the deployment
itself. For that, Pulumi has an integration test framework. The two
approaches work great together!&lt;/p&gt;
&lt;p&gt;Pulumi&amp;rsquo;s integration test framework is written in Go and is how we test
much of our internal code. If the earlier unit test approach took more
of a white box testing approach, the integration test framework is very
much black box. (There are also options for thorough internals-oriented
testing.) This framework has been built to take a directory containing a
full Pulumi program and drive various lifecycle operations against it
&amp;ndash; such as deploying a new stack from scratch, updating it with
variations, and tearing it down afterwards, potentially multiple times.
We run these in checkin suites, on a recurring basis (such as nightly),
and as stress tests.&lt;/p&gt;
&lt;p&gt;(We have &lt;a href="https://github.com/pulumi/pulumi/issues/2287"&gt;an open work item&lt;/a&gt; which will bring
similar integration testing capabilities to each native language SDK.
You can use the Go integration test framework no matter the language
your Pulumi program is written in, however.)&lt;/p&gt;
&lt;p&gt;By running a program through this integration test framework, you can
ensure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your project&amp;rsquo;s code is syntactically well formed and runs without
errors.&lt;/li&gt;
&lt;li&gt;Your stack&amp;rsquo;s configuration and secrets settings work and are
interpreted correctly.&lt;/li&gt;
&lt;li&gt;Your project can be successfully deployed to your cloud provider of
choice.&lt;/li&gt;
&lt;li&gt;Your project can be successfully updated from its starting state to
N other states.&lt;/li&gt;
&lt;li&gt;Your project can be successfully destroyed and removed from your
cloud provider.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As we will see soon, you can also leverage this framework to perform
runtime validation.&lt;/p&gt;
&lt;h3 id="a-basic-integration-test"&gt;A Basic Integration Test&lt;/h3&gt;
&lt;p&gt;To see this in action, we&amp;rsquo;ll look from the pulumi/examples repo, since
our team and community here at Pulumi use that to validate our own
examples in pull requests, checkins, and nightlies.&lt;/p&gt;
&lt;p&gt;The following is a simplified test of our example that provisions an
&lt;a href="https://github.com/pulumi/examples/tree/master/aws-js-s3-folder"&gt;S3 bucket and some objects in Pulumi&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;example_test.go:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;testing&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;github.com/pulumi/pulumi/pkg/testing/integration&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;TestExamples&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;awsRegion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;AWS_REGION&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;awsRegion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;awsRegion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;us-west-1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Getwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProgramTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProgramTestOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Quick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SkipRefresh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;..&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;..&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;aws-js-s3-folder&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;aws:region&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;awsRegion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This test runs through a basic lifecycle of stack creation, updating,
and destruction, for the &lt;code&gt;aws-js-s3-folder&lt;/code&gt; example. It takes about a
minute or so to report success:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ go test .
PASS
ok ... 43.993s
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are many, many options to control the behavior of these tests. For
a full set of options, see &lt;a href="https://godoc.org/github.com/pulumi/pulumi/pkg/testing/integration#ProgramTestOptions"&gt;the &lt;code&gt;ProgramTestOptions&lt;/code&gt; data
structure&lt;/a&gt;.
For instance, you can configure a Jaeger endpoint for tracing
(&lt;code&gt;Tracing&lt;/code&gt;), tell the harness to expect failure for negative testing
(&lt;code&gt;ExpectFailure&lt;/code&gt;), apply a series of &amp;ldquo;edits&amp;rdquo; to the program for a
sequence of update state transitions (&lt;code&gt;EditDirs&lt;/code&gt;), and more. Let&amp;rsquo;s see
how to use these to do validate that we deployed what we expected.&lt;/p&gt;
&lt;h3 id="validating-the-shape-of-resources"&gt;Validating the Shape of Resources&lt;/h3&gt;
&lt;p&gt;All of the above integration ensures that our program &amp;ldquo;works&amp;rdquo; &amp;ndash; as in,
it does not crash. But what if we want to validate properties of the
resulting stack? For example, that certain kinds of resources did (or
did not) get provisioned, and that they have certain attributes.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ExtraRuntimeValidation&lt;/code&gt; option for &lt;code&gt;ProgramTestOptions&lt;/code&gt; allows us
to look at the post-deployment state that Pulumi has captured, so that
we can do extra validation on it. This includes a full snapshot of the
resulting stack&amp;rsquo;s state, including configuration, exported outputs, all
resources and their property values, and all inter-resource
dependencies.&lt;/p&gt;
&lt;p&gt;To see a basic example of this, let&amp;rsquo;s check that our program creates a
single S3 Bucket:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProgramTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProgramTestOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// as before...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ExtraRuntimeValidation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RuntimeValidationStackInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;foundBuckets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&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;Deployment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Resources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;aws:s3/bucket:Bucket&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;foundBuckets&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;foundBuckets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Expected to find a single AWS S3 Bucket&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now when we run &lt;code&gt;go test&lt;/code&gt;, it will not only run through the battery of
lifecycle tests but will also, after successfully standing up the stack,
perform the extra validation against the resulting state.&lt;/p&gt;
&lt;h2 id="runtime-testing"&gt;Runtime Testing&lt;/h2&gt;
&lt;p&gt;All of the testing thus far has been exclusively about deployment
behavior and the Pulumi resource model. What if you want to test that
your provisioned infrastructure is actually working, however? For
example, that our VM is alive, our S3 bucket contains what we expect,
and so on.&lt;/p&gt;
&lt;p&gt;You may have already guessed how to accomplish some of this: the
&lt;code&gt;ExtraRuntimeValidation&lt;/code&gt; option for &lt;code&gt;ProgramTestOptions&lt;/code&gt; is the perfect
opportunity to do this. At this point, you are running an arbitrary Go
test, with access to the full state of your program&amp;rsquo;s resources. This
state includes information such as VM IP addresses, URLs, and everything
you need to actually interact with your resulting cloud applications and
infrastructure to perform verification.&lt;/p&gt;
&lt;p&gt;For instance, our test program above exports the bucket&amp;rsquo;s
&lt;code&gt;websiteEndpoint&lt;/code&gt; property, as the stack output named &lt;code&gt;websiteUrl&lt;/code&gt;,
which is a full URL that we can use to fetch the bucket&amp;rsquo;s configured
index document. Although we could just as well have dug through the
state file to find the bucket and read this property directly, many
times our stacks will export useful properties like this that we can use
conveniently for verification:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProgramTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProgramTestOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;// as before ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ExtraRuntimeValidation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RuntimeValidationStackInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;http://&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Outputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;websiteUrl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;].(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ioutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello, Pulumi!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Like our previous runtime validation checks, this will run in the
harness right after the stack is stood up, all in response to a simple
&lt;code&gt;go test&lt;/code&gt; invocation. This just scratches the surface of what&amp;rsquo;s possible
&amp;ndash; anything you can write in code using Go&amp;rsquo;s test capabilities is
within reach!&lt;/p&gt;
&lt;h2 id="continuous-infrastructure-integration"&gt;Continuous Infrastructure Integration&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s wonderful to be able to easily run these on demand, on a laptop,
when you&amp;rsquo;re iterating on infrastructure changes and want to verify them
before submitting for code review. But we and many of our customers run
infrastructure testing at various points in the development lifecycle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In each pull request opened, to validate before allowing it to be
merged.&lt;/li&gt;
&lt;li&gt;In response to each commit, to double check it was merged correctly.&lt;/li&gt;
&lt;li&gt;On a recurring basis, such as nighty or even weekly for extended
testing.&lt;/li&gt;
&lt;li&gt;As part of more exotic and thorough test programs, such as
performance or stress testing, which typically run over a longer
period of time, execute many things in parallel, and/or deploy the
same program multiple times.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For each of these, Pulumi supports integration with your favorite
continuous integration system. This gives you similar continuous
integration testing coverage for your infrastructure as you might
already enjoy for your application software or in other programming
domains.&lt;/p&gt;
&lt;p&gt;Pulumi supports your existing CI systems. Here are a few of those
supported:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/aws-code-services/"&gt;AWS Code Services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/azure-devops/"&gt;Azure DevOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/circleci/"&gt;CircleCI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/github-actions/"&gt;GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/gitlab-ci/"&gt;GitLab CI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/google-cloud-build/"&gt;Google Cloud Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/travis/"&gt;Travis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please refer to the
&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/"&gt;Continuous Delivery documentation&lt;/a&gt; for a more
comprehensive guide.&lt;/p&gt;
&lt;h2 id="ephemeral-environments"&gt;Ephemeral Environments&lt;/h2&gt;
&lt;p&gt;A very powerful capability this unlocks is the ability to spin up
ephemeral environments solely for purposes of acceptance testing.
Pulumi&amp;rsquo;s concept of &lt;a href="https://www.pulumi.com/docs/using-pulumi/organizing-projects-stacks/"&gt;projects and stacks&lt;/a&gt; is
designed to make it very easy to stand up entirely isolated and
independent environments, and to tear them down, all in either a few
easy CLI gestures, or by using the integration testing framework.&lt;/p&gt;
&lt;p&gt;If you are using GitHub, Pulumi offers a
&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/github-app/"&gt;GitHub App&lt;/a&gt; that helps to glue
together your Pull Request workflow with this sort of acceptance testing
run inside of your CI pipelines. Simply install the App into your GitHub
repos, and Pulumi in your CI, and your Pull Requests will light up with
infrastructure previews, updates, and test results seamlessly:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/testing-your-infrastructure-as-code-with-pulumi/pr-comment.png" alt="pr-comment"&gt;&lt;/p&gt;
&lt;p&gt;By leveraging Pulumi for your core acceptance test workflow, you&amp;rsquo;ll
unlock new automation capabilities that improve your team&amp;rsquo;s productivity
and confidence in the quality of changes.&lt;/p&gt;
&lt;h2 id="in-summary"&gt;In Summary&lt;/h2&gt;
&lt;p&gt;In this post, we saw that by using general purpose programming
languages, we unlock many software engineering practices we&amp;rsquo;ve benefited
from in our application development. This includes unit testing,
integration testing, and even the intersection of both to do extensive
runtime testing. It&amp;rsquo;s easy to run these on demand and harness them in
your CI system of choice.&lt;/p&gt;
&lt;p&gt;Pulumi is open source, free to use, and works with your favorite
languages and clouds &amp;ndash; &lt;a href="https://www.pulumi.com/docs/"&gt;give it a try today&lt;/a&gt;!&lt;/p&gt;</description><author>Joe Duffy</author><category>javascript</category><category>typescript</category><category>continuous-delivery</category><category>python</category></item><item><title>Easy Serverless Apps and Infrastructure</title><link>https://www.pulumi.com/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/</link><pubDate>Mon, 11 Mar 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/index.png" /&gt;
&lt;p&gt;With Pulumi, you can create, deploy, and manage any cloud resource using
your favorite language. This includes application &lt;strong&gt;and&lt;/strong&gt; infrastructure
related resources, often in the same program.&lt;/p&gt;
&lt;p&gt;One area this gets really fun is serverless computing. Because we&amp;rsquo;re using
general purpose languages, we can create resources, and then wire up
event handlers, just like normal event-driven programming. This is the
way serverless architecture should be!&lt;/p&gt;
&lt;p&gt;In this article, we&amp;rsquo;ll see how. There&amp;rsquo;s a broad range of options
depending on what you want to do, and how your team likes to operate.
We&amp;rsquo;ll be using Amazon Web Services (AWS) and TypeScript, but other clouds and languages are
available.&lt;/p&gt;
&lt;h2 id="a-simple-serverless-app"&gt;A Simple Serverless App&lt;/h2&gt;
&lt;p&gt;In this article, we&amp;rsquo;ll use a very simple serverless app. We have two S3
buckets: one for TPS reports, and the other to store archived zipfiles
of those reports. Anytime a new report comes in, our Lambda will zip and
archive it.&lt;/p&gt;
&lt;p&gt;Simple enough. This can be visualized as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/architecture.png" alt="Serverless app architecture"&gt;&lt;/p&gt;
&lt;p&gt;All source code for this example is
&lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-s3-lambda-copyzip"&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="event-sources-and-event-handlers"&gt;Event Sources and Event Handlers&lt;/h2&gt;
&lt;p&gt;Serverless app models today make you think of the event sources &amp;ndash; the
S3 buckets &amp;ndash; and event handlers &amp;ndash; the Lambdas and associated code
&amp;ndash; as very different things &amp;ndash; &amp;ldquo;infrastructure&amp;rdquo; versus &amp;ldquo;app code&amp;rdquo;
&amp;ndash; managed with distinct tools and workflows. Pulumi, in contrast,
gives you a single CLI, &lt;code&gt;pulumi&lt;/code&gt;, to manage everything consistently.&lt;/p&gt;
&lt;p&gt;You can create event sources and handlers in the same program, but
Pulumi&amp;rsquo;s approach is flexible, allowing you to ease into serverless
without boiling the ocean. You can choose to wire functions up to
existing resources (say, if your team already created the buckets), or
even use functions your team has already made.&lt;/p&gt;
&lt;p&gt;Also, because Pulumi provisions and manages resources, updating your
functions after creating them is easy. Just edit your code,
run &lt;code&gt;pulumi up&lt;/code&gt;, and Pulumi will diff and compute the minimal set of
changes it can make to upgrade your code &amp;ndash; no downtime required.
We&amp;rsquo;ll see this in action shortly. But first let&amp;rsquo;s create our initial
app.&lt;/p&gt;
&lt;h2 id="creating-an-event-handler"&gt;Creating an Event Handler&lt;/h2&gt;
&lt;p&gt;There are multiple approaches to creating a function. Let&amp;rsquo;s start with
the easiest.&lt;/p&gt;
&lt;h3 id="approach1-magic-functions"&gt;Approach 1: Magic Functions&lt;/h3&gt;
&lt;p&gt;Our favorite way to create functions is to write them inline. This blurs
the line between infrastructure and application logic in a way that
makes Pulumi programs feel like real, first class distributed apps.&lt;/p&gt;
&lt;p&gt;First, we have our TPS reports and zips buckets (for now
we&amp;rsquo;ll &lt;code&gt;new&lt;/code&gt; them up, more on that later):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="kr"&gt;from&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;tpsReports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tpsReports&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;tpsZips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tpsZips&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;The bucket objects have many properties, including obvious ones like the
ARN, domain name, CORS rules, and so on. But if we look closely, we&amp;rsquo;ll
see there are some &lt;code&gt;onX&lt;/code&gt; methods. These register event handlers:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/intellisense.png" alt="Intellisense"&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s use &lt;code&gt;onObjectCreated&lt;/code&gt; to create a Lambda that will zip up any new
reports:&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;tpsReports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReports&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AdmZip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;adm-zip&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;s3&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;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&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="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rec&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Records&lt;/span&gt; &lt;span class="o"&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zip&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;AdmZip&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;      &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;buck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Zipping &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;buck&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;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; into &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tpsZips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;.zip`&lt;/span&gt;&lt;span class="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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getObject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt;: &lt;span class="kt"&gt;buck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Key&lt;/span&gt;: &lt;span class="kt"&gt;key&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="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;zip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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;Body&lt;/span&gt;&lt;span class="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;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putObject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;         &lt;span class="nx"&gt;Bucket&lt;/span&gt;: &lt;span class="kt"&gt;tpsZips.bucket.get&lt;/span&gt;&lt;span class="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;Key&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;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;.zip`&lt;/span&gt;&lt;span class="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="kt"&gt;zip.toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;      &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Most of the code here is the application logic &amp;ndash; precisely what we
want to be focusing on! In fact, this code looks just like any ordinary
JavaScript event-driven program.&lt;/p&gt;
&lt;p&gt;A neat thing that Pulumi can do is capture references to other
resources. So, notice here, we actually capture a reference
to &lt;code&gt;tpsZips&lt;/code&gt; from within the event handler, and Pulumi figures out how
to serialize the resulting closure. For the most part, any code you
write there will work, unlocking all sorts of powerful composition
scenarios: multiple functions can be composed together, you can use
modules as usual, and so on. Just program like you normally would,
without the awkward environment variables and configuration. That&amp;rsquo;s why
we call these &lt;em&gt;magic&lt;/em&gt; functions.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s deploy the resources to AWS:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/up.png" alt="UP UP AND AWAY!"&gt;&lt;/p&gt;
&lt;p&gt;After confirming, &lt;code&gt;yes&lt;/code&gt;, everything is up and running after just a few
seconds:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/done.png" alt="done"&gt;&lt;/p&gt;
&lt;p&gt;Note the resource graph. Pulumi uses familiar languages, which allows
encapsulation and hiding of unnecessary complexity. So underneath that
simple-looking &lt;code&gt;onObjectCreated&lt;/code&gt; are many AWS resources, like IAM
attachments. This is a lot like how we can ignore all the messy OS API
calls when using Node.js abstractions.&lt;/p&gt;
&lt;p&gt;Pulumi has many convenience functions built-in, and one of them
is &lt;code&gt;pulumi logs&lt;/code&gt;, which will let us tail the logs associated with all
compute in our stack (including serverless functions):&lt;/p&gt;
&lt;p&gt;$ pulumi logs -f
Collecting logs for stack dev since 2019-03-10T10:09:56.000-07:00&amp;hellip;&lt;/p&gt;
&lt;p&gt;Now that we&amp;rsquo;re tailing the logs, let&amp;rsquo;s copy a file over using the AWS
CLI:&lt;/p&gt;
&lt;p&gt;$ aws s3 cp ./tps001.txt s3://$(pulumi stack output tpsReportsBucket)
upload: ../tps001.txt to s3://tpsreports-96458ef/tps001.txt&lt;/p&gt;
&lt;p&gt;We have used the &lt;code&gt;pulumi stack output&lt;/code&gt; command to conveniently fetch the
bucket name. After the upload completes, we&amp;rsquo;ll see that our function
comes alive in the &lt;code&gt;pulumi logs&lt;/code&gt; command:&lt;/p&gt;
&lt;p&gt;2019-03-10T11:10:48.617-07:00[zipTpsReports] Zipping
    tpsreports-96458ef/tps001.txt into tpszips-edfde11/tps001.txt.zip&lt;/p&gt;
&lt;p&gt;And, sure enough, if we list the contents of the zip bucket, we will see
our newly added file:&lt;/p&gt;
&lt;p&gt;$ aws s3 ls s3://$(pulumi stack output tpsZipsBucket)
2019-03-10 11:10:50 6206 tps001.txt.zip&lt;/p&gt;
&lt;p&gt;Voila! A fully functioning serverless application.&lt;/p&gt;
&lt;p&gt;Note also that the &lt;code&gt;CallbackFunction&lt;/code&gt; class &amp;ndash; the powerful abstraction
behind all of this &amp;ndash; is exported, and offers some knobs,
in case you want to do things like reuse existing IAM roles rather than
creating new ones.
See the &lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/lambda"&gt;&lt;code&gt;pulumi/aws/lambda&lt;/code&gt; documentation&lt;/a&gt; for details.
For instance, say we want to increase the RAM available to our function from 128MB to 256MB:&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;tpsReports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="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;zipTpsReports&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;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;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CallbackFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReportsFunc&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;memorySize&lt;/span&gt;: &lt;span class="kt"&gt;256&lt;/span&gt; &lt;span class="cm"&gt;/*MB*/&lt;/span&gt;&lt;span class="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;callback&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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 class="cm"&gt;/* same code as before */&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;   &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Soon we&amp;rsquo;ll see how to manage and update our functions, but first let&amp;rsquo;s
see some alternative coding styles.&lt;/p&gt;
&lt;h3 id="approach2-manual-function-resources"&gt;Approach 2: Manual Function Resources&lt;/h3&gt;
&lt;p&gt;We just saw that we can write our serverless application logic inside of our Pulumi
program, alongside our infrastructure resource definitions. This can
feel magical and super productive. But not everybody wants to mix these
together. Sometimes you want to stick to the classic way of doing this:
keeping your function&amp;rsquo;s application logic in one place, and your
function&amp;rsquo;s infrastructure definition in another. This aligns more
closely with many team workflows.&lt;/p&gt;
&lt;p&gt;There is another practical reason to want this: if your functions are
written in one language (say, C# or Java) while your Pulumi
infrastructure is authored in a different one (say, JavaScript or
Python).&lt;/p&gt;
&lt;p&gt;Pulumi supports this, by simply defining your Lambda function like any
other infrastructure resource:&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;// First, create some IAM machinery:
&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;zipFuncRole&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;zipTpsReportsFuncRole&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="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="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 class="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;lambda.amazonaws.com&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="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;Sid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;      &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;   &lt;span class="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;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;RolePolicyAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReportsFuncRoleAttach&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;role&lt;/span&gt;: &lt;span class="kt"&gt;zipFuncRole&lt;/span&gt;&lt;span class="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;policyArn&lt;/span&gt;: &lt;span class="kt"&gt;aws.iam.ManagedPolicy.AWSLambdaExecute&lt;/span&gt;&lt;span class="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;// Next, create the Lambda function itself:
&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;zipFunc&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;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReportsFunc&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;environment&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;variables&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="s2"&gt;&amp;#34;TPS_ZIP_BUCKET&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tpsZips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&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;code&lt;/span&gt;: &lt;span class="kt"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AssetArchive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;      &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FileArchive&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&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;runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nodejs12.x&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;role&lt;/span&gt;: &lt;span class="kt"&gt;zipFuncRole.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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Finally, register the Lambda to fire when a new TPS report arrives:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tpsReports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReports&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zipFunc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because we&amp;rsquo;re taking matters into our own hands, we have to create the
IAM machinery ourselves. We also need a way to communicate the zip
bucket name, so we use an environment variable. This highlights both the
benefits and drawbacks to programming at this level &amp;ndash; we need to know
how to configure all of these ancillary resources, but as a result,
the
&lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/lambda/function/"&gt;entire power of Lambda is at our fingertips&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Notice that we&amp;rsquo;ve pointed to our application logic inside of &lt;code&gt;./app&lt;/code&gt;.
Pulumi will create the zipfile for you. If we instead wanted to use a
zipfile we&amp;rsquo;ve already packaged, just change &lt;code&gt;code&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&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;code&lt;/span&gt;: &lt;span class="kt"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FileArchive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./app.zip&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="c1"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using Pulumi&amp;rsquo;s
&lt;a href="https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/pulumi/asset"&gt;&lt;code&gt;Asset&lt;/code&gt; and &lt;code&gt;Archive&lt;/code&gt; classes&lt;/a&gt;,
we can fetch code from anywhere &amp;ndash; even the network.&lt;/p&gt;
&lt;p&gt;Although managing your functions manually isn&amp;rsquo;t quite as magical, it is
practically useful, and still delivers all the usual infrastructure as
code benefits, enabling robust infrastructure management and versioning.&lt;/p&gt;
&lt;h3 id="approach3wire-up-an-existing-function"&gt;Approach 3: Wire Up an Existing Function&lt;/h3&gt;
&lt;p&gt;This post is mainly about using serverless functions with Pulumi. But
what if you&amp;rsquo;re using Pulumi mainly for infrastructure, and another
solution for the functions themselves? No problem &amp;ndash; Pulumi can still
glue everything together, even if it&amp;rsquo;s not provisioning all of the
resources.&lt;/p&gt;
&lt;p&gt;Every resource type in Pulumi has a static &lt;code&gt;get&lt;/code&gt; that looks up an
existing resource. These resources aren&amp;rsquo;t managed by Pulumi, but you
can still access their properties and use them. In this case, we&amp;rsquo;ll
just look up an existing Lambda, &lt;code&gt;zipTpsReportsFunc&lt;/code&gt;, and register it as
an event handler:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zipFunc&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;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReportsFunc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;zipTpsReports-19d51dc&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;tpsReports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReports&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zipFunc&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;We&amp;rsquo;ve given the function&amp;rsquo;s ID, &lt;code&gt;zipTpsReports-19d51dc&lt;/code&gt;, which allows
Pulumi to locate it in your account and reuse it. This can make it easy
to incrementally adopt Pulumi one piece at a time, collaborate between
teams, or stitch together resources
&lt;a href="https://www.pulumi.com/docs/using-pulumi/organizing-projects-stacks/"&gt;managed by different stacks&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="more-about-functions"&gt;More About Functions&lt;/h2&gt;
&lt;p&gt;Now that we&amp;rsquo;ve seen the basics, let&amp;rsquo;s look at a few associated
function management topics.&lt;/p&gt;
&lt;h3 id="updating-a-function" class="code-line" line="246"&gt;Updating a Function&lt;/h3&gt;
&lt;p&gt;Updating the function is as easy as can be. Imagine we&amp;rsquo;re running into
a problem and want to do some good old fashioned &lt;code&gt;printf&lt;/code&gt; debugging
inside of our lambda. Maybe we want to log the size of the report and
its resulting zipfile. Just add the relevant line:&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 class="nx"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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;Body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Report: &lt;/span&gt;&lt;span class="si"&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;ContentLength&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;, Zip: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&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="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putObject&lt;/span&gt;&lt;span class="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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And run &lt;code&gt;pulumi up&lt;/code&gt; &amp;ndash; it will show us what changed:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/update.png" alt="pulumi up"&gt;&lt;/p&gt;
&lt;p&gt;We can view the full diff, by choosing &lt;code&gt;details&lt;/code&gt;, including a Git-like
diff of the code changes themselves!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/easy-serverless-apps-and-infrastructure-real-events-real-code/diffs.png" alt="Diffs"&gt;&lt;/p&gt;
&lt;p&gt;After selecting &lt;code&gt;yes&lt;/code&gt;, the function is updated in just a couple of
seconds. Similarly, we can update any of the resource definitions, and
Pulumi will figure out how to incrementally update them.&lt;/p&gt;
&lt;h2 id="splitting-up-your-codebase"&gt;Splitting Up your Codebase&lt;/h2&gt;
&lt;p&gt;A nice middle ground between magic and manual functions is to use your
language&amp;rsquo;s module system to structure code differently. This is similar
to how you might structure a typical application: route definitions over
here, business logic over there, markup over here, etc. Pulumi can
figure out the diffs regardless of how you&amp;rsquo;ve structured your code, so
updates are always based only on what&amp;rsquo;s changed.&lt;/p&gt;
&lt;p&gt;For example, maybe we&amp;rsquo;ve defined our callback function in &lt;code&gt;./app&lt;/code&gt;:&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;export&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;zipReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;: &lt;span class="kt"&gt;aws.s3.BucketEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;   &lt;span class="c1"&gt;// app code, as shown above, goes here
&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 now we can go back to our infrastructure code, and eliminate the
application logic entirely:&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;zipReport&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;./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="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;tpsReports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReports&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zipReport&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;We can take this further and use dynamic package management to split up
the code, possibly even spreading pieces of infrastructure and
application code across multiple repos and/or packages. This works well
for larger teams with independent components versioning at their own
pace.&lt;/p&gt;
&lt;p&gt;Lastly, it&amp;rsquo;s possible to use Pulumi stacks to actually break apart your
cloud resources and functions into independently deployable pieces. This
allows teams to leverage features
like &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/access-management/teams/"&gt;RBAC&lt;/a&gt;.
For instance, it&amp;rsquo;s common for the DevOps team to manage the physical
cloud resources like queues, topics, and buckets, while the development
team authors and manages the serverless functions attached to them. Read
more about
this in the &lt;a href="https://www.pulumi.com/docs/using-pulumi/organizing-projects-stacks/"&gt;organizaing projects &amp;amp; stacks documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="more-about-event-sources"&gt;More About Event Sources&lt;/h2&gt;
&lt;p&gt;We glossed over one of the more interesting points: what resources have
event handlers associated with them, and how to get our hands on them.&lt;/p&gt;
&lt;h2 id="provision-a-new-resource"&gt;Provision a New Resource&lt;/h2&gt;
&lt;p&gt;The simplest answer here is to create a new resource in your Pulumi
program using &lt;code&gt;new&lt;/code&gt;, as we saw above. Because Pulumi is an
infrastructure as code platform, any resources in any cloud are
available &amp;ndash;
&lt;a href="https://www.pulumi.com/docs/iac/get-started/aws/"&gt;AWS&lt;/a&gt;,
&lt;a href="https://www.pulumi.com/docs/iac/get-started/azure/"&gt;Azure&lt;/a&gt;,
&lt;a href="https://www.pulumi.com/docs/iac/get-started/gcp/"&gt;GCP&lt;/a&gt;,
&lt;a href="https://www.pulumi.com/docs/iac/get-started/kubernetes/"&gt;Kubernetes&lt;/a&gt;, etc. When
you &lt;code&gt;new&lt;/code&gt; one up, Pulumi understands how to provision and manage it.&lt;/p&gt;
&lt;p&gt;We saw simple examples of this earlier:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tpsReports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tpsReports&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;Note that the full set of configuration options are available on each
resource. Let&amp;rsquo;s say we want to turn on server-side encryption of our
TPS archive bucket:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tpsReports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tpsReports&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;serverSideEncryptionConfiguration&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;sseAlgorithm&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;AES256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;   &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Just as with functions, subsequent updates will be diffed and updated in
the minimally impactful way.&lt;/p&gt;
&lt;h2 id="connect-to-an-existing-resource"&gt;Connect to an Existing Resource&lt;/h2&gt;
&lt;p&gt;Although it&amp;rsquo;s nice we can provision the event sources and targets in a
single program, sometimes different members of the team manage different
parts of the infrastructure. For example, maybe your DevOps engineers
provision resources like buckets, topics, and so on, and your developers
wire up the functions.&lt;/p&gt;
&lt;p&gt;Not to worry, you can import an existing resource and program against it
in the same way:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tpsReports&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;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tpsReports&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;arn:aws:s3:::tpsReports-4f64efc&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;This time, when you run your Pulumi program, you&amp;rsquo;ll still see this
resource but notice it says &lt;code&gt;read&lt;/code&gt; instead of &lt;code&gt;create&lt;/code&gt; or &lt;code&gt;update&lt;/code&gt;. This
just means that the resource is read from your account as-is. No matter
what you do, the bucket itself will not be modified by Pulumi, other
than to subscribe an event.&lt;/p&gt;
&lt;p&gt;This, of course, can be combined with the earlier similar functionality
for functions, to glue together a bucket and a Lambda, where neither was
actually provisioned by Pulumi!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tpsReports&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;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tpsReports&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;arn:aws:s3:::tpsReports-4f64efc&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;zipFunc&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;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReportsFunc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;zipTpsReports-19d51dc&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;tpsReports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zipTpsReports&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zipFunc&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;h2 id="other-notable-event-sources"&gt;Other Notable Event Sources&lt;/h2&gt;
&lt;p&gt;Finally, it&amp;rsquo;s worth noting there are many event handler functions
exposed in the AWS package:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-apigateway"&gt;&lt;code&gt;apigateway.x.API&lt;/code&gt;&lt;/a&gt;:
create serverless APIs using an Express.js style&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cloudwatch.onSchedule&lt;/code&gt;:
fire a CloudWatch event on a particular schedule, e.g. a cron
expression&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cloudwatch.Event.onEvent&lt;/code&gt;:
fire an event when a particular CloudWatch event occurs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cloudwatch.LogGroup.onEvent&lt;/code&gt;:
fire an event when a CloudWatch logs event occurs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb.Table.onEvent&lt;/code&gt;:
fire events for DynamoDB insert, modify, or remove operations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kinesis.Stream.onEvent&lt;/code&gt;:
fire Kinesis Stream events at particular times or batch sizes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3.Bucket.onObjectCreated&lt;/code&gt;:
trigger a function anytime an object is created in an S3 Bucket&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3.Bucket.onObjectRemoved&lt;/code&gt;:
trigger a function anytime an object is removed from an S3 Bucket&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3.Bucket.onEvent&lt;/code&gt;:
trigger a function for a wide range of S3 Bucket events&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sns.Topic.onEvent&lt;/code&gt;:
fire SNS Topic events when new messages arrive&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqs.Queue.onEvent&lt;/code&gt;:
fire SQS Queue events when new messages are enqueued (or on DLQ
events, etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these handlers can be programmed using the spectrum of techniques
outlined above.&lt;/p&gt;
&lt;h2 id="in-conclusion"&gt;In Conclusion&lt;/h2&gt;
&lt;p&gt;In this post, we saw many different options for serverless programming
with Pulumi, from magic functions that make serverless apps feel like true
event-driven programming, to provisioning and managing infrastructure as
code.&lt;/p&gt;
&lt;p&gt;We also saw that all of these options can be combined in any way,
depending on you and your team&amp;rsquo;s needs. This is the magic of
programming languages in action!&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to try it out,
&lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-s3-lambda-copyzip"&gt;check out the code for this post on GitHub&lt;/a&gt;.
Happy serverless hacking!&lt;/p&gt;</description><author>Joe Duffy</author><category>javascript</category><category>serverless</category><category>aws</category></item><item><title>Serverless on AWS with Pulumi: Simple, Event-based Functions</title><link>https://www.pulumi.com/blog/serverless-on-aws-with-pulumi-simple-event-based-functions/</link><pubDate>Mon, 14 Jan 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/serverless-on-aws-with-pulumi-simple-event-based-functions/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/serverless-on-aws-with-pulumi-simple-event-based-functions/index.png" /&gt;
&lt;p&gt;One of Pulumi&amp;rsquo;s goals is to provide the simplest way possible to do
serverless programming on AWS by enabling you to create cloud
infrastructure with familiar programming languages that you are already
using today. We believe that the existing constructs already present in
these languages, like flow control, inheritance, composition, and so on,
provide the right abstractions to effectively build up infrastructure in
a simple and familiar way.&lt;/p&gt;
&lt;p&gt;In a &lt;a href="https://www.pulumi.com/blog/lambdas-as-lambdas-the-magic-of-simple-serverless-functions/"&gt;previous post&lt;/a&gt;
we focused on how Pulumi could allow you to simply create an AWS Lambda
out of your own JavaScript function. While this was much easier than
having to manually create a &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html"&gt;Lambda Deployment Package&lt;/a&gt;
yourself, it could still be overly complex to integrate these Lambdas
into complete serverless application.&lt;/p&gt;
&lt;p&gt;To get a sense of that complexity,
let&amp;rsquo;s look at how one would normally have to work with AWS&amp;rsquo;s resource
system to create a simple Serverless application:&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;// Simplified for brevity
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="kr"&gt;from&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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;slack&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@slack/client&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 a simple bucket.
&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;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;testbucket&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;serverSideEncryptionConfiguration&lt;/span&gt;: &lt;span class="kt"&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;forceDestroy&lt;/span&gt;: &lt;span class="kt"&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;// Create a lambda that will post a message to slack when the bucket changes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// We can pass a simple JavaScript/TypeScript lambda here thanks to the magic of &amp;#34;Lambdas as Lambdas&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// See: www.pulumi.com/blog/lambdas-as-lambdas-the-magic-of-simple-serverless-functions/
&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;lambda&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;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CallbackFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;postToSlack&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;callback&lt;/span&gt;: &lt;span class="kt"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&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;slack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WebClient&lt;/span&gt;&lt;span class="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="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rec&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Records&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;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&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="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="c1"&gt;// Give the bucket permission to invoke the lambda.
&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;permission&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;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Permission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;invokelambda&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="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="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;lambda:InvokeFunction&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="s2"&gt;&amp;#34;s3.amazonaws.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sourceArn&lt;/span&gt;: &lt;span class="kt"&gt;bucket.id.apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucketName&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sb"&gt;`arn:aws:s3:::&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bucketName&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Now hookup a notification that will trigger the lambda when any object is created in the bucket.
&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;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BucketNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;onAnyObjectCreated&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;: &lt;span class="kt"&gt;bucket.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;lambdaFunctions&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;events&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;s3:ObjectCreated:*&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;lambdaFunctionArn&lt;/span&gt;: &lt;span class="kt"&gt;lambda.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="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Phew&amp;hellip; that&amp;rsquo;s a lot of code &lt;code&gt;:-/&lt;/code&gt; So what happened above? Well, in the
AWS resource-oriented view of the world, most things are nouns (i.e
objects). Your bucket is an object. The lambda is an object. The
permission is an explicit object. And even the connection between the
bucket and lambda (the &lt;code&gt;BucketNotification&lt;/code&gt;) is itself an object. That&amp;rsquo;s
one way to decompose the world, and it makes sense in some domains where
everything is just &amp;lsquo;data&amp;rsquo;. But that isn&amp;rsquo;t a natural fit for a
programming language, and it just doesn&amp;rsquo;t feel very idiomatic or
appropriate for how people would expect things to work in their
programming language of choice. Programming languages have lots of other
facilities at their disposal to work with data. You can have
methods/functions/messages you can send to an object. You can have
events and callbacks on objects. You can encapsulate objects behind
other objects. You can &amp;lsquo;run code&amp;rsquo; to do things based on those objects,
etc. etc.&lt;/p&gt;
&lt;p&gt;Now, while data should be the start of how you interact with system, it
shouldn&amp;rsquo;t be the end. Toward that goal, Pulumi makes it possible to
simply compose and connect these resources in a more natural fashion. By
just adding our own components, and leveraging what programming languages
can already do, we can make it possible to simplify the above down to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="kr"&gt;from&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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;slack&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@slack/client&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 a simple bucket.
&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;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;testbucket&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;serverSideEncryptionConfiguration&lt;/span&gt;: &lt;span class="kt"&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;forceDestroy&lt;/span&gt;: &lt;span class="kt"&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;// Create a lambda that will post a message to slack when the bucket changes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;postToSlack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&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;slack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WebClient&lt;/span&gt;&lt;span class="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="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rec&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Records&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;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&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="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;This now feels far more like how one might expect to express this
concept with a normal application. A simple conceptual idea now maps to
a simple code pattern. Instead of manually creating objects to represent
the connection between the Bucket and the Lambda, we simply directly ask
the Bucket to call the Lambda when the &lt;code&gt;ObjectCreated&lt;/code&gt; event fires.
Because this is all &amp;lsquo;code&amp;rsquo;, we can take care of all the boring cruft
(like creating permissions) on your behalf. Of course, if you need to
tweak this, that&amp;rsquo;s still possible. We believe you should always be in
control of what&amp;rsquo;s going on. The actual low level details of how that is
done are interesting as well, but a followup post will explain how we
did it (and how you can do it too!). If you want to create your own
composable modules for making cloud deployments easier then definitely
look for that post!&lt;/p&gt;
&lt;p&gt;Now, while it&amp;rsquo;s how the above examples worked, it&amp;rsquo;s also worth noting
that the use of a JavaScript function for the AWS Lambda is not
required. You can hook up a serverless event to call an AWS Lambda you
create just by using &lt;a href="https://github.com/pulumi/pulumi-aws/blob/master/sdk/nodejs/lambda/function.ts#L14"&gt;new aws.lambda.Function&lt;/a&gt;.
Or, you can get a reference to an existing AWS Lambda created outside of
Pulumi and have that be the receiver of your serverless event. Here&amp;rsquo;s
how that would look:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="kr"&gt;from&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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;slack&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@slack/client&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 a simple bucket.
&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;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;testbucket&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;serverSideEncryptionConfiguration&lt;/span&gt;: &lt;span class="kt"&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;forceDestroy&lt;/span&gt;: &lt;span class="kt"&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;// Retrieve an existing Lambda Function already created in your AWS infrastructure.
&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;func&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;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;postToSlack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;... existing lambda arn ...&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;// Call that Lambda Function when an object is created in the bucket.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;postToSlack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func&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;We&amp;rsquo;ve tried to make it this simple to hook up many interesting AWS
serverless events. For example, you can register to hear about events on
&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html"&gt;S3 Buckets&lt;/a&gt;,
&lt;a href="https://docs.aws.amazon.com/sns/latest/dg/sns-lambda-as-subscriber.html"&gt;SNS Topics&lt;/a&gt;,
&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html"&gt;SQS Queues&lt;/a&gt;,
&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html"&gt;Kinesis Streams&lt;/a&gt;,
&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html"&gt;DynamoDB Tables&lt;/a&gt;,
&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/RunLambdaSchedule.html"&gt;Cloudwatch Events&lt;/a&gt;,
and more. We&amp;rsquo;ve tried to provide these prebuilt components for the most
common and interesting cases. If there&amp;rsquo;s some serverless event we
haven&amp;rsquo;t added support for that you want to use, definitely let us know!&lt;/p&gt;
&lt;p&gt;Finally, because we think it&amp;rsquo;s great to be able to use a JavaScript or
TypeScript function as the code for your Lambda, we&amp;rsquo;ve introduced a
simple quality-of-life improvement for writing those functions.
Specifically, because it&amp;rsquo;s so common that one will use the real &lt;a href="https://aws.amazon.com/sdk-for-node-js/"&gt;AWS SDK
for Node.js&lt;/a&gt; within a Lambda,
we&amp;rsquo;ve provided a quick and simple way to get to a fully initialized
instance of that API without needing any additional &lt;code&gt;requires&lt;/code&gt; or
&lt;code&gt;imports&lt;/code&gt; in your code. Here&amp;rsquo;s how that would look:&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;// The only module you need to import.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="kr"&gt;from&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&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="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;postToSlack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;// direct access to the aws-sdk through `aws.sdk`.
&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;sqs&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;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SQS&lt;/span&gt;&lt;span class="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;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This allows you to use &lt;code&gt;@pulumi/aws&lt;/code&gt; as both the deployment-time API to
define your AWS infrastructure, as well as being the run-time API that
you can use when your Lambda executes to access the full set of AWS
functionality.&lt;/p&gt;
&lt;p&gt;To get a sense of a more complete example of how this all ties together
you can see many of our &lt;a href="https://github.com/pulumi/examples"&gt;examples&lt;/a&gt;.
One example that helps demonstrate many of the above concept is
&lt;a href="https://github.com/pulumi/examples/blob/master/aws-ts-twitter-athena/index.ts"&gt;Twitter+Athena&lt;/a&gt;.
In it, we use serverless events to define a schedule, a callback to
invoke when the even t fires, use of a third-party twitter API to
retrieve a set of relevant Tweets, use of the aws-sdk to store the Tweet
information into an S3 Bucket, and finally the creation of an Athena
Query to extract information from the information stored into the
bucket. You could augment this example with any number of resources and
events for all sorts of interesting use cases.&lt;/p&gt;
&lt;p&gt;As we&amp;rsquo;ve seen, with a tiny amount of code, you can easily define the
cloud resources you want to create (or reference your existing cloud
resources). It&amp;rsquo;s then simple to add code to respond to events and call
new or existing Lambdas. And, if you use a JavaScript function to create
your Lambda, you can reference those resources along with the AWS SDK
easily, all within the same application code!&lt;/p&gt;</description><author>Cyrus Najmabadi</author><category>javascript</category><category>serverless</category><category>aws</category></item><item><title>From Terraform to Infrastructure as Software</title><link>https://www.pulumi.com/blog/from-terraform-to-infrastructure-as-software/</link><pubDate>Fri, 02 Nov 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/from-terraform-to-infrastructure-as-software/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/from-terraform-to-infrastructure-as-software/index.png" /&gt;
&lt;p&gt;Here at Pulumi, we love programming the cloud using infrastructure as
code. From the project&amp;rsquo;s outset, we&amp;rsquo;ve been inspired by technologies
like Terraform, AWS CloudFormation, and Helm, and in fact leverage the
Terraform Providers ecosystem, to support a broad range of clouds,
including AWS, Azure, and Google Cloud.&lt;/p&gt;
&lt;p&gt;Just recently, we extended this
with first class support for Kubernetes. Pulumi delivers the same
infrastructure as code workflows only using general purpose languages
like JavaScript, TypeScript, Python, and Go, extending robust
infrastructure provisioning with abstraction and reuse, highly
productive tooling, and access to all the other things we already know
and love about programming languages.&lt;/p&gt;
&lt;p&gt;In this article, we will convert existing Terraform configuration to
Pulumi TypeScript. By doing so, we&amp;rsquo;ll see how using general purpose
programming languages can help you create simpler, more flexible
infrastructure as code, with greater productivity and less repetition.
The infrastructure we&amp;rsquo;ll be working with describes a load-balanced web
server hosted by an AWS EC2 instance per availability zone with an
option to allow SSH access. Of course, these same benefits would also
accrue were we to target Azure, Google Cloud, or Kubernetes instead.&lt;/p&gt;
&lt;h2 id="starting-from-terraform-configuration"&gt;Starting from Terraform Configuration&lt;/h2&gt;
&lt;p&gt;The Terraform configuration we&amp;rsquo;ll be starting with is below. This
configuration accepts a list of AZs and an optional public key as inputs
and produces a (possibly empty) list of the web servers&amp;rsquo; public IP
addresses and the URL of the load-balanced web site as outputs. When a
public key is provided, this configuration will enable SSH access to the
virtual machines that serve the web site.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s walk the example one step at a time &amp;ndash; feel free to skip ahead if
you&amp;rsquo;re a Terraform expert and just want to see how Pulumi is different.&lt;/p&gt;
&lt;p&gt;First, we declare that &lt;code&gt;availability_zones&lt;/code&gt; and &lt;code&gt;public_key&lt;/code&gt; are
parameterizable inputs:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;variable &amp;#34;availability_zones&amp;#34; {}
variable &amp;#34;public_key&amp;#34; {
default = &amp;#34;&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then we look up the latest AWS AMI for Ubuntu Trusty in our current
region. This is what our web server will run when we get around to
provisioning it later:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;data &amp;#34;aws_ami&amp;#34; &amp;#34;ubuntu&amp;#34; {
 most_recent = true
 filter {
   name   = &amp;#34;name&amp;#34;
   values = [&amp;#34;ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*&amp;#34;]
 }
 filter {
   name   = &amp;#34;virtualization-type&amp;#34;
   values = [&amp;#34;hvm&amp;#34;]
 }
 owners = [&amp;#34;099720109477&amp;#34;] # Canonical
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we&amp;rsquo;ll create security groups that allow HTTP ingress on port 80
and, if a public key is available, SSH over port 22, in addition to
allowing outbound Internet access. Notice that we are using
the &lt;code&gt;count&lt;/code&gt; property for the SSH rule to enable it conditionally:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;resource &amp;#34;aws_security_group&amp;#34; &amp;#34;default&amp;#34; {
name_prefix = &amp;#34;example_sg&amp;#34;
}
resource &amp;#34;aws_security_group_rule&amp;#34; &amp;#34;allow_http&amp;#34; {
type = &amp;#34;ingress&amp;#34;
from_port = 80
to_port = 80
protocol = &amp;#34;tcp&amp;#34;
cidr_blocks = [&amp;#34;0.0.0.0/0&amp;#34;]
security_group_id = &amp;#34;${aws_security_group.default.id}&amp;#34;
}
resource &amp;#34;aws_security_group_rule&amp;#34; &amp;#34;allow_all_outbound&amp;#34; {
type = &amp;#34;egress&amp;#34;
from_port = 0
to_port = 0
protocol = &amp;#34;-1&amp;#34;
cidr_blocks = [&amp;#34;0.0.0.0/0&amp;#34;]
security_group_id = &amp;#34;${aws_security_group.default.id}&amp;#34;
}
resource &amp;#34;aws_security_group_rule&amp;#34; &amp;#34;allow_ssh&amp;#34; {
 count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? 0 : 1}&amp;#34;
 type = &amp;#34;ingress&amp;#34;
 from_port = 22
 to_port = 22
 protocol = &amp;#34;tcp&amp;#34;
 cidr_blocks = [&amp;#34;0.0.0.0/0&amp;#34;]
 security_group_id = &amp;#34;${aws_security_group.default.id}&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let&amp;rsquo;s actually create the web servers. This is where things get a
little awkward in a DSL, because we want to conditionally attach
the &lt;code&gt;key_name&lt;/code&gt;, based on whether SSH access is enabled. This requires
that we duplicate some configuration for the key and no-key code paths,
in addition to splitting, transforming, and indexing into our list of
AZs:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; resource &amp;#34;aws_key_pair&amp;#34; &amp;#34;default&amp;#34; {
 count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? 0 : 1}&amp;#34;
 key_name_prefix = &amp;#34;default&amp;#34;
 public_key = &amp;#34;${var.public_key}&amp;#34;
}
resource &amp;#34;aws_instance&amp;#34; &amp;#34;web-server-key&amp;#34; {
 count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? 0 : length(split(&amp;#34;,&amp;#34;, var.availability_zones))}&amp;#34;
 ami = &amp;#34;${data.aws_ami.ubuntu.id}&amp;#34;
 instance_type = &amp;#34;t2.micro&amp;#34;
 availability_zone = &amp;#34;${element(split(&amp;#34;,&amp;#34;, var.availability_zones), count.index)}&amp;#34;
 user_data = &amp;#34;${file(&amp;#34;userdata.sh&amp;#34;)}&amp;#34;
 security_groups = [&amp;#34;${aws_security_group.default.name}&amp;#34;]
 key_name = &amp;#34;${aws_key_pair.default.0.key_name}&amp;#34;
}
resource &amp;#34;aws_instance&amp;#34; &amp;#34;web-server-nokey&amp;#34; {
 count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? length(split(&amp;#34;,&amp;#34;, var.availability_zones)) : 0}&amp;#34;
 ami = &amp;#34;${data.aws_ami.ubuntu.id}&amp;#34;
 instance_type = &amp;#34;t2.micro&amp;#34;
 availability_zone = &amp;#34;${element(split(&amp;#34;,&amp;#34;, var.availability_zones), count.index)}&amp;#34;
 user_data = &amp;#34;${file(&amp;#34;userdata.sh&amp;#34;)}&amp;#34;
 security_groups = [&amp;#34;${aws_security_group.default.name}&amp;#34;]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After creating the web servers, we will create and attach an ELB, so
that traffic is spread across the AZs. This also requires the same
conditional duplication for key and no-key configurations:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;resource &amp;#34;aws_elb&amp;#34; &amp;#34;elb&amp;#34; {
name_prefix = &amp;#34;webelb&amp;#34;
availability_zones = [&amp;#34;${split(&amp;#34;,&amp;#34;, var.availability_zones)}&amp;#34;]
listener {
instance_port = 80
instance_protocol = &amp;#34;http&amp;#34;
lb_port = 80
lb_protocol = &amp;#34;http&amp;#34;
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = &amp;#34;HTTP:80/&amp;#34;
interval = 30
}
}
resource &amp;#34;aws_elb_attachment&amp;#34; &amp;#34;web-server-key&amp;#34; {
count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? 0 : length(split(&amp;#34;,&amp;#34;, var.availability_zones))}&amp;#34;
elb = &amp;#34;${aws_elb.elb.id}&amp;#34;
instance = &amp;#34;${element(aws_instance.web-server-key.*.id, count.index)}&amp;#34;
}
resource &amp;#34;aws_elb_attachment&amp;#34; &amp;#34;web-server-nokey&amp;#34; {
count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? length(split(&amp;#34;,&amp;#34;, var.availability_zones)) : 0}&amp;#34;
elb = &amp;#34;${aws_elb.elb.id}&amp;#34;
instance = &amp;#34;${element(aws_instance.web-server-nokey.*.id, count.index)}&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we&amp;rsquo;ll export the public instance IPs for SSH access (if
enabled) and the ELB URL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;output &amp;#34;public_ips&amp;#34; {
 value = &amp;#34;${aws_instance.web-server-key.*.public_ip}&amp;#34;
}
output &amp;#34;url&amp;#34; {
 value = &amp;#34;http://${aws_elb.elb.dns_name}&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And that&amp;rsquo;s it. This program provisions an EC2 VM per AZ in your region
of choice, behind a load balancer, with conditional SSH enablement.The
full code is &lt;a href="https://gist.github.com/pgavlin/c09972d6e04e452250c86d10bd7ccd31#file-main-tf"&gt;available here&lt;/a&gt; if
you&amp;rsquo;d like to see it in one place.&lt;/p&gt;
&lt;h2 id="migrating-to-a-general-purpose-language"&gt;Migrating to a General Purpose Language&lt;/h2&gt;
&lt;p&gt;Now let&amp;rsquo;s see how to migrate this program to using a general purpose
language. There are several opportunities to simplify the program &amp;ndash;
most immediately apparent, using familiar if statements instead of the
the awkward conditionals that use count variables.&lt;/p&gt;
&lt;p&gt;We could just go ahead and convert the program by hand, however
the &lt;code&gt;tf2pulumi&lt;/code&gt; tool was designed
to take a Terraform HCL program as input and perform the entire
translation automatically for you. This is a great place to get started
&amp;ndash; and from there we can then refactor the resulting program gradually
to leverage languages better.&lt;/p&gt;
&lt;div class="note note-info"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;As of Pulumi CLI v3.71.0, &lt;code&gt;tf2pulumi&lt;/code&gt; has been replaced with &lt;code&gt;pulumi convert --from terraform&lt;/code&gt;. &lt;a href="https://www.pulumi.com/blog/converting-full-terraform-programs-to-pulumi/"&gt;Converting Full Terraform Programs to Pulumi blog&lt;/a&gt; has more details.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The first step is to create a Pulumi project.
&lt;a href="https://www.pulumi.com/docs/install/"&gt;Download Pulumi here&lt;/a&gt;, and then run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ pulumi new aws-typescript
-n webservers -d &amp;#34;Pulumi Web Servers&amp;#34; -g -f
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Run this from wherever your Terraform program resides. This will
initialize and scaffold a minimal Pulumi TypeScript program targeting
AWS in the pulumi subdirectory.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll then install &lt;code&gt;tf2pulumi&lt;/code&gt;,
and run it, to generate the Pulumi TypeScript program in &lt;code&gt;index.ts&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ tf2pulumi
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The full program created by this command can be
&lt;a href="https://gist.github.com/pgavlin/c09972d6e04e452250c86d10bd7ccd31#file-index-generated-ts"&gt;found here&lt;/a&gt;.
Notice that it looks structurally very similar to the Terraform HCL
program above, except that it&amp;rsquo;s written in TypeScript instead. Even this
rote translation has benefits &amp;ndash; for example, open it in your favorite
editor, and you&amp;rsquo;ll notice you get syntax highlighting, statement
completion, and integrated documentation. But we&amp;rsquo;ll soon see that once
we have this code in our favorite language, we can refactor it to
leverage familiar programming languages more idiomatically, reducing a lot
of boilerplate.&lt;/p&gt;
&lt;p&gt;After the conversion is complete, we can create, configure, and update a
Pulumi stack to create the resources described by the generated Pulumi
program:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nb"&gt;cd&lt;/span&gt; pulumi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ npm install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi stack init webservers-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi config &lt;span class="nb"&gt;set&lt;/span&gt; aws:region us-west-2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi config &lt;span class="nb"&gt;set&lt;/span&gt; availabilityZones &lt;span class="s2"&gt;&amp;#34;us-west-2a,us-west-2b,us-west-2c&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A Pulumi stack is an environment that can be configured and deployed to
independently of all other stacks. Here we&amp;rsquo;ve
created &lt;code&gt;webservers-prod&lt;/code&gt; and set it to deploy into
the &lt;code&gt;us-west-2a&lt;/code&gt;, &lt;code&gt;us-west-2b&lt;/code&gt;, and &lt;code&gt;us-west-2c&lt;/code&gt; availability zones in
AWS&amp;rsquo;s &lt;code&gt;us-west-2&lt;/code&gt; region. &lt;code&gt;pulumi up&lt;/code&gt; will then show you a plan and then
let you confirm it to deploy your resources:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/from-terraform-to-infrastructure-as-software/tf-pulumi-preview.png" alt="tf-pulumi-preview"&gt;&lt;/p&gt;
&lt;p&gt;After selecting &amp;ldquo;yes&amp;rdquo;, the update will proceed with interactive status
updates:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/from-terraform-to-infrastructure-as-software/tf-pulumi-update.png" alt="tf-pulumi-update"&gt;&lt;/p&gt;
&lt;h2 id="leveraging-programming-languages"&gt;Leveraging Programming Languages&lt;/h2&gt;
&lt;p&gt;This is great &amp;ndash; we now have a working Pulumi program. Let&amp;rsquo;s now go
about simplifying the generated code so that it is easier to read,
understand, and maintain. We&amp;rsquo;ll only scratch the surface in this post,
as many of the real benefits shine when you go beyond a single program
and use reusable constructs like functions, classes, and packages, to
cut down on copy and paste. But even some of the syntactic
transformations we&amp;rsquo;ll make can provide a real boost to productivity.&lt;/p&gt;
&lt;p&gt;First, notice that our package dependencies are just NPM packages. The
full ecosystem of our language&amp;rsquo;s native package manager is available to
us:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/pulumi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="kr"&gt;from&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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;fs&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;The first order of business is to place the list of configured
availability zones into a local variable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const availabilityZones = config.require(&amp;quot;availabilityZones&amp;quot;).split(&amp;quot;,&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because Pulumi programs are ordinary programs, we can use variables in
the usual ways. Notice that we&amp;rsquo;ve not only defined a variable, but also
split the string, so that we don&amp;rsquo;t need to do it on every usage within
the file, using the standard JavaScript &lt;code&gt;split&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Terraform data sources are represented as function calls in our program.
So, to fetch the Ubuntu AMI, we simply call the &lt;code&gt;getAmi&lt;/code&gt; function,
passing in the filters as parameters to it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ubuntuAmi&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;getAmi&lt;/span&gt;&lt;span class="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 class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&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="nx"&gt;values&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;ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*&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="p"&gt;{&lt;/span&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;virtualization-type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&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 class="s2"&gt;&amp;#34;hvm&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="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;mostRecent&lt;/span&gt;: &lt;span class="kt"&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;owners&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;099720109477&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Canonical
&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 already saw how the use of variables can save us from repeating
ourselves over and over again, and again, we can use this to avoid
repeating the 0.0.0.0/0 CIDR block everywhere:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anyCidr&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;0.0.0.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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webServerSg&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;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SecurityGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;default&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;egress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;cidrBlocks&lt;/span&gt;: &lt;span class="kt"&gt;anyCidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fromPort&lt;/span&gt;: &lt;span class="kt"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toPort&lt;/span&gt;: &lt;span class="kt"&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="nx"&gt;ingress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;cidrBlocks&lt;/span&gt;: &lt;span class="kt"&gt;anyCidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fromPort&lt;/span&gt;: &lt;span class="kt"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toPort&lt;/span&gt;: &lt;span class="kt"&gt;80&lt;/span&gt; &lt;span class="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 can change how we conditionally create the web server instances.
This is where we&amp;rsquo;ll start to see the big wins, because we can use any of
the familiar programming language control constructs,
including &lt;code&gt;for&lt;/code&gt; loops, &lt;code&gt;case&lt;/code&gt; statements, &lt;code&gt;if&lt;/code&gt; blocks, function calls,
and even higher order list functions like &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;filter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In order to conditionally create a resource in Terraform, we had to set
its &lt;code&gt;count&lt;/code&gt; to either &lt;code&gt;0&lt;/code&gt; or the desired AZ count. However, this affects
any resources that depend on the conditional resource: the dependent
resources either become conditional themselves or require complex
interpolation expressions in order to ensure that the configuration does
not reference any undefined properties. In our original Terraform
configuration, the conditional creation of an AWS Key Pair forces us to
declare our web servers twice: once with the key pair and once without.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;resource &amp;#34;aws_key_pair&amp;#34; &amp;#34;default&amp;#34; {
count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? 0 : 1}&amp;#34;
…
resource &amp;#34;aws_instance&amp;#34; &amp;#34;web-server-key&amp;#34; {
count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? 0 : length(split(&amp;#34;,&amp;#34;, var.availability_zones))}&amp;#34;
…
resource &amp;#34;aws_elb_attachment&amp;#34; &amp;#34;web-server-key&amp;#34; {
count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? 0 : length(split(&amp;#34;,&amp;#34;, var.availability_zones))}&amp;#34;
…
resource &amp;#34;aws_instance&amp;#34; &amp;#34;web-server-nokey&amp;#34; {
count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? length(split(&amp;#34;,&amp;#34;, var.availability_zones)) : 0}&amp;#34;
…
resource &amp;#34;aws_elb_attachment&amp;#34; &amp;#34;web-server-nokey&amp;#34; {
count = &amp;#34;${var.public_key == &amp;#34;&amp;#34; ? length(split(&amp;#34;,&amp;#34;, var.availability_zones)) : 0}&amp;#34;
…
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is rather cumbersome. The syntactic transformation performed
by &lt;code&gt;tf2pulumi&lt;/code&gt; preserved this same structure to ensure the semantics of
translation are identical, but we can now go back and clean it up
considerably.&lt;/p&gt;
&lt;p&gt;First, to conditionally allocate an EC2 KeyPair and to open port 22, we
use an &lt;code&gt;if&lt;/code&gt; statement:&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;keyName&lt;/span&gt;: &lt;span class="kt"&gt;pulumi.Output&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&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;keyName&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;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyPair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;publicKey&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="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;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SecurityGroupRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;allow_ssh&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="kr"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ingress&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;cidrBlocks&lt;/span&gt;: &lt;span class="kt"&gt;anyCidr&lt;/span&gt;&lt;span class="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;fromPort&lt;/span&gt;: &lt;span class="kt"&gt;22&lt;/span&gt;&lt;span class="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;toPort&lt;/span&gt;: &lt;span class="kt"&gt;22&lt;/span&gt;&lt;span class="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;protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tcp&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;securityGroupId&lt;/span&gt;: &lt;span class="kt"&gt;webServerSg.id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&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;The use of conditionals just works and interacts with planning just as
you might expect, so that we always see the result of the conditional
evaluation before we&amp;rsquo;ve decided to deploy anything.&lt;/p&gt;
&lt;p&gt;Another subtle advantage is that we can just pass
the &lt;code&gt;webServerSg.id&lt;/code&gt; property &amp;ndash; Pulumi understands the resource DAG,
and the use of this property automatically forms an edge.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s create our web servers. We might use a for loop for this, and
append each one to an array, however it&amp;rsquo;s just as easy to use the
JavaScript &lt;code&gt;map&lt;/code&gt; function to do this more declaratively:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webServers&lt;/span&gt;: &lt;span class="kt"&gt;aws.ec2.Instance&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;availabilityZones&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;az&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="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;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`web-server-key-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;az&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;ami&lt;/span&gt;: &lt;span class="kt"&gt;ubuntuAmi.then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ami&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ami&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;associatePublicIpAddress&lt;/span&gt;: &lt;span class="kt"&gt;publicKey&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;availabilityZone&lt;/span&gt;: &lt;span class="kt"&gt;az&lt;/span&gt;&lt;span class="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="s2"&gt;&amp;#34;t2.micro&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;securityGroups&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;webServerSg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;userData&lt;/span&gt;: &lt;span class="kt"&gt;fs.readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;userdata.sh&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;utf-8&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;keyName&lt;/span&gt;: &lt;span class="kt"&gt;keyName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Interestingly, we&amp;rsquo;ve passed &lt;code&gt;keyName&lt;/code&gt; to the constructor here, even
though it might not have been defined (the JavaScript default value
of &lt;code&gt;undefined&lt;/code&gt;). Pulumi understands that this means &amp;ldquo;the absence of a
value&amp;rdquo;, which is how we were able to simplify the web server
provisioning so greatly. We also read in the &lt;code&gt;userdata.sh&lt;/code&gt; file using
standard JavaScript I/O functionality.&lt;/p&gt;
&lt;p&gt;Now that we&amp;rsquo;ve simplified our configuration such that the web servers
are declared once, we can simplify their interaction with the load
balancer. We can just create the ELB last and pass it a list of all of
our instances. We can also improve the reliability of configuration by
ensuring that the ELB is associated with each availability zone in which
we&amp;rsquo;ve created a web server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webServerElb&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;elasticloadbalancing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;elb&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;availabilityZones&lt;/span&gt;: &lt;span class="kt"&gt;webServers.map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availabilityZone&lt;/span&gt;&lt;span class="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;instances&lt;/span&gt;: &lt;span class="kt"&gt;webServers.map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;instance&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;healthCheck&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;healthyThreshold&lt;/span&gt;: &lt;span class="kt"&gt;2&lt;/span&gt;&lt;span class="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;interval&lt;/span&gt;: &lt;span class="kt"&gt;30&lt;/span&gt;&lt;span class="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;target&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;HTTP:80/&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;timeout&lt;/span&gt;: &lt;span class="kt"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;unhealthyThreshold&lt;/span&gt;: &lt;span class="kt"&gt;2&lt;/span&gt;&lt;span class="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;listeners&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;instancePort&lt;/span&gt;: &lt;span class="kt"&gt;80&lt;/span&gt;&lt;span class="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;instanceProtocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http&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;lbPort&lt;/span&gt;: &lt;span class="kt"&gt;80&lt;/span&gt;&lt;span class="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;lbProtocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, the way we express outputs is by using standard JavaScript
module exports:&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;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publicIps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;webServers&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;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicIp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;webServerElb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dnsName&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;hostname&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sb"&gt;`http://&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hostname&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it. After doing this refactoring, you can simply
run &lt;code&gt;pulumi up&lt;/code&gt;, and it will show you any diffs compared to your
previous deployment, which you can choose to accept and deploy. The full
code is &lt;a href="https://gist.github.com/pgavlin/c09972d6e04e452250c86d10bd7ccd31#file-index-ts"&gt;available here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;rsquo;ve applied general purpose programming languages to
infrastructure as code to achieve more expressive and reusable programs,
simplify our infrastructure&amp;rsquo;s readability and maintainability, and
deliver a heightened level of productivity. Pulumi is
&lt;a href="https://github.com/pulumi/pulumi"&gt;open source&lt;/a&gt; and free to use &amp;ndash;
give it a try today!&lt;/p&gt;</description><author>Pat Gavlin</author><category>javascript</category><category>typescript</category></item><item><title>Provisioning and Managing Cloud Infrastructure with Pulumi</title><link>https://www.pulumi.com/blog/provisioning-and-managing-cloud-infrastructure-with-pulumi/</link><pubDate>Fri, 20 Jul 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/provisioning-and-managing-cloud-infrastructure-with-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/provisioning-and-managing-cloud-infrastructure-with-pulumi/index.png" /&gt;
&lt;p&gt;If you&amp;rsquo;ve been following the blog, you know that Pulumi is great for
building &lt;a href="https://www.pulumi.com/blog/code-deploy-and-manage-a-serverless-rest-api-on-aws-with-pulumi/"&gt;serverless applications&lt;/a&gt;,
&lt;a href="https://www.pulumi.com/blog/deploying-production-ready-containers-with-pulumi/"&gt;container-based applications&lt;/a&gt;,
and a &lt;a href="https://www.pulumi.com/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/"&gt;combination of the two&lt;/a&gt;.
But, did you know that you can manage any cloud resource in AWS, Azure, or Google Cloud Platform?&lt;/p&gt;
&lt;p&gt;You can use the
&lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/"&gt;AWS&lt;/a&gt;,
&lt;a href="https://www.pulumi.com/registry/packages/azure/api-docs/"&gt;Azure&lt;/a&gt;,
or &lt;a href="https://www.pulumi.com/registry/packages/gcp/api-docs/"&gt;Google Cloud&lt;/a&gt;
libraries to manage cloud resources. Using these libraries, you can
directly manage the properties of any cloud resource.&lt;/p&gt;
&lt;p&gt;For example, in just a few lines of code, you can provision a security
group and an EC2 instance:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let group = new aws.ec2.SecurityGroup(&amp;quot;web-secgrp&amp;quot;, {
ingress: [
{ protocol: &amp;quot;tcp&amp;quot;, fromPort: 22, toPort: 22, cidrBlocks: [&amp;quot;0.0.0.0/0&amp;quot;] },
],
});
let server = new aws.ec2.Instance(&amp;quot;web-server-www&amp;quot;, {
instanceType: &amp;quot;t2.micro&amp;quot;,
securityGroups: [ group.name ], // reference the group object above
ami: &amp;quot;ami-7172b611&amp;quot;,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&amp;rsquo;s add a a CloudWatch metric alarm that is triggered when when
the CPU utilization is over 80%:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const metricAlarm = new aws.cloudwatch.MetricAlarm(&amp;quot;mymetricalarm&amp;quot;, {
comparisonOperator: &amp;quot;GreaterThanOrEqualToThreshold&amp;quot;,
evaluationPeriods: 2,
metricName: &amp;quot;CPUUtilization&amp;quot;,
namespace: &amp;quot;AWS/EC2&amp;quot;,
period: 120,
statistic: &amp;quot;Average&amp;quot;,
threshold: 80,
alarmDescription: &amp;quot;This metric monitors ec2 cpu utilization&amp;quot;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition to alerting, you may want to provision a &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Dashboards.html"&gt;CloudWatch dashboard&lt;/a&gt;
to have a single monitoring view for all your application resources. You
can define this dashboard directly in code using Pulumi. The following
code defines a dashboard and specifies the widgets to include:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const dashboard = new aws.cloudwatch.Dashboard(&amp;quot;mydashboard&amp;quot;, {
dashboardName: &amp;quot;my-dashboard&amp;quot;,
dashboardBody: JSON.stringify({
widgets: [
{
type: &amp;quot;metric&amp;quot;,
x: 0,
y: 0,
width: 12,
height: 6,
properties: {
metrics: [
[
&amp;quot;AWS/EC2&amp;quot;,
&amp;quot;CPUUtilization&amp;quot;,
&amp;quot;InstanceId&amp;quot;,
&amp;quot;i-012345&amp;quot;
]
],
period: 300,
stat: &amp;quot;Average&amp;quot;,
region: &amp;quot;us-east-1&amp;quot;,
title: &amp;quot;EC2 Instance CPU&amp;quot;
}
}
]
})
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Perhaps you want to post to an SNS topic whenever a user signs in to the
AWS console for your production account. This requires provisioning
three resources: the SNS topic, a CloudWatch event rule, and a
CloudWatch event target. With Pulumi, this can all be specified with
just a few lines of JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const loginsTopic = new aws.sns.Topic(&amp;quot;myloginstopic&amp;quot;);
const eventRule = new aws.cloudwatch.EventRule(&amp;quot;myeventrule&amp;quot;, {
eventPattern: JSON.stringify({
&amp;quot;detail-type&amp;quot;: [
&amp;quot;AWS Console Sign In via CloudTrail&amp;quot;
]
})
});
const eventTarget = new aws.cloudwatch.EventTarget(&amp;quot;myeventtarget&amp;quot;, {
rule: eventRule.name,
targetId: &amp;quot;SendToSNS&amp;quot;,
arn: loginsTopic.arn
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are just a few examples of the AWS resources you can manage in
Pulumi. You can provision
&lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/athena/"&gt;Athena databases&lt;/a&gt;,
&lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/dynamodb/"&gt;DynamoDB tables&lt;/a&gt;,
&lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/iam/"&gt;IAM users, roles, groups, and role policies&lt;/a&gt;,
&lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/kinesis/"&gt;Kinesis streams&lt;/a&gt;, and more.&lt;/p&gt;
&lt;p&gt;To learn more, take a look at the
&lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/"&gt;AWS API documentation&lt;/a&gt;
and the &lt;a href="https://github.com/pulumi/examples/blob/master/aws-ts-resources/index.ts"&gt;sample code that provisions a variety of infrastructure resources&lt;/a&gt;.&lt;/p&gt;</description><author>Donna Malayeri</author><category>javascript</category><category>aws</category></item><item><title>AWS SQS to Lambda with Pulumi: Serverless Queue Example</title><link>https://www.pulumi.com/blog/using-pulumi-with-aws-sqs-and-lambdas/</link><pubDate>Tue, 10 Jul 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/using-pulumi-with-aws-sqs-and-lambdas/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/using-pulumi-with-aws-sqs-and-lambdas/index.png" /&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/aws/aws-lambda-adds-amazon-simple-queue-service-to-supported-event-sources/"&gt;Two weeks ago&lt;/a&gt;
Amazon added &lt;a href="https://aws.amazon.com/sqs/"&gt;Simple Queue Service&lt;/a&gt; (SQS)
as a supported event source for
&lt;a href="https://aws.amazon.com/lambda/"&gt;Lambda&lt;/a&gt;. SQS is one of AWS&amp;rsquo;s oldest
services, providing access to a powerful message queue that can do
things like guarantee messages will be delivered at least once, or
messages that will be processed in the same order they were received in.
Adding SQS as a supported event source for Lambda means that now it&amp;rsquo;s
possible to use SQS in a serverless computing infrastructure, where
Lambdas are triggered in response to messages added to your SQS queue.
Now, instead of needing some sort of Service dedicated to polling your
SQS queue, or creating &lt;a href="https://aws.amazon.com/sns/"&gt;Simple Notification Service&lt;/a&gt; (SNS)
notifications from your
messages, you can instead just directly trigger whatever Lambda you
want.&lt;/p&gt;
&lt;h2 id="example-using-aws-sqs-and-lambda-to-post-messages-to-slack"&gt;Example: Using AWS SQS and Lambda to post messages to Slack&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a simple example of using SQS as an event source with Pulumi:
upon receipt of a message via SQS we post a new message into Slack. The
full project is available on Github in our
&lt;a href="https://github.com/pulumi/examples/tree/master/aws-js-sqs-slack"&gt;examples repo&lt;/a&gt;,
and includes the instructions to get this up and running.&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="kd"&gt;let&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;serverless&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-serverless&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;config&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;./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&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;queue&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;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mySlackQueue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;visibilityTimeoutSeconds&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt; &lt;span 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;serverless&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mySlackPoster&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;slack&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;@slack/client&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;client&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;slack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slackToken&lt;/span&gt;&lt;span class="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="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rec&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="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;channel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slackChannel&lt;/span&gt;&lt;span class="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;text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`*SQS message &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageId&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;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sb"&gt;`(with :love_letter: from Pulumi)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;as_user&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 class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Posted SQS message &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; to Slack channel &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slackChannel&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 class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;batchSize&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&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;queueURL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queue&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="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="implementing-sqs-in-pulumi"&gt;Implementing SQS in Pulumi&lt;/h2&gt;
&lt;p&gt;When we heard about this new functionality, we immediately knew we
wanted to let people access this new power from their Pulumi
applications. Based on Amazon&amp;rsquo;s documentation on this new capability we
felt it would likely be very easy to provide. Amazon introduced this
functionality by reusing existing systems and APIs. Specifically, to
create a Lambda trigger off an SQS message you only need to create a new
&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/API_CreateEventSourceMapping.html"&gt;Event Source Mapping&lt;/a&gt;
mapping between the two. This seemed like it would be very simple, and
we set off to expose the right Pulumi API to make this possible.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/pulumi/pulumi-aws-serverless"&gt;@pulumi/aws-serverless&lt;/a&gt;
is Pulumi&amp;rsquo;s convenience library for creating
&lt;a href="https://aws.amazon.com/serverless/"&gt;Serverless&lt;/a&gt; computing components.
And we added the necessary code to expose the functionality in our
simple Pulumi programming model
&lt;a href="https://github.com/pulumi/pulumi-aws-serverless/blob/master/nodejs/aws-serverless/queue.ts"&gt;here&lt;/a&gt;.
With this new API you would now be able to write code in your Pulumi
application like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="nx"&gt;from&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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/aws-serverless&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 the queue. Provide whatever specific configuration you need here.
&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;sqsQueue&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;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;queue&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;visibilityTimeoutSeconds&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="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 up a subscription that will fire whenever the queue receives a message. Here we ask
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// for &amp;#39;batchSize = 1&amp;#39; so we will only process a single message at a time.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;serverless&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;subscription&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sqsQueue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Add whatever code you want here to run in the AWS lambda. &amp;#39;event&amp;#39; will contain 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;// all the necessary data about the message added to the queue.
&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;batchSize&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This example also demonstrates several of the great capabilities of a
Pulumi application. First, the ability to define your infrastructure
directly in code (i.e. the AWS Queue, Lambda, and Event Subscriptions),
instead of needing to do that separately. Second, the ability to avoid
needing to specify every single AWS resource necessary to get up and
running. For example, I did not have to supply explicit Roles,
Role-Policy-Attachments, or Permissions here as Pulumi took care of that
for me by default. Third, the ability to create an AWS Lambda directly
from a JavaScript/TypeScript arrow-function. That &lt;code&gt;async (event) =&amp;gt; ...&lt;/code&gt;
is written in code as a normal async-arrow-function, but it will be
converted by Pulumi into all the machinery necessary to have a true AWS
Lambda running in your infrastructure.&lt;/p&gt;
&lt;p&gt;After adding the API and writing up a simple app to test things, we then
ran a &lt;code&gt;pulumi update&lt;/code&gt; to deploy our application to AWS. Unfortunately,
we immediately ran into a problem:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Plan apply failed:
Error creating Lambda event source mapping: ValidationException: 1 validation error detected: Value &amp;#39;&amp;#39; at
&amp;#39;startingPosition&amp;#39; failed to satisfy constraint: Member must satisfy enum value set:
[LATEST, AT_TIMESTAMP, TRIM_HORIZON]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So what happened? Well, as it turns out, Pulumi leverages and integrates
with many other amazing Open Source projects under the covers. In this
case, it was our integration with &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt;
that was causing this small snag. It turns out that while Amazon relaxed
their API to no longer have that constraint on &amp;lsquo;startingPosition&amp;rsquo;,
Terraform was still referencing an older awssdk API that still contained
that constraint.&lt;/p&gt;
&lt;p&gt;At Pulumi we think that Terraform is fantastic, and we love using it. So
we thought the best thing we could do in this position was to help out
that project to support this new functionality as well. We did this
around two weeks ago by contributing &lt;a href="https://github.com/terraform-providers/terraform-provider-aws/pull/5024"&gt;this PR&lt;/a&gt;
to their project. We worked with Terraform over a couple over a few days
to get the PR up to snuff, and eventually got it in. With this, now both
Terraform and Pulumi customers will be able to benefit from these
changes in the upcoming releases for both projects!&lt;/p&gt;
&lt;p&gt;After getting the change in we went back and tested our example and saw
that now everything worked as expected. We also expanded things out
&lt;a href="https://github.com/pulumi/pulumi-aws/blob/master/examples/queue/index.ts"&gt;in an example&lt;/a&gt;
to show how you might use this in practice. In this example, we receive
the event, and then just write the data of it into an &lt;a href="https://aws.amazon.com/s3/"&gt;S3 Bucket&lt;/a&gt;. Running this example we now
successfully see:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;pulumi update
Updating stack &amp;#39;cysqstest&amp;#39;
Performing changes:
Type Name Status
+ pulumi:pulumi:Stack queue-cysqstest created
+ ├─ aws:serverless:Function subscription-queue-subscription created
+ │ ├─ aws:iam:Role subscription-queue-subscription created
+ │ ├─ aws:iam:RolePolicyAttachment subscription-queue-subscription-32be53a2 created
+ │ ├─ aws:iam:RolePolicyAttachment subscription-queue-subscription-7cd09230 created
+ │ └─ aws:lambda:Function subscription-queue-subscription created
+ ├─ aws:sqs:Queue queue created
+ ├─ aws:s3:Bucket testbucket created
+ └─ aws-serverless:queue:QueueEventSubscription subscription created
+ ├─ aws:lambda:Permission subscription created
+ └─ aws:lambda:EventSourceMapping subscription created
info: 11 changes performed:
+ 11 resources created
Update duration: 28s
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once created, we could then see this Stack at &lt;a href="https://app.pulumi.com/signin"&gt;app.pulumi.com&lt;/a&gt;,
and we could easily use the functionality there to even navigate to
those resources over at our &lt;a href="https://console.aws.amazon.com"&gt;AWS Console&lt;/a&gt;. Over there were were able to
use the console to both send a message to the Queue, verify our Lambda
got triggered, and even check our Bucket to see the data written into
it. In only around a dozen lines of code, we were able to provision 11
AWS resources, and create a real end-to-end Serverless message-receiving
and processing pipeline. Now, we can expand on this functionality with
more resources or more functionality, run &lt;code&gt;pulumi update&lt;/code&gt; and just
simply and safely update our cloud infrastructure!&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see this functionality lighting up in our next release of
&lt;code&gt;@pulumi/pulumi&lt;/code&gt; and &lt;code&gt;@pulumi/aws-serverless&lt;/code&gt;. We hope it helps you out
and makes your cloud life that much easier. Happy coding!&lt;/p&gt;</description><author>Cyrus Najmabadi</author><category>javascript</category><category>serverless</category><category>aws</category></item><item><title>Code, Deploy, and Manage a Serverless REST API on AWS</title><link>https://www.pulumi.com/blog/code-deploy-and-manage-a-serverless-rest-api-on-aws-with-pulumi/</link><pubDate>Fri, 22 Jun 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/code-deploy-and-manage-a-serverless-rest-api-on-aws-with-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/code-deploy-and-manage-a-serverless-rest-api-on-aws-with-pulumi/index.png" /&gt;
&lt;p&gt;Pulumi makes it easy to build serverless
applications and connect to other cloud resources. In this blog post,
we&amp;rsquo;ll create a simple REST API that counts the number of times a route
has been hit, using JavaScript to define both the infrastructure and
application code.&lt;/p&gt;
&lt;p&gt;In Pulumi, you define your application infrastructure in regular code,
using JavaScript, Python or Go, and you can target AWS, Azure, Google Cloud, or
Kubernetes. The Pulumi command line tool transforms your into a
declarative plan, following the best practices of immutable
infrastructure. You can write your app code in any language supported by
your serverless platform.&lt;/p&gt;
&lt;h2 id="example-app-serverless-route-counter"&gt;Example app: serverless route counter&lt;/h2&gt;
&lt;p&gt;In this tutorial, we&amp;rsquo;ll build a simple REST API that counts the number
of times a route has been hit. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl $URL/wow
{&amp;quot;route&amp;quot;:&amp;quot;wow&amp;quot;,&amp;quot;count&amp;quot;:1}
$ curl $URL/pulumi
{&amp;quot;route&amp;quot;:&amp;quot;pulumi&amp;quot;,&amp;quot;count&amp;quot;:1}
$ curl $URL/wow
{&amp;quot;route&amp;quot;:&amp;quot;wow&amp;quot;,&amp;quot;count&amp;quot;:2}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;rsquo;ll implement this using API Gateway, Lambda, and Dynamo DB:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/code-deploy-and-manage-a-serverless-rest-api-on-aws-with-pulumi/counter-arch.png" alt="counter-arch"&gt;&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;If this is your first time using Pulumi, go to &lt;a href="https://app.pulumi.com/signup"&gt;app.pulumi.com&lt;/a&gt; and sign in with
GitHub.&lt;/p&gt;
&lt;p&gt;Then, run the following command to install the Pulumi CLI:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -fsSL https://get.pulumi.com/ | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&amp;rsquo;re on Windows, run this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@&amp;quot;%SystemRoot%System32WindowsPowerShell1.0powershell.exe&amp;quot; -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command &amp;quot;iex ((New-Object System.Net.WebClient).DownloadString('https://get.pulumi.com/install.ps1'))&amp;quot;
SET &amp;quot;PATH=%PATH%;%USERPROFILE%.pulumiin&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&amp;rsquo;ll deploy this app to your own AWS account, so follow the steps to
&lt;a href="https://www.pulumi.com/docs/iac/get-started/aws/"&gt;configure your AWS account&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Make sure you have &lt;a href="https://nodejs.org/en/download/"&gt;Node.js&lt;/a&gt; installed,
with a version of 6.10.x or later.&lt;/p&gt;
&lt;h2 id="create-the-app"&gt;Create the App&lt;/h2&gt;
&lt;p&gt;First we&amp;rsquo;ll create a Pulumi project and add code for both the
infrastructure definitions and application code.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To create a new Pulumi project, run the following commands:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mkdir hello-http &amp;amp;&amp;amp; cd hello-http
pulumi new aws-javascript
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This creates a new project in the directory &lt;code&gt;hello-http&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace the contents of &lt;code&gt;index.js&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const cloud = require(&amp;#34;@pulumi/cloud-aws&amp;#34;);
/* Create a mapping from &amp;#39;route&amp;#39; to a count */
let counterTable = new cloud.Table(&amp;#34;counterTable&amp;#34;, &amp;#34;route&amp;#34;);
/* Create an REST API endpoint */
let endpoint = new cloud.API(&amp;#34;hello-world&amp;#34;);
endpoint.get(&amp;#34;/{route+}&amp;#34;, (req, res) =&amp;gt; {
let route = req.params[&amp;#34;route&amp;#34;];
console.log(`Getting count for &amp;#39;${route}&amp;#39;`);
/* get previous value and increment */
/* reference outer counterTable object */
counterTable.get({ route }).then(value =&amp;gt; {
let count = (value &amp;amp;&amp;amp; value.count) || 0;
counterTable.insert({ route, count: ++count }).then(() =&amp;gt; {
res.status(200).json({ route, count });
console.log(`Got count ${count} for &amp;#39;${route}&amp;#39;`);
});
});
});
exports.endpoint = endpoint.publish().url;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The definition for &lt;code&gt;counterTable&lt;/code&gt; stores a counter for each route, using
&lt;code&gt;cloud.Table&lt;/code&gt;. On AWS, this provisions a DynamoDB instance. To create a
new API Gateway instance, we create an instance of &lt;code&gt;cloud.API&lt;/code&gt;. New
routes can be added to this endpoint using functions such as &lt;code&gt;get&lt;/code&gt;,
&lt;code&gt;post&lt;/code&gt;, &lt;code&gt;put&lt;/code&gt; etc.&lt;/p&gt;
&lt;p&gt;The function passed to &lt;code&gt;get&lt;/code&gt; is the interesting part: this becomes the
body of a new AWS Lambda function that is called on a GET request to the
API Gateway. The body of this function can use variables defined in the
main program, such as &lt;code&gt;counterTable&lt;/code&gt;. This is translated to a lookup on
the provisioned DynamoDB instance; there is no need to store the table
name in an environment variable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, add &lt;code&gt;@pulumi/cloud-aws&lt;/code&gt; NPM package:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ npm install --save @pulumi/cloud @pulumi/cloud-aws
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="deploy-the-app"&gt;Deploy the App&lt;/h2&gt;
&lt;p&gt;To deploy both the infrastructure and app code, we&amp;rsquo;ll run
&lt;code&gt;pulumi update&lt;/code&gt;. This command first shows a preview of all the resources
that will be created and prompts for confirmation.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/code-deploy-and-manage-a-serverless-rest-api-on-aws-with-pulumi/pulumi-update-preview.png" alt="pulumi-update-preview"&gt;&lt;/p&gt;
&lt;p&gt;Choose the &amp;ldquo;yes&amp;rdquo; option to deploy to AWS. At the end of the update,
you&amp;rsquo;ll see a link to the Pulumi Service that shows the details of the
deployment.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/code-deploy-and-manage-a-serverless-rest-api-on-aws-with-pulumi/pulumi-update-complete.png" alt="pulumi-update-complete"&gt;&lt;/p&gt;
&lt;p&gt;Go to this link and click the &lt;strong&gt;Resources&lt;/strong&gt; tab. You&amp;rsquo;ll see all the
resources you&amp;rsquo;ve created, including links to the AWS Console.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/code-deploy-and-manage-a-serverless-rest-api-on-aws-with-pulumi/console-resources-tab.png" alt="console-resources-tab"&gt;&lt;/p&gt;
&lt;h2 id="test-the-app"&gt;Test the App&lt;/h2&gt;
&lt;p&gt;Now that the app is deployed, let&amp;rsquo;s try it out! With the Pulumi CLI,
you can easily view output properties for your stack. This line in the
JavaScript code creates a &lt;strong&gt;stack output&lt;/strong&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="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can view stack outputs in the Pulumi Service, or via
&lt;code&gt;pulumi stack output&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pulumi stack output
Current stack outputs (1):
OUTPUT VALUE
endpoint https://5e8xrktey3.execute-api.us-west-2.amazonaws.com/stage/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&amp;rsquo;s curl some routes!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl $(pulumi stack output endpoint)/wow
{&amp;quot;route&amp;quot;:&amp;quot;wow&amp;quot;,&amp;quot;count&amp;quot;:1}
$ curl $(pulumi stack output endpoint)/pulumi
{&amp;quot;route&amp;quot;:&amp;quot;pulumi&amp;quot;,&amp;quot;count&amp;quot;:1}
$ curl $(pulumi stack output endpoint)/wow
{&amp;quot;route&amp;quot;:&amp;quot;wow&amp;quot;,&amp;quot;count&amp;quot;:2}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also view logs for the Lambda via &lt;code&gt;pulumi logs&lt;/code&gt;. To get a log
tail, use the &lt;code&gt;--follow&lt;/code&gt; or &lt;code&gt;-f&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pulumi logs
Collecting logs for stack hello-http-dev since 2018-06-15T12:53:35.000-07:00.
2018-06-15T13:52:30.123-07:00[ hello-world4fcc7b60] Getting count for 'wow'
2018-06-15T13:52:34.361-07:00[ hello-world4fcc7b60] Got count 1 for 'wow'
2018-06-15T13:52:39.621-07:00[ hello-world4fcc7b60] Getting count for 'pulumi'
2018-06-15T13:52:39.757-07:00[ hello-world4fcc7b60] Got count 1 for 'pulumi'
2018-06-15T13:52:42.189-07:00[ hello-world4fcc7b60] Getting count for 'wow'
2018-06-15T13:52:42.325-07:00[ hello-world4fcc7b60] Got count 2 for 'wow'
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="clean-up"&gt;Clean up&lt;/h2&gt;
&lt;p&gt;To clean up the resources, run &lt;code&gt;pulumi destroy&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next steps&lt;/h2&gt;
&lt;p&gt;The
&lt;a href="https://github.com/pulumi/examples/tree/5f3eae87bb132595e4c60d2d5f8e8ee1473f6a7b/cloud-js-httpserver"&gt;sample code for this application&lt;/a&gt;
is available in the Pulumi examples repo on GitHub. For an end-to-end
TypeScript application with a frontend, see the
&lt;a href="https://github.com/pulumi/examples/tree/5f3eae87bb132595e4c60d2d5f8e8ee1473f6a7b/cloud-ts-url-shortener"&gt;URL shortener sample&lt;/a&gt;.&lt;/p&gt;</description><author>Donna Malayeri</author><category>javascript</category><category>serverless</category><category>aws</category></item><item><title>Build a Video Thumbnailer on AWS</title><link>https://www.pulumi.com/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/</link><pubDate>Thu, 21 Jun 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/index.png" /&gt;
&lt;p&gt;Pulumi makes it easy to build cloud applications that use a combination
of containers, lambdas, and connected data services and infrastructure:
Colada apps.&lt;/p&gt;
&lt;p&gt;An example of a Colada app is extracting a thumbnail from a video. A
serverless function can only run for 5 minutes, so we&amp;rsquo;ll run a
container in AWS Fargate to do the video processing.&lt;/p&gt;
&lt;p&gt;In this app, a Lambda function is triggered whenever a new video is
uploaded to S3. This function launches a task in Fargate that
uses &lt;a href="https://www.ffmpeg.org/"&gt;FFmpeg&lt;/a&gt; to extract a video thumbnail. A
second Lambda function is triggered when a new thumbnail has been
created.&lt;/p&gt;
&lt;p&gt;This post was inspired by a blog post from Serverless, Inc that shows
how to combine &lt;a href="https://serverless.com/blog/serverless-application-for-long-running-process-fargate-lambda/"&gt;AWS Fargate and Lambda for a long-running
process&lt;/a&gt;.
Let&amp;rsquo;s see how a similar app would be implemented in Pulumi entirely in
code.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/video-thumbnail-diagram.png" alt="video-thumbnail-diagram"&gt;&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;If this is your first time using Pulumi, go
to &lt;a href="https://app.pulumi.com/signup"&gt;https://app.pulumi.com&lt;/a&gt; and sign in with
GitHub.&lt;/p&gt;
&lt;p&gt;Then, run the following command to install the Pulumi CLI:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -fsSL https://get.pulumi.com/ | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&amp;rsquo;re on Windows, run this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@&amp;quot;%SystemRoot%System32WindowsPowerShell1.0powershell.exe&amp;quot; -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command &amp;quot;iex ((New-Object System.Net.WebClient).DownloadString('https://get.pulumi.com/install.ps1'))&amp;quot;
SET &amp;quot;PATH=%PATH%;%USERPROFILE%.pulumiin&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&amp;rsquo;ll deploy this app to your own AWS account, so follow the steps
to &lt;a href="https://www.pulumi.com/registry/packages/aws/installation-configuration/"&gt;configure your AWS account&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Make sure you have &lt;a href="https://nodejs.org/en/download/"&gt;Node.js&lt;/a&gt; installed,
with a version of 6.10.x or later.&lt;/p&gt;
&lt;p&gt;Finally, make sure &lt;a href="https://docs.docker.com/install/"&gt;Docker&lt;/a&gt; is
installed and running.&lt;/p&gt;
&lt;h2 id="create-the-app"&gt;Create the App&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll create a Pulumi project, define the infrastructure and app code
in JavaScript, and create a Dockerfile for the Fargate task.&lt;/p&gt;
&lt;p&gt;First, to create a new Pulumi project, run the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir hello-colada &amp;amp;&amp;amp; cd hello-colada
pulumi new aws-javascript
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a new project in the &lt;code&gt;hello-colada&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Second, replace the contents of &lt;code&gt;index.js&lt;/code&gt; with the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cloud&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/cloud-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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// A bucket to store videos and thumbnails.
&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;bucket&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;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bucket&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bucketName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// A task which runs a containerized FFMPEG job to extract a thumbnail image.
&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;ffmpegThumbnailTask&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;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ffmpegThumbTask&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;build&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 class="c1"&gt;// folder containing the Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;memoryReservation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="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;// When a new video is uploaded, run the FFMPEG task on the video file.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Use the time index specified in the filename (e.g. cat_00-01.mp4 uses timestamp 00:01)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onPut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;onNewVideo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucketArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`*** New video: file &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bucketArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; was uploaded at &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bucketArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventTime&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bucketArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span 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;thumbnailFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substring&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 class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.jpg&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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;framePos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&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 class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;:&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="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ffmpegThumbnailTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="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;environment&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="s2"&gt;&amp;#34;S3_BUCKET&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="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;INPUT_VIDEO&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="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;TIME_OFFSET&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;framePos&lt;/span&gt;&lt;span class="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;OUTPUT_FILE&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;thumbnailFile&lt;/span&gt;&lt;span class="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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Running thumbnailer task.`&lt;/span&gt;&lt;span class="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;keySuffix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.mp4&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;// When a new thumbnail is created, log a message.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onPut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;onNewThumbnail&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucketArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`*** New thumbnail: file &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bucketArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; was saved at &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bucketArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventTime&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 class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;keySuffix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.jpg&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;// Export the bucket name.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucketName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bucketName&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 code uses &lt;code&gt;cloud.Task&lt;/code&gt;, a high-level, convenient component for
working with containers. The component automatically provisions a
container registry instance in ECR, runs a Docker build, and saves the
Docker image to the provisioned ECR instance. It also defines an ECS
task and configures it to use the built image. All this in just 4 lines
of code!&lt;/p&gt;
&lt;p&gt;Next, in the same directory, create a &lt;code&gt;Dockerfile&lt;/code&gt; with the following
contents. We&amp;rsquo;ll use an existing FFmpeg container and install the AWS
CLI. When the container is started, it copies the video file from S3,
runs &lt;code&gt;ffmpeg&lt;/code&gt;, and copies the output back to S3.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; FROM jrottenberg/ffmpeg
RUN apt-get update &amp;amp;&amp;amp;
apt-get install python-dev python-pip -y &amp;amp;&amp;amp;
apt-get clean
RUN pip install awscli
WORKDIR /tmp/workdir
ENTRYPOINT
echo &amp;quot;Starting ffmpeg task...&amp;quot; &amp;amp;&amp;amp;
echo &amp;quot;Copying video from S3&amp;quot; &amp;amp;&amp;amp;
aws s3 cp s3://${S3_BUCKET}/${INPUT_VIDEO} ./${INPUT_VIDEO} &amp;amp;&amp;amp;
ffmpeg -v error -i ./${INPUT_VIDEO} -ss ${TIME_OFFSET} -vframes 1 -f image2 -an -y ${OUTPUT_FILE} &amp;amp;&amp;amp;
echo &amp;quot;Copying thumbnail to S3&amp;quot; &amp;amp;&amp;amp;
aws s3 cp ./${OUTPUT_FILE} s3://${S3_BUCKET}/${OUTPUT_FILE}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the &lt;code&gt;@pulumi/cloud-aws&lt;/code&gt; NPM package:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npm install --save @pulumi/cloud-aws @pulumi/cloud
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, configure Pulumi to use AWS Fargate. Note that, currently,
Fargate is available only in &lt;code&gt;us-east-1&lt;/code&gt;, &lt;code&gt;us-east-2&lt;/code&gt;, &lt;code&gt;us-west-2&lt;/code&gt;,
and &lt;code&gt;eu-west-1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pulumi config set cloud-aws:useFargate true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s it! Now, let&amp;rsquo;s deploy the app.&lt;/p&gt;
&lt;h2 id="deploy-the-app"&gt;Deploy the App&lt;/h2&gt;
&lt;p&gt;To deploy both the infrastructure and app code, we&amp;rsquo;ll
run &lt;code&gt;pulumi update&lt;/code&gt;. This command first shows a preview of all the
resources that will be created and prompts for confirmation. During the
preview phase, Pulumi invokes &lt;code&gt;docker build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Choose the &amp;ldquo;yes&amp;rdquo; option to deploy to AWS. This will take about 5
minutes. Pulumi automatically builds and provisions an AWS container
repository in ECR, builds the Docker container, and places the image in
the repository. This all happens automatically and does not require
manual configuration on your part.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/pulumi-update-output.png" alt="pulumi-update-output"&gt;&lt;/p&gt;
&lt;p&gt;At the end of the update, you&amp;rsquo;ll see a link to the Pulumi Service that
shows the details of the deployment.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/pulumi-update-complete-1.png" alt="pulumi-update-complete-1"&gt;&lt;/p&gt;
&lt;p&gt;Go to the &lt;strong&gt;Resources&lt;/strong&gt; tab and filter to S3 resources. Then, on the S3
bucket, click the link to go to the AWS Console.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/console-s3-filtered.png" alt="console-s3-filtered"&gt;&lt;/p&gt;
&lt;h2 id="upload-a-video"&gt;Upload a Video&lt;/h2&gt;
&lt;p&gt;Upload an .mp4 video to your S3 bucket, making sure to encode the
desired time index in the filename. For instance,
use &lt;code&gt;myvideo_00-02.mp4&lt;/code&gt; to extract the frame at 0 minutes and 2 seconds.&lt;/p&gt;
&lt;p&gt;You can &lt;a href="https://github.com/pulumi/examples"&gt;use this video of my cat&lt;/a&gt;,
which you should save as &lt;code&gt;cat_00-01.mp4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Upload this video in the S3 console. Or, use the AWS CLI along with
the &lt;code&gt;pulumi stack output&lt;/code&gt; command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ aws s3 cp cat_00-01.mp4 s3://$(pulumi stack output bucketName)
upload: cat_00-01.mp4 to s3://bucket-3a4f226/cat_00-01.mp4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see what the app is doing, use the &lt;code&gt;pulumi logs&lt;/code&gt; command, with
the &lt;code&gt;--follow&lt;/code&gt; or &lt;code&gt;-f&lt;/code&gt; parameter. This command aggregates all the logs
for your compute, so the logs for the Lambda function &lt;strong&gt;and&lt;/strong&gt; the
Fargate task are all in one place!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/build-a-video-thumbnailer-with-pulumi-using-lambdas-containers-and-infrastructure-on-aws/terminal-logs.png" alt="terminal-logs"&gt;&lt;/p&gt;
&lt;p&gt;Once the thumbnail has been generated, either view it in the S3 console,
or download it with the AWS CLI:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ aws s3 cp s3://$(pulumi stack output bucketName)/cat.jpg .
download: s3://bucket-0c91106/cat.jpg to ./cat.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="clean-up"&gt;Clean Up&lt;/h2&gt;
&lt;p&gt;To clean up the resources we&amp;rsquo;ve provisioned, run &lt;code&gt;pulumi destroy&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="next-steps"&gt;Next Steps&lt;/h3&gt;
&lt;p&gt;In this post, we saw how easy it is to use containers and serverless
functions in one application. With Pulumi, you get the best of both
worlds, and don&amp;rsquo;t have to choose one or the other.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/pulumi/examples"&gt;sample code for this post&lt;/a&gt; is
available in the &lt;a href="https://github.com/pulumi/examples"&gt;Pulumi examples repo on
GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For a version of this sample that includes AWS Rekognition, see
the &lt;a href="https://github.com/pulumi/examples"&gt;Video Thumbnailer with Machine Learning&lt;/a&gt; JavaScript
example.&lt;/p&gt;</description><author>Donna Malayeri</author><category>javascript</category><category>serverless</category><category>aws</category><category>containers</category></item><item><title>Deploying production-ready containers with Pulumi</title><link>https://www.pulumi.com/blog/deploying-production-ready-containers-with-pulumi/</link><pubDate>Wed, 20 Jun 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/deploying-production-ready-containers-with-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/deploying-production-ready-containers-with-pulumi/index.png" /&gt;
&lt;p&gt;Containers are a great way to deploy applications to the cloud,
especially with new execution models like &lt;a href="https://aws.amazon.com/fargate/"&gt;AWS
Fargate&lt;/a&gt;. Pulumi makes it easy to
deploy production Docker containers, handling details such as creating a
container registry instance in ECR, creating task definitions in ECS,
and configuring a load balancer. With Pulumi, deploying a container to
production is almost as easy as running it locally!&lt;/p&gt;
&lt;p&gt;In this blog post, we&amp;rsquo;ll deploy a simple Docker container running
NGINX.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;If this is your first time using Pulumi, go to
&lt;a href="https://app.pulumi.com/signup"&gt;https://app.pulumi.com&lt;/a&gt; and sign in with
GitHub.&lt;/p&gt;
&lt;p&gt;Then, run the following command to install the Pulumi CLI:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -fsSL https://get.pulumi.com/ | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&amp;rsquo;re on Windows, run this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@&amp;quot;%SystemRoot%System32WindowsPowerShell1.0powershell.exe&amp;quot; -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command &amp;quot;iex ((New-Object System.Net.WebClient).DownloadString('https://get.pulumi.com/install.ps1'))&amp;quot;
SET &amp;quot;PATH=%PATH%;%USERPROFILE%.pulumiin&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&amp;rsquo;ll deploy this app to your own AWS account, so follow the steps to
&lt;a href="https://www.pulumi.com/registry/packages/aws/installation-configuration/"&gt;configure your AWS account&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Make sure you have &lt;a href="https://nodejs.org/en/download/"&gt;Node.js&lt;/a&gt; installed,
with a version of 6.10.x or later.&lt;/p&gt;
&lt;p&gt;Finally, make sure &lt;a href="https://docs.docker.com/install/"&gt;Docker&lt;/a&gt; is
installed and running.&lt;/p&gt;
&lt;h2 id="create-the-app"&gt;Create the app&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll create a Pulumi project, define the infrastructure in JavaScript,
and create a Dockerfile.&lt;/p&gt;
&lt;p&gt;To create a new Pulumi project, run the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir hello-containers &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; hello-containers
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pulumi new aws-javascript
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a new project in the directory &lt;code&gt;hello-containers&lt;/code&gt;. Next, replace the contents of &lt;code&gt;index.js&lt;/code&gt; with the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cloud&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/cloud-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="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;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pulumi-nginx&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;containers&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;nginx&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;build&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="nx"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="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;ports&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="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;replicas&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="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="cm"&gt;/* export just the hostname property of the container frontend */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultEndpoint&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;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sb"&gt;`http://&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These 15 lines of code are everything you need to deploy a custom
container! We&amp;rsquo;re using &lt;code&gt;cloud.Service&lt;/code&gt;, which is a high-level,
convenient interface for building containers and provisioning an AWS
container service. Using the &lt;code&gt;build&lt;/code&gt; property, we point to a folder
containing a &lt;code&gt;Dockerfile&lt;/code&gt;, which is &lt;code&gt;app&lt;/code&gt; in this case.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s create a &lt;code&gt;Dockerfile&lt;/code&gt; and a static page. Create a
subfolder &lt;code&gt;app&lt;/code&gt; with the following files.&lt;/p&gt;
&lt;p&gt;Add the following as &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;FROM nginx
COPY index.html /usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add the following as &lt;code&gt;index.html&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello containers!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Made with ❤️ with &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://pulumi.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Pulumi&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should have the following directory structure:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/deploying-production-ready-containers-with-pulumi/file-layout.png" alt="file layout"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install the &lt;code&gt;@pulumi/cloud-aws&lt;/code&gt; NPM package:&lt;/p&gt;
&lt;p&gt;npm install &amp;ndash;save @pulumi/cloud-aws @pulumi/cloud&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure Pulumi to use AWS Fargate. Note that, currently, Fargate is
available only in &lt;code&gt;us-east-1&lt;/code&gt;, &lt;code&gt;us-east-2&lt;/code&gt;, &lt;code&gt;us-west-2&lt;/code&gt;, and
&lt;code&gt;eu-west-1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;pulumi config set cloud-aws:useFargate true&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="deploy-the-app"&gt;Deploy the app&lt;/h2&gt;
&lt;p&gt;To deploy both the infrastructure and app code, we&amp;rsquo;ll run
&lt;code&gt;pulumi update&lt;/code&gt;. This command first shows a preview of all the resources
that will be created and prompts for confirmation. During the preview
phase, Pulumi invokes &lt;code&gt;docker build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/deploying-production-ready-containers-with-pulumi/pulumi-update-preview.png" alt="Pulumi preview"&gt;&lt;/p&gt;
&lt;p&gt;Choose the &lt;code&gt;yes&lt;/code&gt; option to deploy to AWS. This will take about 5
minutes. Pulumi automatically builds and provisions an AWS container
repository in ECR, builds the Docker container, and places the image in
the repository. This all happens automatically and does not require
manual configuration on your part.&lt;/p&gt;
&lt;p&gt;At the end of the update, you&amp;rsquo;ll see a link to the Pulumi Service that
shows the details of the deployment.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/deploying-production-ready-containers-with-pulumi/update-complete.png" alt="update complete"&gt;&lt;/p&gt;
&lt;p&gt;Go to this link and click the &lt;strong&gt;Resources&lt;/strong&gt; tab. You&amp;rsquo;ll see all the
resources you&amp;rsquo;ve created. Notice that Pulumi has created an ECR
repository, a load balancer, an ECS service and task definition, and IAM
roles.&lt;/p&gt;
&lt;p&gt;Now, go back to the stack details page, which shows the stack
configuration, as well as the stack output property. The following line
creates the stack output &lt;code&gt;url&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;exports.url = service.defaultEndpoint.apply(e =&amp;gt; `http://${e.hostname}`);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you navigate to the link for &lt;code&gt;url&lt;/code&gt; you&amp;rsquo;ll see the following page:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/deploying-production-ready-containers-with-pulumi/hello-world-page.png" alt="&amp;ldquo;Hello, World!&amp;rdquo;"&gt;&lt;/p&gt;
&lt;h2 id="view-logs"&gt;View logs&lt;/h2&gt;
&lt;p&gt;You can view container frontend and compute logs via the &lt;code&gt;pulumi logs&lt;/code&gt;
command. You can see here that most of the traffic is hitting routes
that don&amp;rsquo;t exist, aside from the last log line where I navigated to the
site using Chrome. Since this is an HTTP server that&amp;rsquo;s open to the
internet, it&amp;rsquo;s not uncommon for attackers to try to find a security
vulnerability on a site. Fortunately, our container is serving only
static content. The logs command makes it easy to get details on your
site traffic.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pulumi logs
Collecting logs for stack hello-containers-dev since 2018-06-15T15:01:18.000-07:00.
2018-06-15T15:27:47.468-07:00[ pulumi-nginx] 2018/06/15 22:27:47 [error] 5#5: *4 open() &amp;quot;/usr/share/nginx/html/proxychecker.axd&amp;quot; failed (2: No such file or directory), client: 62.210.157.152, server: localhost, request: &amp;quot;GET http://proxy.dazhou.net/proxychecker.axd?e=457758511%40qq.com&amp;amp;p=http%3A%2F%2F34.201.27.186%3A80&amp;amp;s=AS HTTP/1.1&amp;quot;, host: &amp;quot;proxy.dazhou.net&amp;quot;
2018-06-15T15:27:47.468-07:00[ pulumi-nginx] 62.210.157.152 - - [15/Jun/2018:22:27:47 +0000] &amp;quot;GET http://proxy.dazhou.net/proxychecker.axd?e=457758511%40qq.com&amp;amp;p=http%3A%2F%2F34.201.27.186%3A80&amp;amp;s=AS HTTP/1.1&amp;quot; 404 170 &amp;quot;-&amp;quot; &amp;quot;Go-http-client/1.1&amp;quot; &amp;quot;-&amp;quot;
2018-06-15T15:32:02.128-07:00[ pulumi-nginx] 2018/06/15 22:32:02 [error] 5#5: *382 &amp;quot;/usr/share/nginx/html/phpmyadmin/index.html&amp;quot; is not found (2: No such file or directory), client: 178.239.177.212, server: localhost, request: &amp;quot;HEAD http://34.201.27.186:80/phpmyadmin/ HTTP/1.1&amp;quot;, host: &amp;quot;34.201.27.186&amp;quot;
2018-06-15T16:07:05.874-07:00[ pulumi-nginx] 172.31.44.144 - - [15/Jun/2018:23:07:05 +0000] &amp;quot;GET / HTTP/1.1&amp;quot; 304 0 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36&amp;quot; &amp;quot;-&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="clean-up"&gt;Clean up&lt;/h2&gt;
&lt;p&gt;To clean up the resources we&amp;rsquo;ve provisioned, run &lt;code&gt;pulumi destroy&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next steps&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/pulumi/examples/tree/5f3eae87bb132595e4c60d2d5f8e8ee1473f6a7b/cloud-js-containers"&gt;sample code for this post&lt;/a&gt;
is available in the &lt;a href="https://github.com/pulumi/examples"&gt;Pulumi examples repo on GitHub&lt;/a&gt;. For an example application
that connects two containers, see the &lt;a href="https://github.com/pulumi/examples/tree/5f3eae87bb132595e4c60d2d5f8e8ee1473f6a7b/cloud-ts-voting-app"&gt;Voting App&lt;/a&gt;
TypeScript sample.&lt;/p&gt;</description><author>Donna Malayeri</author><category>javascript</category><category>aws</category><category>containers</category></item></channel></rss>