<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Cyrus Najmabadi</title><link>https://www.pulumi.com/blog/author/cyrus-najmabadi/</link><description>Pulumi blog posts: Cyrus Najmabadi.</description><language>en-us</language><pubDate>Tue, 25 Feb 2020 00:00:00 +0000</pubDate><item><title>Scheduling Serverless</title><link>https://www.pulumi.com/blog/scheduling-serverless/</link><pubDate>Tue, 25 Feb 2020 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/scheduling-serverless/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/scheduling-serverless/index.png" /&gt;
&lt;p&gt;Scheduling events has long been an essential part of automation; many tasks need to run at specific times or intervals. You could be checking StackOverflow for new questions every 20 minutes or compiling a report that is emailed every other Friday at 4:00 pm. Today, many of these tasks can be efficiently accomplished in the cloud. While each cloud has its flavor of scheduled functions, this post steps you through an example using &lt;a href="https://aws.amazon.com/cloudwatch/"&gt;AWS CloudWatch&lt;/a&gt; with the help of Pulumi.&lt;/p&gt;
&lt;h2 id="taking-out-the-trash"&gt;Taking out the trash&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s assume we have an S3 Bucket similar to the Recycle Bin on your desktop. It contains discarded items that we might not be ready to delete yet, but because we are paying for storage, we want to empty this bucket every Friday at 6:00 pm EST.&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ObjectIdentifier&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;aws-sdk/clients/s3&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 an AWS resource (S3 Bucket)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trashBucket&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;trash&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;We’re now ready to create our Lambda function and schedule it. Pulumi simplifies creating Lambda functions by allowing us to provide a handler function to an event such as &lt;code&gt;cloudwatch.onSchedule&lt;/code&gt;. To learn more, check out our &lt;a href="https://www.pulumi.com/blog/lambdas-as-lambdas-the-magic-of-simple-serverless-functions/"&gt;Lambdas as Lambdas&lt;/a&gt; post. Let’s create a &lt;code&gt;cloudwatch.EventRuleEventHandler&lt;/code&gt; async function that will get all the items from the trash bucket and delete them.&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;// A handler function that will list objects in the bucket and bulk delete them
&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;emptyTrash&lt;/span&gt;: &lt;span class="kt"&gt;aws.cloudwatch.EventRuleEventHandler&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;event&lt;/span&gt;: &lt;span class="kt"&gt;aws.cloudwatch.EventRuleEvent&lt;/span&gt;
&lt;/span&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="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;s3Client&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 class="c1"&gt;//creates interface to 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;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;trashBucket&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 class="kr"&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&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;Contents&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 class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt; &lt;span class="c1"&gt;//get list of objects in bucket
&lt;/span&gt;&lt;/span&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;listObjects&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;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 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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objects&lt;/span&gt;: &lt;span class="kt"&gt;ObjectIdentifier&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Contents&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="kt"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Key&lt;/span&gt;: &lt;span class="kt"&gt;object.Key&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt; &lt;span class="c1"&gt;//delete objects
&lt;/span&gt;&lt;/span&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;deleteObjects&lt;/span&gt;&lt;span class="p"&gt;({&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&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Delete&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Objects&lt;/span&gt;: &lt;span class="kt"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Quiet&lt;/span&gt;: &lt;span class="kt"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="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 class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;error&lt;/span&gt;&lt;span class="p"&gt;));&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sb"&gt;`Deleted &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Contents&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; item&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Contents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&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="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="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; from &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bucket&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 class="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 that we have our handler function, we can create a CloudWatch event that fires based on the specified schedule, which is every Friday at 6:00 pm EST. We’ll need to represent that as a Schedule Expression. CloudWatch events support cron and rate expressions. If we wanted our function to run on a set interval (i.e., every 20 minutes), we would choose a rate expression. Since we want fine-grained schedule control, we’ll be using a cron expression. You can learn more at the &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html"&gt;Schedule Expressions documentation&lt;/a&gt;. Because time for cron jobs uses UTC, we’ll need to schedule our event to fire on Friday at 11:00 pm UTC.&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;// Schedule the function to run every Friday at 11:00pm UTC (6:00pm EST)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// More info on Schedule Expressions at
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html
&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;emptyTrashSchedule&lt;/span&gt;: &lt;span class="kt"&gt;aws.cloudwatch.EventRuleEventSubscription&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;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onSchedule&lt;/span&gt;&lt;span class="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;emptyTrash&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;cron(0 23 ? * FRI *)&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;emptyTrash&lt;/span&gt;&lt;span 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 name of the bucket
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&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;trashBucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, run &lt;code&gt;pulumi up&lt;/code&gt; to deploy your new scheduled function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ pulumi up
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Previewing update &lt;span class="o"&gt;(&lt;/span&gt;dev&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;…
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Updating &lt;span class="o"&gt;(&lt;/span&gt;dev&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Type Name Status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; pulumi:pulumi:Stack aws-ts-scheduled-function-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + └─ aws:cloudwatch:EventRuleEventSubscription emptyTrash created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ├─ aws:cloudwatch:EventRule emptyTrash created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ├─ aws:iam:Role emptyTrash created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ├─ aws:iam:RolePolicyAttachment emptyTrash-32be53a2 created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ├─ aws:lambda:Function emptyTrash created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ├─ aws:lambda:Permission emptyTrash created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + └─ aws:cloudwatch:EventTarget emptyTrash created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Outputs:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; bucketName: &lt;span class="s2"&gt;&amp;#34;trash-1cde441&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Resources:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + &lt;span class="m"&gt;7&lt;/span&gt; created
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;2&lt;/span&gt; unchanged
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Duration: 14s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Test it out by adding some items to your bucket and see if they’re deleted. You can use &lt;code&gt;pulumi logs&lt;/code&gt; to view the &lt;code&gt;console.log&lt;/code&gt; from the handler function. If you can’t wait until Friday, experiment with changing the cron expression and rerun &lt;code&gt;pulumi up&lt;/code&gt;. You can find the &lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-scheduled-function"&gt;full code&lt;/a&gt; in our GitHub repository.&lt;/p&gt;
&lt;h2 id="want-to-know-more"&gt;Want to know more?&lt;/h2&gt;
&lt;p&gt;This simple example demonstrates what you can do with serverless functions, such as AWS Lambda, without having to set up your infrastructure. Check out more serverless examples in the &lt;a href="https://github.com/pulumi/examples"&gt;Pulumi Examples&lt;/a&gt; to see what you can build.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pulumi/examples/tree/master/classic-azure-ts-serverless-url-shortener-global"&gt;Serverless URL Shortener on Azure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pulumi/examples/tree/master/gcp-py-functions"&gt;GCP function to SMS friends&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-s3-lambda-copyzip"&gt;Serverless App to Copy and Zip S3 Objects Between Buckets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><author>Cyrus Najmabadi</author><category>serverless</category><category>lambda</category></item><item><title>Building and Publishing Docker Images to an ECR Repository</title><link>https://www.pulumi.com/blog/building-and-publishing-docker-images-to-a-private-amazon-ecr-repository/</link><pubDate>Tue, 18 Jun 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/building-and-publishing-docker-images-to-a-private-amazon-ecr-repository/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/building-and-publishing-docker-images-to-a-private-amazon-ecr-repository/index.png" /&gt;
&lt;div class="note note-warning"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#p-warning-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;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;p&gt;Amazon Elastic Container Registry (&lt;a href="https://aws.amazon.com/ecr/"&gt;ECR&lt;/a&gt;)
is a fully-managed Docker container registry that makes it easy for
developers to store, manage, and deploy Docker container images. ECR is
integrated with Amazon Elastic Container Service
(&lt;a href="https://aws.amazon.com/ecs/"&gt;ECS&lt;/a&gt;), including for Kubernetes
(&lt;a href="https://aws.amazon.com/eks"&gt;EKS&lt;/a&gt;), simplifying your development to
production workflow, securing access through IAM, and eliminating the
need to operate your own container repositories or worry about scaling
the underlying infrastructure. ECR hosts your images in a highly
available and scalable architecture, allowing you to reliably deploy
containers for your applications. In this article, we&amp;rsquo;ll see how
&lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;Pulumi Crosswalk for AWS&lt;/a&gt; lets you use
infrastructure as code to easily build, publish, and pull from private
ECR repositories.&lt;/p&gt;
&lt;h2 id="a-simple-ecs-fargate-service"&gt;A Simple ECS Fargate Service&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s see how using Pulumi you can provision an ECR repository, and
build and publish to it, in just a few lines of code. Additional
features such as lifecycle management make it easy to declare policies
specifying how and when stale images should be dropped. Built images can
then be referenced from your ECS services (whether
&lt;a href="https://aws.amazon.com/ec2/"&gt;EC2,&lt;/a&gt; &lt;a href="https://aws.amazon.com/fargate/"&gt;Fargate&lt;/a&gt;,
or &lt;a href="https://aws.amazon.com/eks"&gt;EKS&lt;/a&gt;), allowing you to easily version
both your images and your infrastructure with one simple, auditable,
system.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at this in action. First, we&amp;rsquo;ll just start with a
simple Pulumi program that creates a load balanced Fargate Service that
accessible to the internet, but uses the &lt;em&gt;public&lt;/em&gt; &lt;code&gt;nginx&lt;/code&gt; container
image from the Docker Hub:&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;// A simple NGINX service, scaled out over two containers.
&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;nginx&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;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;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;desiredCount&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;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;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;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nginx&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="kt"&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;portMappings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&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;elasticloadbalancingv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApplicationListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;nginx&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&gt;&lt;/span&gt;&lt;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;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nginxEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nginxListener&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this give us:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Updating (teststack):
Type Name Status
+ pulumi:pulumi:Stack teststack created
+ ├─ awsx:x:elasticloadbalancingv2:ApplicationLoadBalancer nginx created
+ │ ├─ awsx:x:elasticloadbalancingv2:ApplicationTargetGroup nginx created
+ │ │ └─ aws:elasticloadbalancingv2:TargetGroup nginx created
+ │ ├─ awsx:x:elasticloadbalancingv2:ApplicationListener nginx created
... more output trimmed ***
Outputs:
nginxEndpoint: { hostname: &amp;quot;********&amp;quot;, port: 80 }
Resources:
+ 36 created
Duration: 3m19s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have trimmed the output, since this simple program provisioned 36 AWS
resources, connected them accordingly, and stood up a load balanced ECS
Fargate service that automatically uses cloud deployment best practices.&lt;/p&gt;
&lt;p&gt;Here, through the use of &lt;code&gt;image: &amp;quot;nginx&amp;quot;&lt;/code&gt; we are letting ECS know that
we want to use &lt;a href="https://hub.docker.com/_/nginx/"&gt;this&lt;/a&gt; publicly
available Docker Hub image. ECS supports pulling these images for you by
default and making them available to your services. If you&amp;rsquo;re using a
public image, this works great and no further work is necessary on your
part.&lt;/p&gt;
&lt;h2 id="using-a-private-ecr-repository"&gt;Using a Private ECR Repository&lt;/h2&gt;
&lt;p&gt;There are many times, however, when you or your organization may not
want to use public images, such as private applications that aren&amp;rsquo;t
meant to be shared with the Internet. The Docker Hub supports private
images, however, if you&amp;rsquo;re already building on AWS, Amazon ECR is a
valuable service that allows you to host those images in your AWS
account, leveraging IAM for secure authentication, and ensuring easy,
fast and secure access from your containers.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see what that looks like with Pulumi. First, we&amp;rsquo;ll create a simple
&lt;code&gt;Dockerfile&lt;/code&gt; that starts with the base
&lt;a href="https://hub.docker.com/_/nginx/"&gt;nginx&lt;/a&gt; image and slightly modifies it
to contain our own custom &lt;code&gt;index.html&lt;/code&gt; file:&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FROM nginx
COPY content/index.html /usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;index.html&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h1&amp;gt; Hi from Pulumi! &amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&amp;rsquo;s see how we&amp;rsquo;d update our Pulumi app to build this custom
Docker image, push it to ECR, get the resulting image name, and
reference it from our ECS service (it works the same in both ECS and
EKS):&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;// common code from before trimmed 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;repository&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;ecr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;repo&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;forceDelete&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;// Invoke &amp;#39;docker&amp;#39; to actually build the DockerFile that is in the &amp;#39;app&amp;#39; folder relative to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// this program. Once built, push that image up to our personal ECR repo.
&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;image&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;ecr&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="s2"&gt;&amp;#34;image&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;repositoryUrl&lt;/span&gt;: &lt;span class="kt"&gt;repository.url&lt;/span&gt;&lt;span class="p"&gt;,&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="sb"&gt;`./app`&lt;/span&gt;&lt;span class="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;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;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;service&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ... common code from before trimmed out
&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;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;service&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;image&lt;/span&gt;&lt;span class="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 let&amp;rsquo;s see what happens when we actually try to run this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/building-and-publishing-docker-images-to-a-private-amazon-ecr-repository/pulumi-update.gif" alt="Pulumi update in progress"&gt;&lt;/p&gt;
&lt;p&gt;As you can see Pulumi is actually launching the real &lt;code&gt;docker&lt;/code&gt; executable
locally to use the &lt;code&gt;Dockerfile&lt;/code&gt; to build the image. As &lt;code&gt;docker&lt;/code&gt; runs,
the output is captured and automatically shown in the real-time Pulumi
update display. When the image is finished building, it is pushed by
&lt;code&gt;docker&lt;/code&gt; itself to the ECR repo. Pulumi safely passes temporary repo
credentials to the &lt;code&gt;docker&lt;/code&gt; executable so it can login and push the
image up. Finally, once available in ECR, the task-definition and
service are appropriately updated to now reference this new image. ECS
will then ensure that the old services are spun down and the new
services are spun up.&lt;/p&gt;
&lt;p&gt;TL;DR, just run &lt;code&gt;pulumi up&lt;/code&gt;, and it takes care of all the heavy lifting.
In the end we see:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/building-and-publishing-docker-images-to-a-private-amazon-ecr-repository/container-def.png" alt="summary results"&gt;&lt;/p&gt;
&lt;p&gt;In less than 30 seconds, Pulumi and Docker built the private image, made
it available on ECR, and properly moved the Service over to using it.
This was all done with a single command, with Pulumi smartly figuring
out at the end of the day exactly what changes needed to be made. From
the above we can see just the creation of the Repository components, and
the updates of the Service to now use it. A nice minimal change that
exactly matches our intuition around what would happen. If necessary,
the &lt;code&gt;awsx.ecr.Image&lt;/code&gt; resource can also take many more options to
control what&amp;rsquo;s happening with &lt;code&gt;docker&lt;/code&gt;. Options around tagging and
caching can be configured, and the &lt;code&gt;docker&lt;/code&gt; command line can also just
be augmented if necessary to handle advanced scenarios.&lt;/p&gt;
&lt;h2 id="managing-ecr-lifecycle-policies"&gt;Managing ECR Lifecycle Policies&lt;/h2&gt;
&lt;p&gt;In practice, an organization may be producing and uploading many images
to their private ECR repositories. This can add up in costs as time goes
on, especially when old images are stored that will never be used again.
To aid with this, Pulumi makes it easy to set up &lt;a href="https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html"&gt;ECR Lifecycle Policies&lt;/a&gt;
to control the lifetime of your images and to easily purge unneeded
images based on flexible criteria to meet your needs.&lt;/p&gt;
&lt;p&gt;As a simple example, here&amp;rsquo;s a way to just remove any untagged images
that are older than one week old:&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;// common code from before trimmed 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;repository&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;ecr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;repo&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;lifeCyclePolicyArgs&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;rules&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;selection&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;untagged&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;maximumAgeLimit&lt;/span&gt;: &lt;span class="kt"&gt;7&lt;/span&gt;&lt;span class="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;Now, you can keep your last two weeks of images around if you want them
with all tagged images (like &lt;code&gt;'latest'&lt;/code&gt;) being preserved. You can
continue pushing rapidly to your repo without having to worry about
manually going and cleaning up the stale garbage in the future.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ve shown how &lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;Pulumi Crosswalk for AWS&lt;/a&gt;
can create a tight developer inner
loop for building, publishing, and consuming Docker images, using
private ECR repositories, while keeping all your ECS or EKS services and
tasks updated properly and securely. This can be done with flexible
policies to ensure that your repos contain the images you care about and
don&amp;rsquo;t keep holding onto images you no longer need, and is all possible
from a single Pulumi app with simple commands to keep everything in
sync.&lt;/p&gt;
&lt;p&gt;Pulumi is open source and free to use. For more information on Getting
Started, check out:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/get-started/aws/"&gt;AWS QuickStart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/introducing-pulumi-crosswalk-for-aws-the-easiest-way-to-aws/"&gt;Pulumi Crosswalk for AWS Announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/mapbox-iot-as-code-with-pulumi-crosswalk-for-aws/"&gt;Mapbox IOT-as-Code with Pulumi Crosswalk for AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;Pulumi Crosswalk for AWS Documentation for ECS, EKS, ELB, and more&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We think there&amp;rsquo;s no easier way to do containers in a tight inner
development loop, and we hope you agree!&lt;/p&gt;</description><author>Cyrus Najmabadi</author><category>kubernetes</category></item><item><title>AWS CloudWatch made easy with Pulumi Infrastructure-as-Code</title><link>https://www.pulumi.com/blog/aws-cloudwatch-made-easy-with-pulumi-infrastructure-as-code/</link><pubDate>Fri, 14 Jun 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/aws-cloudwatch-made-easy-with-pulumi-infrastructure-as-code/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/aws-cloudwatch-made-easy-with-pulumi-infrastructure-as-code/index.png" /&gt;
&lt;p&gt;Pulumi Crosswalk for AWS modules can be used to get first class insights
and visualizations directly inside your Pulumi application.&lt;/p&gt;
&lt;p&gt;As cloud applications tend to be long-lived, we think it&amp;rsquo;s vital that it
be possible to get regular insights on the performance of the
application at all times. Using
&lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;Crosswalk for AWS&lt;/a&gt; Pulumi applications
allow you to easily define and visualize the appropriate
&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/working_with_metrics.html"&gt;metrics&lt;/a&gt;
that show the health of your services, create
&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html"&gt;alarms&lt;/a&gt;
to let you know when something is wrong, and easily create
&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Dashboards.html"&gt;dashboards&lt;/a&gt;
to get live visualization of what is happening in the cloud. Because
this is vital to the health of the application, we think this should be
something built in from the start, and not something added after the
fact as an out of band artifact.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see what that means in practice! I&amp;rsquo;m going to use a recent
hackathon project I created where I built a &lt;a href="https://api.slack.com/bot-users"&gt;Slack bot&lt;/a&gt; to notify me about all the
&lt;code&gt;@mentions&lt;/code&gt; I&amp;rsquo;d received. Using this, I&amp;rsquo;d have a place I could go back
to to look at, to find messages I knew had come in, but maybe I wanted
to take a second glance at. The bot just sends me messages in a private
channel we share like so:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/aws-cloudwatch-made-easy-with-pulumi-infrastructure-as-code/mention-bot.png" alt="Mention Bot 9000"&gt;&lt;/p&gt;
&lt;p&gt;I intend to add more functionality to this bot over time to help me out.
If you&amp;rsquo;d like to create something similar, the code and instructions are
&lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-slackbot"&gt;here&lt;/a&gt;.
The bot works by hearing about incoming events (pushed to
it from Slack&amp;rsquo;s servers), looking for mentions of my name, then pushing
requests back to Slack&amp;rsquo;s server to update our private channel.&lt;/p&gt;
&lt;p&gt;Now the bot I wrote has to do something interesting due to restrictions
in the Slack messaging system. Slack will only give you a few seconds to
deal with a message they send you. If you don&amp;rsquo;t complete the request in
a timely fashion then they&amp;rsquo;ll assume something has gone wrong, and
they&amp;rsquo;ll resend. This will happen a few times before they just give up.
This means that we don&amp;rsquo;t really want to try to process a message, and
then also make an outgoing network call to Slack all in the same
request. That outgoing call could take far too long, causing Slack to
think there was a problem. So, instead, we simply receive the request,
and push it right away to an &lt;a href="https://aws.amazon.com/sns/"&gt;SNS&lt;/a&gt; topic.
We can then process that topic message later with another Lambda.&lt;/p&gt;
&lt;p&gt;The skeleton of the code basically looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Location to store the incoming slack messages to.
const messageTopic = new aws.sns.Topic(&amp;quot;messages&amp;quot;);
// Create an API endpoint that slack will use to push events to us with.
const endpoint = new awsx.apigateway.API(&amp;quot;mentionbot&amp;quot;, {
routes: [{
path: &amp;quot;/events&amp;quot;,
method: &amp;quot;POST&amp;quot;,
eventHandler: async (event) =&amp;gt; {
// process the different types of messages. Push normal messages to 'messageTopic'
// ...
// Return success quickly so that Slack doesn't just immediately resend this message to us.
return { statusCode: 200, body: &amp;quot;&amp;quot; };
},
}],
});
// Hook up a lambda that will then process the topic when possible.
const subscription = messageTopic.onEvent(&amp;quot;processTopicMessage&amp;quot;, async ev =&amp;gt; {
// process the messages in here. Look for mentions, then notify slack to post a message in
// right channel if we find one.
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Overall, this is a very simple app. But how do we know if the app is
actually doing well? What if something starts to go wrong and the
&lt;code&gt;endpoint&lt;/code&gt; is no longer responding in a timely fashion. How would we
find out? It turns out to be pretty simple to add this functionality
here! Let&amp;rsquo;s see how that would look.&lt;/p&gt;
&lt;p&gt;First, we want to be profiling the &lt;a href="https://aws.amazon.com/lambda/"&gt;Lambda Functions&lt;/a&gt; that were created by those
two resources. That&amp;rsquo;s very simple to do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const endpointFunc = endpoint.getFunction(&amp;quot;/events&amp;quot;);
const topicFunc = subscription.func;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we want to get information about how long these functions are
taking. With Pulumi&amp;rsquo;s
&lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;Crosswalk&lt;/a&gt; APIs it&amp;rsquo;s simple
to get at this information. Inside all the main &amp;ldquo;Crosswalk for AWS&amp;rdquo;
modules are exposed
&lt;a href="https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/awsx/classic/cloudwatch/#Metric"&gt;Metrics&lt;/a&gt;
for almost anything you might ever need. In this case, we want to know
how long those Functions are taking so we can get that information as
follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const endpointFuncDuration = awsx.lambda.metrics.duration({ function: endpointFunc });
const topicProcessDuration = awsx.lambda.metrics.duration({ function: topicFunc });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Metrics are very customizable. But for now we&amp;rsquo;ll just use the defaults
returned for those functions by AWS. Now that we have the metrics, it&amp;rsquo;s
simple to create an CloudWatch alarm that will alert us when things take
too long:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Notify us if takes us longer than 5 seconds to process a Slack message. But only notify if this
// happened at least once every 5 minutes in a 15 minute period. We're ok with the occasional
// random network hiccup causing delays.
const alarm = endpointFuncDuration.withUnit(&amp;quot;Seconds&amp;quot;)
.withPeriod(300)
.createAlarm(&amp;quot;TooLong&amp;quot;, {
threshold: 5,
evaluationPeriods: 3,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we can use both those metrics &lt;em&gt;and&lt;/em&gt; the alarm we just created
to make a great looking dashboard that we can use to get an overall view
of the health of this app:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const dashboard = new awsx.cloudwatch.Dashboard(&amp;quot;mentionbot&amp;quot;, {
widgets: [
new awsx.cloudwatch.LineGraphMetricWidget({
width: 12,
title: &amp;quot;Receiving duration&amp;quot;,
metrics: [ endpointFuncDuration.with({ extendedStatistic: 99, label: &amp;quot;Duration p99&amp;quot; }) ],
annotations: new awsx.cloudwatch.HorizontalAnnotation(alarm),
}),
new awsx.cloudwatch.LineGraphMetricWidget({
width: 12,
title: &amp;quot;Processing duration&amp;quot;,
metrics: [ topicProcessDuration.with({ extendedStatistic: 99, label: &amp;quot;Duration p99&amp;quot; }) ],
}),
],
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href="https://github.com/pulumi/pulumi-awsx/tree/master/awsx-classic/cloudwatch#dashboards"&gt;Crosswalk for AWS API for dashboards&lt;/a&gt;
gives a lot of options for building up a dashboard programmatically.
There are a wealth of Widgets and Annotations to expose the full power
of Cloudwatch&amp;rsquo;s dashboards to your app in a simple to use manner.&lt;/p&gt;
&lt;p&gt;With the above Dashboard code we get this useful chart:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/aws-cloudwatch-made-easy-with-pulumi-infrastructure-as-code/dashboard.png" alt="CloudWatch Dashboard using Pulumi"&gt;&lt;/p&gt;
&lt;p&gt;We can see that things look healthy, and the chart shows us both the
information about our p99 latencies, as well as the threshold point that
would cause our alarm to trigger.&lt;/p&gt;
&lt;p&gt;With these facilities I now have an app that does what I need, but also
directly exposes mechanisms to track and get insights to its health.
What&amp;rsquo;s great is that if I now change my app (for example, in a way that
causes new Lambda Functions to be created), I don&amp;rsquo;t need to do anything
to keep these alarms and dashboards working. Because they&amp;rsquo;re directly
referencing the real Cloud resources they will be kept up to date
automatically as my app grows and changes over time. That&amp;rsquo;s really
powerful, and much simpler than having to manage all these different
parts independently!&lt;/p&gt;
&lt;p&gt;We hope these new Crosswalk for AWS APIs will be just as useful for you!
For more information on Pulumi Crosswalk for AWS checkout other related
content:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/introducing-pulumi-crosswalk-for-aws-the-easiest-way-to-aws/"&gt;Pulumi Crosswalk for AWS Announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/blog/mapbox-iot-as-code-with-pulumi-crosswalk-for-aws/"&gt;Mapbox IOT-as-Code with Pulumi Crosswalk for AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/"&gt;Pulumi Crosswalk for AWS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description><author>Cyrus Najmabadi</author><category>aws</category><category>logging</category></item><item><title>Simple Serverless programming with Google Cloud Functions</title><link>https://www.pulumi.com/blog/simple-serverless-programming-with-google-cloud-functions-and-pulumi/</link><pubDate>Wed, 10 Apr 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/simple-serverless-programming-with-google-cloud-functions-and-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/simple-serverless-programming-with-google-cloud-functions-and-pulumi/index.png" /&gt;
&lt;p&gt;Pulumi lets you create, deploy, and manage Google Cloud applications and
infrastructure in familiar languages like JavaScript, TypeScript, and
Python, and without needing to learn new DSLs or YAML templating
solutions. This means great productivity and getting to use tools you
already know and love. Since serverless is all about focusing more on
your application code, and less on infrastructure and configuration
toil, we absolutely love Google Functions.&lt;/p&gt;
&lt;h2 id="the-simplest-way-to-serverless"&gt;The Simplest Way to Serverless&lt;/h2&gt;
&lt;p&gt;In fact, serverless has never been simpler than it is when you combine
Pulumi with Google Cloud Functions. Want to serve a simple HTTP API with
no fixed costs? It&amp;rsquo;s just a few lines of code &amp;ndash; and no, we&amp;rsquo;re not
hiding any YAML here:&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;gcp&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/gcp&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;greeting&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;gcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudfunctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HttpCallbackFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;greeting&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;// Change this code to fit your needs!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="nx"&gt;res&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 class="sb"&gt;`Greetings from &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&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="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Google Cloud Functions&amp;#39;&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&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;greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpsTriggerUrl&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;Thanks to Pulumi&amp;rsquo;s language heritage, we know how to take that
function, serialize it, and publish it to a Google Bucket and Object,
eliminating those tedious manual steps behind a single &lt;code&gt;pulumi up&lt;/code&gt; CLI
invocation.&lt;/p&gt;
&lt;p&gt;Or perhaps a &lt;a href="https://cloud.google.com/pubsub/"&gt;pubsub&lt;/a&gt; topic that runs
some custom code on every message received. This sounds like a perfect
use case for an event-driven style of programming, so why don&amp;rsquo;t we
write it that 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="c1"&gt;// Create a PubSub Topic
&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;requests&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;gcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Topic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;requests&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;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessagePublished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;newMessage&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;data&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;// Print out a log message for every message on the Topic.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="c1"&gt;// Change this code to fit your needs!
&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;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;from&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;data&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 class="nx"&gt;toString&lt;/span&gt;&lt;span class="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;Because Pulumi knows how to manage infrastructure as code, it can
provision and manage not only the Google Cloud Function, but also the
pubsub topic resource itself, for each creation and management.&lt;/p&gt;
&lt;p&gt;Or perhaps, we&amp;rsquo;d like a function that respond to any uploads of new
objects to your &lt;a href="https://cloud.google.com/storage/"&gt;storage&lt;/a&gt; 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="c1"&gt;// Create a Storage Bucket
&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;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;gcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data&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;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onObjectFinalized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;newobject&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;data&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;// Print out a log message for every upload to the Bucket.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="c1"&gt;// Change this code to fit your needs!
&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 file uploaded: &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;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;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="acompletegoogle-functions-slack-bot"&gt;A Complete Google Functions Slack Bot&lt;/h2&gt;
&lt;p&gt;For an idea of how you might fit these together to make a real-world
cloud application, let&amp;rsquo;s look a simple skeleton structure for a
&lt;a href="https://api.slack.com/bot-users"&gt;Slack Bot&lt;/a&gt; using Pulumi and Google Cloud
Functions:&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;// secure config tokens to use to validate incoming messages as well as authenticate ourself to slack
&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mentionbot&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;slackToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;slackToken&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;verificationToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;verificationToken&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 topic that we can enqueue slack events to so they can be processed in batch later on
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messageTopic&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;gcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Topic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;messages&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 an http endpoint that slack will use to push events to us.
&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;gcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudfunctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HttpCallbackFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bot&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;callbackFactory&lt;/span&gt;&lt;span class="o"&gt;:&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/events&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;// Importantly: This is the code that will run in your serverless GCP cloud function!
&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;            &lt;span class="c1"&gt;// Process the body as appropriate. If it&amp;#39;s something we need to respond to immediately
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;            &lt;span class="c1"&gt;// (like a verification request), then do so. Otherwise, add the message to our pubsub
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;            &lt;span class="c1"&gt;// topic to be processed later:
&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;pubSub&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;PubSub&lt;/span&gt;&lt;span class="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;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pubSub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageTopic&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="kr"&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="nx"&gt;topic&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;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="p"&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 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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;            &lt;span class="c1"&gt;// Quickly respond with success so that slack doesn&amp;#39;t retry.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="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;return&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="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;messageTopic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessagePublished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;processTopicMessage&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;data&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;// Actually handle the &amp;#39;data&amp;#39; in the pubsub message.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="c1"&gt;// Importantly: This is the code that will run in your serverless GCP cloud function!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;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 this url to slack to let them know where to post their events to.
&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;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpsTriggerUrl&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 the real code for a complete SlackBot application running on
GCP, from the cloud resources to the serverless code, all within a
unified Pulumi application! Customizing this for your own use case is as
simple as changing the code in the two JavaScript arrow-functions.&lt;/p&gt;
&lt;p&gt;To see the complete project, take a look at our
&lt;a href="https://github.com/pulumi/examples/tree/master/gcp-ts-slackbot"&gt;@mentionbot example&lt;/a&gt;.
That example will listen for mentions of your name and will notify you
of them in a channel of your choosing, giving you you a persistent
timeline you can go back to look at to make sure you can find all these
messages.&lt;/p&gt;
&lt;p&gt;Although it&amp;rsquo;s a simple example, there are a lot of moving parts this
takes care of that you would normally be responsible for. This includes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Figuring out the shape (the input/output-types) for your Cloud
Functions, and then creating an appropriate program that exports the
right entrypoint that matches. In this case, because we&amp;rsquo;ve exposed
the right abstractions (like &lt;code&gt;HttpCallbackFunction&lt;/code&gt; and
&lt;code&gt;Topic.onMessagePublished&lt;/code&gt;), the arrow-functions you pass in will
all have the right types, and your program will be typechecked by
TypeScript.&lt;/li&gt;
&lt;li&gt;Creating separate Cloud Functions for each serverless callback. One
for listening and responding to the initial Slack events and the
second for processing the messages in the Topic. Here, you can write
a single Pulumi App where all the code can be placed how you like it
(in this case in a single file).&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions/docs/writing/"&gt;Packaging&lt;/a&gt; each
callback up in the appropriate structure Cloud Functions expects,
including how to get all your dependencies in place.&lt;/li&gt;
&lt;li&gt;Creating a &lt;a href="https://cloud.google.com/storage/docs/creating-buckets"&gt;Storage
Bucket&lt;/a&gt; for
all of your packaged programs to live in.&lt;/li&gt;
&lt;li&gt;Uploading each packaged program to a &lt;a href="https://cloud.google.com/storage/docs/uploading-objects"&gt;Bucket
Object&lt;/a&gt; in
that bucket.&lt;/li&gt;
&lt;li&gt;Configuring the appropriate triggers on your Cloud Functions stating
how they should be triggered (for example, in response to an &lt;a href="https://cloud.google.com/functions/docs/calling/http"&gt;HTTP
trigger&lt;/a&gt; or a
&lt;a href="https://cloud.google.com/functions/docs/calling/pubsub"&gt;Pub/Sub
trigger&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Including the right information in the function so you can interact
with your other cloud resources in the Pulumi App. Without this, you
would need to find a way to include that data in each Cloud
Function&amp;rsquo;s &lt;a href="https://cloud.google.com/functions/docs/env-var"&gt;environment variables&lt;/a&gt; (or just
hardcode them in &amp;lsquo;1&amp;rsquo;) so that your program can access the rest of
your cloud infrastructure. In the above example, you can see how you
can just reference your resources directly (like the PubSub Topic)
&lt;em&gt;directly&lt;/em&gt; from your Cloud Function callback. Pulumi makes sure this
all works, and that the data you use is available in that Cloud
Function when it finally is triggered.&lt;/li&gt;
&lt;li&gt;Figuring out a safe and secure way to encode and access secrets for
your Cloud Function. Here, we can use Pulumi&amp;rsquo;s
&lt;a href="https://www.pulumi.com/docs/concepts/config#secrets"&gt;Config Secrets&lt;/a&gt; to safely
encrypt and manage secrets for your Cloud Function code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Not to mention that by doing all of that, you can achieve continuous deployment
&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/google-cloud-build/"&gt;Pulumi and Google Cloud Build&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="updating-your-google-functions-code"&gt;Updating Your Google Functions Code&lt;/h2&gt;
&lt;p&gt;This is a lot to figure out to manage cloud applications and
infrastructure. If you want to tweak things even slightly you might need
to go make many manual changes and updates to ensure everything is
properly updated. With Pulumi, all this complexity is handled with a
single update! Just run &lt;code&gt;pulumi up&lt;/code&gt; , and it figures out the rest.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s just take a look at what Pulumi does when you tweak a few things
in the program:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pulumi up
Updating (slackbot):
     Type Name Status Info
     pulumi:pulumi:Stack slackbot
 +- ├─ gcp:pubsub:Topic messages replaced [diff: +labels]
     │ └─ gcp:cloudfunctions:CallbackFunction processTopicMessage
 +- │ ├─ gcp:storage:BucketObject processTopicMessage replaced [diff: ~name,source]
 ~ │ └─ gcp:cloudfunctions:Function processTopicMessage updated [diff: ~eventTrigger,sourceArchiveObject]
     └─ gcp:cloudfunctions:CallbackFunction mentionbot
 +- ├─ gcp:storage:BucketObject mentionbot replaced [diff: ~name,source]
 ~ └─ gcp:cloudfunctions:Function mentionbot updated [diff: ~sourceArchiveObject]
Outputs:
    url: &amp;quot;https://***.cloudfunctions.net/mentionbot&amp;quot;
Resources:
    ~ 2 updated
    +-3 replaced
    2 changes. 5 unchanged
Duration: 52s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One command later, and your entire stack is updated properly!&lt;/p&gt;
&lt;h2 id="winding-down"&gt;Winding Down&lt;/h2&gt;
&lt;p&gt;The cloud provides tremendous potential, and we want to make it easy for
developers to tap into that potential. Using Pulumi, it&amp;rsquo;s easy to blur
the line between business logic and serverless resources, bringing the
focus back to the problems you care about. One codebase that&amp;rsquo;s easy to
create, update, and maintain.&lt;/p&gt;
&lt;p&gt;To check things out, get started today:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/get-started/gcp/"&gt;Get Started with Pulumi on GCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pulumi/examples/tree/master/gcp-ts-functions"&gt;Deploy a Minimal Google Cloud Function Application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PS: If you&amp;rsquo;re interested in how Pulumi manages to take a
JavaScript/TypeScript &lt;code&gt;=&amp;gt;&lt;/code&gt; function and somehow analyze and transform it
into a form that Cloud Functions can use, please see our deep dive on
this topic in:
&lt;a href="https://www.pulumi.com/blog/lambdas-as-lambdas-the-magic-of-simple-serverless-functions"&gt;&lt;strong&gt;Lambdas as Lambdas: The magic of simple serverless Functions&lt;/strong&gt;&lt;/a&gt;.
We&amp;rsquo;re leveraging the same great programming language and analysis
framework to power our GCP solution here.&lt;/p&gt;</description><author>Cyrus Najmabadi</author><category>serverless</category><category>google-cloud</category></item><item><title>Simplified Outputs in Pulumi 0.17</title><link>https://www.pulumi.com/blog/simplified-outputs-in-pulumi-0.17.0/</link><pubDate>Tue, 19 Mar 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/simplified-outputs-in-pulumi-0.17.0/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/simplified-outputs-in-pulumi-0.17.0/index.png" /&gt;
&lt;div class="note note-info"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.70121449e0dde6f8c01ff68423fffaa0336ecc73c7bbc87506404126694ca58c.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;p&gt;This blog post refers to an outdated version of Pulumi (0.17). For current information about Outputs and the latest features, please refer to the &lt;a href="https://www.pulumi.com/docs/concepts/inputs-outputs/"&gt;Inputs and Outputs documentation&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Pulumi allows cloud developers to use programming languages like
JavaScript, TypeScript and Python to define and deploy cloud
infrastructure and applications. To do this, Pulumi exposes a notion of
&lt;a href="https://www.pulumi.com/docs/concepts/inputs-outputs/"&gt;Outputs&lt;/a&gt;
that track how the outputs of one cloud resource are used and
transformed as part of creating another cloud resource.&lt;/p&gt;
&lt;p&gt;These &lt;code&gt;Output&lt;/code&gt; types are heavily used in many Pulumi application. They
are the way that Resources expose their values and are commonly used to
pass values from one Resource to another. &lt;code&gt;Outputs&lt;/code&gt; are also a key part
of how Pulumi tracks dependencies between resources. In fact, Outputs
are similar to promises/futures that you may be familiar with from other
programming models but also carry along dependency information.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve heard a lot of feedback about how Outputs can lead
otherwise-simple code to become hard to write and reason about. So we&amp;rsquo;ve
looked into what we can do to significantly simplify the experience
working with Outputs - making the user experience simpler while
maintaining the rich dependency tracking and type checking that Pulumi
has always provided for cloud infrastructure.&lt;/p&gt;
&lt;h4 id="simplifying-outputs"&gt;Simplifying Outputs&lt;/h4&gt;
&lt;p&gt;In 0.17.0 of the &lt;code&gt;@pulumi/pulumi&lt;/code&gt; package, we&amp;rsquo;ve made most common
patterns for working with Outputs much simpler.&lt;/p&gt;
&lt;p&gt;Prior to this release of &lt;code&gt;@pulumi/pulumi&lt;/code&gt; package, it was fairly common
to have to write code like 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;cert&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;acm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cert&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;domainName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;example.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="nx"&gt;validationMethod&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DNS&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;certValidation&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;route53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cert_validation&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;records&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainValidationOptions&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;domainValidationOptions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;domainValidationOptions&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;resourceRecordValue&lt;/span&gt;&lt;span class="p"&gt;)],&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="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainValidationOptions&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;domainValidationOptions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;domainValidationOptions&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;resourceRecordType&lt;/span&gt;&lt;span class="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;In particular, creating the aws.route53.Record involves a fair amount of
complexity with those arrow functions. i.e.:&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;records&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainValidationOptions&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;domainValidationOptions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;domainValidationOptions&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;resourceRecordValue&lt;/span&gt;&lt;span class="p"&gt;)],&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="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainValidationOptions&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;domainValidationOptions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;domainValidationOptions&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;resourceRecordType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;zoneId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zone&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;zone&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Yikes! This is so verbose, it doesn&amp;rsquo;t even fit on the width of the page!&lt;/p&gt;
&lt;p&gt;The idea of the &lt;code&gt;.apply&lt;/code&gt; function is similar to &lt;code&gt;Promise.then&lt;/code&gt;. It
allows one to pass a piece of code that will be applied to the
underlying value once it becomes available (after the corresponding
cloud resource is created or updated) and will return an &lt;code&gt;Output&lt;/code&gt; that
then points to the transformed underlying value. Importantly, the new
&lt;code&gt;Output&lt;/code&gt; will still track dependency information properly. In the above
example the &lt;code&gt;aws.route53.Record&lt;/code&gt; will know that it depends on &lt;code&gt;cert&lt;/code&gt;,
even though the Certificate resource is not itself passed directly do
the constructor.&lt;/p&gt;
&lt;p&gt;Unfortunately, while &lt;code&gt;.apply&lt;/code&gt; gives a lot of power and flexibility, it
is also somewhat verbose and clunky for describing such a simple
concept. Fortunately, we found a way to improve the situation greatly.
This realization came about from great work done in our Python package.
First, before diving into the low level details, let&amp;rsquo;s first see what
the above code would now look like in 0.17.0:&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;certValidation&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;route53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cert_validation&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;records&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainValidationOptions&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;resourceRecordValue&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainValidationOptions&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;resourceRecordType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;zoneId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zone&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;p&gt;That&amp;rsquo;s a lot nicer than before! The following improvements happened:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There&amp;rsquo;s no more callbacks!&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s no need for a repetitive lambda parameter (which might
conflict with some other name in scope).&lt;/li&gt;
&lt;li&gt;The code is just pure simple idiomatic JavaScript/TypeScript that
clearly conveys its intent.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Importantly, no information has been lost here. The exact same
dependency information flows along here like it did before. And, thanks
to TypeScript&amp;rsquo;s flexible type system, the above is totally typesafe and
will let you know the right types of things and will still error if you
happen to make mistakes.&lt;/p&gt;
&lt;p&gt;So, how was this done? Well, the core part of the change is thanks to a
little-known feature of JavaScript:
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy"&gt;Proxies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;JavaScript proxies allow libraries to return objects that intercept and
override many facets of how JavaScript works at runtime. There are many
things a Proxy lets you control, however for our needs the most
important bit was that it allows you to override how member-lookup
works. In other words, when the code contains &lt;code&gt;someProxy.someMember&lt;/code&gt; the
proxy actually gets a chance to determine what should happen and what
someMember should actually return! With that flexible interception point
available, we actually took our core Output type and made it into a
Proxy. We then added the right interception code so that if the code
contains someOutput.someMember that that gets translated exactly into
&lt;code&gt;someOutput.apply(o =&amp;gt; o.someMember)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This also works just fine for array-accesses (which are just
property-lookups from JavaScript&amp;rsquo;s perspective). In other words, at
runtime, the member-lookup form and the &lt;code&gt;.apply&lt;/code&gt; form will be equivalent
(except that the former is so much nicer to write!). This is why, for
example,
&lt;code&gt;certCertificate.domainValidationOptions[0].resourceRecordValue&lt;/code&gt; will
have the correct value (with all the right dependency information). At
runtime it really will be equivalent to the original
&lt;code&gt;certCertificate.apply(certCertificate =&amp;gt; certCertificate.domainValidationOptions[0].resourceRecordValue)&lt;/code&gt;
form.&lt;/p&gt;
&lt;p&gt;Now, while this was fairly easy to get working at runtime from a
JavaScript perspective, it was a little more challenging to figure out
how to make this work in TypeScript&amp;rsquo;s typing system. For example, if you
had a value 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;const&lt;/span&gt; &lt;span class="nx"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;domainValidationOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Output&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resourceRecordName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resourceRecordType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resourceRecordValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&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="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;firstOption&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resourceRecordName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resourceRecordType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resourceRecordValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firstOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainName&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;Then how does TypeScript know that &lt;code&gt;cert&lt;/code&gt; should have a property on it
called &lt;code&gt;domainValidationOptions&lt;/code&gt;? And how can it know that
&lt;code&gt;domainValidationOptions&lt;/code&gt; can be indexed into? And how would it know
that once indexed, that &lt;code&gt;Output&lt;/code&gt; would have a &lt;code&gt;domainName&lt;/code&gt; property?
Clearly, these are all &lt;code&gt;Outputs&lt;/code&gt;. Yet, each &lt;code&gt;Output&amp;lt;...&amp;gt;&lt;/code&gt; has a
different set of properties exposed off of it!&lt;/p&gt;
&lt;p&gt;To do this required taking advantage of some very interesting and
advanced parts of TypeScript&amp;rsquo;s type system. If this is a part of
TypeScript that interests you, or you just want to see how we did this,
you can dive in deep into the source
&lt;a href="https://github.com/pulumi/pulumi/blob/7d7e104ee3184d1244ea3517ab5cae5f52170dba/sdk/nodejs/output.ts#L624-L631"&gt;here&lt;/a&gt;!
And, if you want to see how we did the actual runtime Proxy work, that
code is self-contained
&lt;a href="https://github.com/pulumi/pulumi/blob/7d7e104ee3184d1244ea3517ab5cae5f52170dba/sdk/nodejs/output.ts#L220-L282"&gt;here&lt;/a&gt;.
Thanks to TypeScript&amp;rsquo;s advanced type-system, these types can be properly
expressed and the entire tooling ecosystem understands them. For
example, in VSCode, if you try to write code like the above, you&amp;rsquo;ll see
all the expected properties with the expected types:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/simplified-outputs-in-pulumi-0.17.0/comp-list.png" alt="Completion Lists"&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We definitely hope these changes to &lt;code&gt;@pulumi/pulumi&lt;/code&gt; in 0.17.0 will make
the programming experience simpler and smoother for many common use
cases. And, if you&amp;rsquo;ve ever wanted to do some fancy tricks like what
we&amp;rsquo;re doing here, these updates can show you how you too can approach
some of these advanced techniques for both JavaScript and TypeScript!&lt;/p&gt;</description><author>Cyrus Najmabadi</author><category>features</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>Lambdas as Lambdas: The magic of simple serverless Functions</title><link>https://www.pulumi.com/blog/lambdas-as-lambdas-the-magic-of-simple-serverless-functions/</link><pubDate>Wed, 10 Oct 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/lambdas-as-lambdas-the-magic-of-simple-serverless-functions/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/lambdas-as-lambdas-the-magic-of-simple-serverless-functions/index.png" /&gt;
&lt;p&gt;Pulumi&amp;rsquo;s approach to infrastructure as code uses familiar languages instead
of YAML or DSLs. One major advantage of this approach is that AWS
Lambdas, Azure Functions, Google Cloud Functions, et al. can just be
real lambdas in your favorite language, offering a flexible and simple path to
serverless. Such functions behave as normal functions, allowing you to
treat serverless code as part of your application instead of separate
&amp;ldquo;infrastructure&amp;rdquo; that needs to be configured, managed, and versioned
manually. In this post, we&amp;rsquo;ll examine this capability in JavaScript,
which is already very function- and callback-oriented, making serverless
feel like a natural extension of the language we already know and love.&lt;/p&gt;
&lt;p&gt;While Functions as a Service (FaaS) systems have become more popular,
getting up and running can still feel overly complex compared to normal
application development. FaaS offerings today divide the development
experience between &amp;ldquo;infrastructure&amp;rdquo; &amp;ndash; doing all the work to
configure the Lambda runtime itself (i.e. how much memory to use, what
environment variables should be present, etc.) &amp;ndash; and writing and
maintaining the code that will execute &lt;em&gt;in&lt;/em&gt; the function itself when
triggered. Most developers just want to focus on the latter, write some
code, and have it work.&lt;/p&gt;
&lt;p&gt;For &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt;, for example, the code
itself needs to written and packaged up with all its dependencies into a
single zip-file, stored as a blob that is then placed in
&lt;a href="https://aws.amazon.com/s3/"&gt;AWS S3&lt;/a&gt;. This blob is then referenced by Lambda
so that it can be loaded on-demand when necessary. Updating just a
single character of this code, like fixing a bug, then requires fully
repackaging and re-uploading everything. Compounding all of that, this
code is necessary independent of any other pieces of your application.
If the code for the Lambda needs to read or write to an S3 bucket,
publish to an &lt;a href="https://aws.amazon.com/sns/"&gt;AWS SNS&lt;/a&gt; topic, etc., it
will need to be manually configured to have that bucket&amp;rsquo;s information
passed to it, often using out of band in environment variables. These
same issues also apply when creating
&lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-overview"&gt;FunctionApps&lt;/a&gt;
using Node.js on Azure.&lt;/p&gt;
&lt;p&gt;At Pulumi, we thought there could be a better way to do this. Why have
to jump out of your application code just to create another bit of
configuration (in YAML or JSON) just to run your application? Put
simply, we wanted developers to write event-driven code, in their
favorite language and in their favorite IDE, and simply hit &amp;ldquo;deploy&amp;rdquo;
when done.&lt;/p&gt;
&lt;h2 id="magic-functions"&gt;Magic Functions&lt;/h2&gt;
&lt;p&gt;From a desire to do better here, the &amp;ldquo;closure serialized function&amp;rdquo; was
born. However, &amp;ldquo;closure serialized function&amp;rdquo; is a geeky mouthful, so I
like just calling them &amp;ldquo;magic functions.&amp;rdquo; Our team is comprised of
cloud, language, and runtime geeks alike, so we figured out a way to
hook into the language runtimes to just make lambdas as lambdas work.&lt;/p&gt;
&lt;p&gt;Before diving more deeply into how magic functions work, let&amp;rsquo;s take a
look at how they look in action:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Example simplified to not get too bogged down :)
const sharp = require(&amp;quot;sharp&amp;quot;);
const videoBucket = new aws.s3.Bucket(&amp;quot;videos&amp;quot;);
const videoTopic = new cloud.Topic(&amp;quot;videoTopic&amp;quot;);
videoBucket.onObjectCreated(&amp;quot;onNewVideo&amp;quot;, async (event) =&amp;gt; {
for (const record of event.Records) {
const dimensions = computeDimensions(record.s3.object.key);
videoTopic.publish({ s3ObjectKey: record.s3.object.key, time: record.eventTime });
}
});
function computeDimensions(objectKey: string): { height: number, width: number } {
/* simplified */
const metadata = await sharp(...);
return { height: metadata.height, width: metadata.width };
}
videoTopic.subscribe(&amp;quot;compressVideo&amp;quot;, async (event) =&amp;gt; {
const { s3ObjectKey, time } = event;
// actually compress video.
});
videoTopic.subscribe(&amp;quot;createThumbnail&amp;quot;, async (event) =&amp;gt; {
const { s3ObjectKey, time } = event;
// actually create thumbnail.
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;rsquo;s still walk through it to see what&amp;rsquo;s happening. It can be a little
subtle because we are mixing &lt;em&gt;deployment-time&lt;/em&gt; code &amp;ndash; code that
declares infrastructure as code resources &amp;ndash; and *runtime *code &amp;ndash;
application code that runs as a function.&lt;/p&gt;
&lt;p&gt;First, we&amp;rsquo;re just defining two simple resources: 1) an &lt;code&gt;s3.Bucket&lt;/code&gt;,
where we expect new video files to be uploaded to, and 2) a
&lt;code&gt;cloud.Topic&lt;/code&gt; that will inform interested parties when new videos are
uploaded. (The full surface area of AWS is available in &lt;a href="https://www.pulumi.com/docs/iac/get-started/aws/"&gt;the aws package&lt;/a&gt;, and
&lt;a href="https://github.com/pulumi/pulumi-cloud"&gt;the cloud package&lt;/a&gt; offers
multi-cloud abstractions that work at a higher level of abstraction.)
Right after defining the resources, we start creating our first FaaS
resources. &lt;code&gt;videoBucket&lt;/code&gt; has an &lt;code&gt;onObjectCreated&lt;/code&gt; event subscription
function that will fire when new objects appear, and we invoke it with a
&lt;em&gt;real&lt;/em&gt; JavaScript (or TypeScript)
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions"&gt;arrow-function&lt;/a&gt;
defining the code that will execute in an AWS Lambda at &lt;em&gt;runtime&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="not-so-fast"&gt;Not So Fast&amp;hellip;&lt;/h2&gt;
&lt;p&gt;This code is pretty magical in a few ways. To see how it&amp;rsquo;s magic, let&amp;rsquo;s
consider a fairly naive way to create a Lambda for
this &lt;code&gt;onObjectCreated&lt;/code&gt; event handler function &amp;ndash; something we need to
do by hand with FaaS solutions today. Imagine we took the raw source
text of this arrow-function by &lt;code&gt;toString&lt;/code&gt;ing it and using that as the
text of the Lambda code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// index.js
module.exports = async (event) =&amp;gt; {
foreach (var record in event.Records) {
const dimensions = computeDimensions(record.s3.object.key);
videoTopic.publish({ s3ObjectKey: record.s3.object.key, time: record.eventTime });
}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, the above translation could have worked for an extremely basic
function, but totally breaks down here for several reasons. First,
&lt;code&gt;computeDimensions&lt;/code&gt; isn&amp;rsquo;t included, which will make this immediately
fail if ever called. Second, if &lt;code&gt;computeDimensions&lt;/code&gt; was included, it
wouldn&amp;rsquo;t work unless the &lt;code&gt;sharp&lt;/code&gt; module was properly imported for it.
Third, even if the module was imported in the code, the actual NPM
package would have to be available along with the code in the cloud.
Fourth, if that module was
properly &lt;code&gt;require&lt;/code&gt;d,
and the package was properly included, the code is both referencing
&lt;code&gt;videoTopic&lt;/code&gt; (a resource defined when the application originally ran),
&lt;em&gt;and&lt;/em&gt; it is invoking a helper method on it. Remember, this code is going
to be executing at cloud &lt;em&gt;runtime&lt;/em&gt;, not at the time when Pulumi is
actually executing the application on your developer machine, and
actually doing things like running the
&lt;code&gt;const videoTopic = new cloud.Topic(&amp;quot;videoTopic&amp;quot;)&lt;/code&gt; code.&lt;/p&gt;
&lt;p&gt;To summarize, we have quickly realized, we can&amp;rsquo;t just analyze
everything locally to figure out what has to be included, instead the
entire transitive closure must be considered &amp;ndash; a task usually left up
to FaaS developers to perform manually! This coupled with the manual
configuration using YAML can lead to plenty a headache.&lt;/p&gt;
&lt;h2 id="compilers-to-the-rescue"&gt;Compilers to the Rescue&lt;/h2&gt;
&lt;p&gt;Despite all those issues, Pulumi is able to make that original
application code work and it is able to magically effectively translate
the above into a working Lambda. The real secret sauce here is that, by
leveraging familiar languages, we can stand on the shoulders of all the
great compiler work underpinning them.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s dig in &amp;ndash; how does this all really work?&lt;/p&gt;
&lt;p&gt;(Note that, if you want some of the more nitty gritty details, we have a
&lt;a href="https://www.pulumi.com/docs/iac/concepts/miscellaneous/function-serialization/"&gt;doc&lt;/a&gt;
going in depth. For now, we&amp;rsquo;ll try to stick to providing a high-level
overview of what&amp;rsquo;s going on, so that we can grok the basic approach.)&lt;/p&gt;
&lt;p&gt;Pulumi executes your application as a normal Node.js application. As
such, when it hits the &lt;code&gt;const videoBucket = new aws.s3.Bucket(&amp;quot;videos&amp;quot;)&lt;/code&gt;
lines, it actually goes and creates those resources. When it then hits
the &lt;code&gt;videoBucket.onObjectCreated(&amp;quot;onNewVideo&amp;quot;, async (event) =&amp;gt; {&lt;/code&gt; line
this executes a normal line of JavaScript. Namely, &lt;code&gt;onObjectCreated&lt;/code&gt; is
a normal JavaScript function that will be passed the &lt;code&gt;name&lt;/code&gt; parameter as
the first arg, and the JavaScript
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions"&gt;function&lt;/a&gt;
as the second arg.
&lt;a href="https://github.com/pulumi/pulumi-aws/blob/2159b44b296ab66ce4386d42b28fb22f27a6ef6a/sdk/nodejs/s3/s3Mixins.ts#L223"&gt;onObjectCreated&lt;/a&gt;
eventually bottoms out with a call to a call to
&lt;a href="https://github.com/pulumi/pulumi/blob/fb18032a42eb34e9b5cbbe22a77a1b292d260a24/sdk/nodejs/runtime/closure/serializeClosure.ts#L89"&gt;pulumi.runtime.serializeFunction&lt;/a&gt;.
This function is the root of where our deep analysis of your JavaScript
function analysis and translation will happen.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth pausing here. We can use all the standard approaches to
function composition to build bigger functions out of smaller functions,
even before we hit that serialization routine. The closures are built up
in the V8 runtime as usual, and are eagerly awaiting the transformation
into magic functions as soon as they are needed. Pretty cool!&lt;/p&gt;
&lt;p&gt;Once we get to that point, using the awesome analysis APIs provided by
&lt;a href="https://github.com/Microsoft/TypeScript"&gt;TypeScript&lt;/a&gt;, we start
introspecting the function instance, seeing what code it contains, and
what external objects it references. The legal objects that can be
included in the captured function&amp;rsquo;s environment include (but aren&amp;rsquo;t
limited) to things like simple JavaScript objects, other functions (like
&lt;code&gt;computeDimensions&lt;/code&gt;), modules (like &lt;code&gt;sharp&lt;/code&gt;), other Pulumi resources
(like &lt;code&gt;videoTopic&lt;/code&gt;), and even complex beasts like
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;Promises&lt;/a&gt;.
Basically, anything that Pulumi knows how to serialize and rehydrate
inside the Lambda.&lt;/p&gt;
&lt;p&gt;When Pulumi determines that these objects are referenced, it
automatically includes enough information about them into the final
translated Lambda code file so that they are available and can be
effectively used at runtime. Thanks to JavaScript&amp;rsquo;s deep, dynamic
introspection capabilities, as well as its rich APIs for creating nearly
anything at runtime, it&amp;rsquo;s very possible to effectively hydrate out all
the information present when Pulumi runs, and put it into a form that
can be cheaply and accurately rehydrated into runtime objects again when
the Lambda runs.&lt;/p&gt;
&lt;p&gt;This analysis also understands the NPM package structure of an
application and its dependencies, ensuring that necessary imported
packages are properly included with the final Lambda. This analysis is
also very smart about not including things that aren&amp;rsquo;t necessary at all.
For example, it might drop information from the serialized form if it
can prove that would never be observable, in addition to relying on
built-in runtime functions being there without needing any sort of
serialization. There are definite subtleties and interesting cases that
can arise here, and for those very curious, reading the documentation
linked above may be very enlightening.&lt;/p&gt;
&lt;p&gt;Pulumi also understands diffs and dependencies, so updating is a cinch.
Any changes to the code in the JavaScript function (including anything
it depends on) is properly tracked and understood by Pulumi, allowing an
easy development model where FaaS code can be easily modified and
republished without jumping through extra hoops.&lt;/p&gt;
&lt;h2 id="the-result-simple-serverless-functions"&gt;The Result? Simple serverless Functions&lt;/h2&gt;
&lt;p&gt;With this approach, serverless programming becomes dramatically simpler,
whether it&amp;rsquo;s an AWS Lambda, Azure Function, or GCP Cloud Function.
(Stay tuned, we&amp;rsquo;re hoping to bring this to Kubernetes FaaS offerings
soon.)&lt;/p&gt;
&lt;p&gt;In short, you just write event-driven JavaScript code that&amp;rsquo;s familiar
and easy, you run &lt;code&gt;pulumi up&lt;/code&gt;, and everything is taken care of from
there. No YAML configuration files, no point and clicking in a cloud
console UI, no fuss.&lt;/p&gt;
&lt;p&gt;On top of all of that, because this is just code itself, and because
these are normal APIs, other tooling (like your editor) knows exactly
what is going on and can guide you through this. For example:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/lambdas-as-lambdas-the-magic-of-simple-serverless-functions/closure.png" alt="closure"&gt;&lt;/p&gt;
&lt;p&gt;While there&amp;rsquo;s a lot of really cool tech going on under the covers here,
we really hope that this ends up just feeling super-awesome to use for
you. Now that we have this functionality, we wouldn&amp;rsquo;t want to ever write
an AWS Lambda any other way!&lt;/p&gt;
&lt;p&gt;Get started building serverless functions on Pulumi today!&lt;/p&gt;</description><author>Cyrus Najmabadi</author><category>serverless</category><category>typescript</category></item><item><title>Running a Serverless Node.js HTTP Server on AWS and Azure</title><link>https://www.pulumi.com/blog/running-a-serverles-nodejs-http-server-on-aws-and-azure/</link><pubDate>Tue, 02 Oct 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/running-a-serverles-nodejs-http-server-on-aws-and-azure/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/running-a-serverles-nodejs-http-server-on-aws-and-azure/index.png" /&gt;
&lt;p&gt;The newly
introduced &lt;a href="https://github.com/pulumi/pulumi-cloud/blob/master/api/httpServer.ts"&gt;cloud.HttpServer&lt;/a&gt; in
Pulumi makes it easy to serve a standard Node.js HTTP server as a
serverless API on any cloud platform.  This new API brings together the
flexibility and rich ecosystem of Node.js HTTP servers, the cost and
operational simplicity of serverless APIs, and the multi-cloud authoring
and deployment of Pulumi.  In this post, we walk through some of the
background on why we introduced this new API and how it fits into the
Node.js HTTP ecosystem.&lt;/p&gt;
&lt;p&gt;Almost 10 years ago, &lt;a href="https://nodejs.org"&gt;Node.js&lt;/a&gt; was introduced,
helping to usher in an era of server-side JavaScript development. From
the beginning, Node.js made it simple and easy to stand up an HTTP
server and to start listening and responding to requests in an
environment that felt natural to people already comfortable working with
JavaScript. From the earliest commits this was a core focus, and from
early on Node.js exposed the initial JavaScript API for creating an
&lt;a href="https://github.com/nodejs/node/commit/a80591aff6704bd71ac5b136e23ddd7b52cf0299#diff-31b367d1856df8608494b65123d57acd"&gt;HTTP Server&lt;/a&gt;.
That API evolved over time to become the well known &lt;a href="https://nodejs.org/api/http.html"&gt;HTTP module&lt;/a&gt; that is the backbone of so
many projects.&lt;/p&gt;
&lt;p&gt;The approach taken by Node.js was to provide an
API, &lt;a href="https://nodejs.org/api/http.html#http_http_createserver_options_requestlistener"&gt;http.createServer&lt;/a&gt;,
to easily stand up a reliable and performant HTTP server, but it left
the processing and control of those actual HTTP conversations up to a
simple callback function that a user of the API would provide. This
callback function itself was very simple, with the entire API having the
form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http.createServer((req, res) =&amp;gt; ...);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this a developer could now easily plug in a function that would be
called with the information about an incoming &lt;code&gt;request&lt;/code&gt; in the
callback&amp;rsquo;s first parameter, and which could supply the appropriate
&lt;code&gt;response&lt;/code&gt; through the callback&amp;rsquo;s second parameter. This approach
allowed Node.js to focus on being simple and easy to get the server
stood up, while being relatively unopinionated giving the developer a
lot of flexibility in terms of how they would actually process requests
and responses.&lt;/p&gt;
&lt;p&gt;Thanks to this split, the Node.js ecosystem then came forward to provide
more structured design patterns to handle those requests and responses
to make things more manageable for developers. These communities
provided &amp;lsquo;middleware&amp;rsquo; APIs that could bridge between this extremely
basic entry-point into one more structured and opinionated, but simpler
to understand and work with. Middleware libraries like
&lt;a href="http://expressjs.com/"&gt;Express.js&lt;/a&gt; allowed one to write simple
route-handlers like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const app = express();
// GET method route
app.get('/', function (req, res) {
res.send('GET request to the homepage')
})
// POST method route
app.post('/', function (req, res) {
res.send('POST request to the homepage')
})
http.createServer(app).listen();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These route-handlers also have extensible middleware points to plug in
other libraries (like &lt;a href="https://github.com/expressjs/body-parser"&gt;Body
Parser&lt;/a&gt;,
&lt;a href="http://www.passportjs.org/"&gt;Passport&lt;/a&gt;, and many others). This large
effort across the entire ecosystem has made the task of serving websites
using JavaScript dramatically simpler, and has helped propel a huge
amount of popularity in this space.&lt;/p&gt;
&lt;p&gt;Interestingly enough though, the Cloud space has gone a slightly
different route here (pun intended). While cloud providers like AWS and
Azure provide ways to both create HTTP servers and to use Node.js, they
offer a very different shape altogether for processing HTTP messages.
For example, in AWS, one needs to first set up an &lt;a href="https://aws.amazon.com/api-gateway/"&gt;API
gateway&lt;/a&gt;. That gateway will then
have &amp;lsquo;methods&amp;rsquo; defined on it which point at &amp;lsquo;Lambda Proxies&amp;rsquo; to finally
process the request. The connected &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS
Lambda&lt;/a&gt; then will manage the request and
response in a decidedly AWS-specific manner. For example, instead of
working with
&lt;a href="https://nodejs.org/api/http.html#http_class_http_incomingmessage"&gt;IncomingMessage&lt;/a&gt;
and
&lt;a href="https://nodejs.org/api/http.html#http_class_http_serverresponse"&gt;ServerResponse&lt;/a&gt;
like one normally would with Node.js, AWS specific messages &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-api-gateway-request"&gt;like so&lt;/a&gt;
are the required. Responding to messages with your own data uses a very
different form as well. Code has to be written like so:&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&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;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&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;// process AWS &amp;#39;event&amp;#39; using information from AWS &amp;#39;context&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// build response
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;response&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;statusCode&lt;/span&gt;: &lt;span class="kt"&gt;responseCode&lt;/span&gt;&lt;span class="p"&gt;,&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;x-custom-header&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my custom header value&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;body&lt;/span&gt;: &lt;span class="kt"&gt;JSON.stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseBody&lt;/span&gt;&lt;span class="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;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="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;Azure follows its own &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook"&gt;distinct style&lt;/a&gt;
as well for processing HTTP messages. These approaches end up working,
but often feel decidedly un-Node.js-like. They&amp;rsquo;re full of cloud-provider-specific values and behaviors,
and they end up making it more difficult
to work with the existing ecosystem of middleware components out there.
(Note that Google Cloud &lt;a href="https://cloud.google.com/functions/docs/writing/http"&gt;HTTP Functions&lt;/a&gt;
actually do natively support the Node.js request/response pattern!).&lt;/p&gt;
&lt;p&gt;We thought this was a place we could definitely help Pulumi applications
with a simpler approach for these cloud ecosystems. To further this
goal, we&amp;rsquo;ve created a new API called
&lt;a href="https://github.com/pulumi/pulumi-cloud/blob/master/api/httpServer.ts"&gt;cloud.HttpServer&lt;/a&gt;.
&lt;code&gt;HttpServer&lt;/code&gt; is a Pulumi
&lt;a href="https://www.pulumi.com/docs/concepts/resources/"&gt;Resource&lt;/a&gt;,
but is designed to work well with the existing large middleware
ecosystem out there. And critically, the same HttpServer API can be
implemented consistently on AWS, Azure and GCP - so you can write once
and deploy to any cloud. The core API shape that accomplishes this is:&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;// Factory function for creating a requestListener function. The returned function is the same
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// callback function that would be passed to http.createServer. See:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// https://nodejs.org/api/http.html#http_http_createserver_options_requestlistener for more details.
&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;type&lt;/span&gt; &lt;span class="nx"&gt;RequestListenerFactory&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;req&lt;/span&gt;: &lt;span class="kt"&gt;http.IncomingMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;: &lt;span class="kt"&gt;http.ServerResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;HttpServerConstructor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/** * @param createRequestListener Function that, when called, will produce the [[requestListener]] * function that will be called for each http request to the server. The function will be * called once when the module is loaded. As such, it is a suitable place for expensive * computation (like setting up a set of routes). The function returned can then utilize the * results of that computation. */&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createRequestListener&lt;/span&gt;: &lt;span class="kt"&gt;RequestListenerFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts?&lt;/span&gt;: &lt;span class="kt"&gt;pulumi.ResourceOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpServer&lt;/span&gt;&lt;span class="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 idea here is that a Pulumi app can now simply take the previous
Express.js example and rewrite it as:&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 `() =&amp;gt; { ... }` callback will actually become the code inside an AWS 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;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;cloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HttpServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;myserver&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// GET method route
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;app&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="s1"&gt;&amp;#39;/&amp;#39;&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;res&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 class="s1"&gt;&amp;#39;GET request to the homepage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// POST method route
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&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="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;res&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 class="s1"&gt;&amp;#39;POST request to the homepage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&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;app&lt;/span&gt;&lt;span class="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 majority of this code is identical to the original Node.js form. The
only small difference is the creation of the cloud.HttpServer resource
(which is needed for Pulumi to track work and dependencies) and the
returning of the &amp;lsquo;app&amp;rsquo; variable instead of directly calling
&lt;code&gt;http.createServer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pulumi will take that code and convert the JavaScript callback^**^
into all the cloud-provider-specific resources necessary to make this
work.  For example, on AWS, an API Gateway and Lambda. The API Gateway
will be properly setup to communicate with the Lambda function, and the
Lambda function will properly handle AWS&amp;rsquo;s specific input/output forms
and will map them along so they can properly work with Node.js&amp;rsquo; http
library expectations.&lt;/p&gt;
&lt;p&gt;Effectively, the above callback will get translated into the following
code that is uploaded to AWS Lambda.&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;// index.js
&lt;/span&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="nx"&gt;WrapAwsEventAndContextAndCallBackAndForwardTo&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// GET method route
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;app&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="s1"&gt;&amp;#39;/&amp;#39;&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;res&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 class="s1"&gt;&amp;#39;GET request to the homepage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// POST method route
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&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="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;res&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 class="s1"&gt;&amp;#39;POST request to the homepage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&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;app&lt;/span&gt;&lt;span class="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;In other words, that factory function will get called, returning the
expected
&lt;a href="https://nodejs.org/api/http.html#http_http_createserver_options_requestlistener"&gt;requestListener&lt;/a&gt;
function. Pulumi will then inject the call to the helper that will wrap
that function with the right translation code to map from the AWS
specific inputs to the expected Node.js form, and from the Node.js
outputs to the AWS expected forms.&lt;/p&gt;
&lt;p&gt;Right now, this functionality has been enabled for AWS and Azure in our
&lt;code&gt;@pulumi/cloud-aws&lt;/code&gt; and &lt;code&gt;@pulumi/cloud-azure&lt;/code&gt; packages (support for GCP
is on the roadmap!). This means that the above code will run properly on
either platform without consumers having to write platform specific code
or figure out how they would need to translate between the two. And,
because of the translation from AWS/Azure specific APIs to the common
Node.js API, you can now use virtually any middleware components
effectively on either platform. Currently, this only works if you are
writing a Pulumi application. However, we are
&lt;a href="https://github.com/pulumi/pulumi-cloud/issues/585"&gt;tracking&lt;/a&gt; the work
to extract the specialized code we&amp;rsquo;ve written into its own library. Once
that work is done, you&amp;rsquo;ll then be able to easily do something like
create an Azure
&lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-overview"&gt;FunctionApp&lt;/a&gt;
manually, setup the appropriate
&lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook"&gt;bindings&lt;/a&gt;,
and then upload your code which will setup the appropriate Azure
endpoint, but then call into this library to translate to the Node.js
http form that will allow you to then code things up as simply as if
this was just an Express.js app!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;^**^&lt;/code&gt; One thing glossed over was how Pulumi knows how to convert a
JavaScript callback into an AWS Lambda or an Azure FunctionApp. Stay
tuned for more on this subject! It&amp;rsquo;s an incredibly cool part of Pulumi,
and we can&amp;rsquo;t wait to dive deeper into that functionality and how it can
make it simple and clear to create an entire Cloud App in one single
package, even including the code in-line that will end up executing in
the cloud at runtime! That magic, along with powerful components like
the new HttpServer API can help make cloud applications dramatically
simpler to write and maintain. Happy coding!&lt;/p&gt;
&lt;p&gt;You can dig in to &lt;a href="https://github.com/pulumi/pulumi-cloud/"&gt;serverless coding with Pulumi here&lt;/a&gt;,
and join us on Wednesday 3rd October at 11am PDT to hear more about
&lt;a href="https://www.youtube.com/watch?v=k8ceyQuJiVM"&gt;serverless programming with Pulumi on our YouTube live stream&lt;/a&gt;.&lt;/p&gt;</description><author>Cyrus Najmabadi</author><category>serverless</category></item><item><title>Advanced TypeScript type FTW!</title><link>https://www.pulumi.com/blog/advanced-typescript-type-ftw/</link><pubDate>Wed, 19 Sep 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/advanced-typescript-type-ftw/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/advanced-typescript-type-ftw/index.png" /&gt;
&lt;p&gt;We at Pulumi love TypeScript for cloud apps and infrastructure, because of its rich type system and great ahead-of-time
typechecking – making for a more productive inner loop and helping to find errors sooner. The typesystem magic behind
how this works for infrastructure as code can be fascinating!&lt;/p&gt;
&lt;p&gt;A core part of the Pulumi &lt;a href="https://www.pulumi.com/docs/concepts/"&gt;programming model&lt;/a&gt; is that we allow people to express complex
&lt;a href="https://www.pulumi.com/docs/concepts/inputs-outputs/"&gt;dependency data&lt;/a&gt; that may &lt;em&gt;eventually&lt;/em&gt; be available.
Traditional JavaScript programming might expose that as a Promise&lt;T&gt;, but we’ve taken that one step further by introducing
a type we call:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Input&amp;lt;T&amp;gt; = T | Promise&amp;lt;T&amp;gt; | Output&amp;lt;T&amp;gt;;
// Like a Promise&amp;lt;T&amp;gt;, but also keeps track of dependency information so we can figure
// out what the upstream/downstream impact is whenever any values change in the system
interface Output&amp;lt;T&amp;gt; { /* ... */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This complex type can often show up as inputs to all Pulumi objects like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface VpcInput {
ipAddresses: Input&amp;lt;Input&amp;lt;string&amp;gt;[]&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Breaking that down, it’s basically saying “I’ll take an array of strings. But those actual string values can individually be
strings, promises-of-strings, or strings-carrying-dependency-information.” It even goes one step beyond that, because the
entire array itself could be just an array-of-those-things, or a promise-of-array-of-those-things, or an
array-of-those-things-with-dependency-information. Whew…!&lt;/p&gt;
&lt;p&gt;This approach is great for people producing the data. As a producer I can do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new Vpc({ ipAddresses: [&amp;quot;an-address&amp;quot;] }); // or even
new Vpc({ ipAddresses: [&amp;quot;an-address&amp;quot;, computeAddressAsynchronously() ]}); // or even
new Vpc({ ipAddresses: [&amp;quot;an-address&amp;quot;, computeAddressAsynchronously(), someDependency.address ]}); // or even
new Vpc({ ipAddresses: computeAnArrayAnyOfTheAboveAsynchrousnously() }); // etc. etc.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s super flexible, and let’s people naturally produce the values in whatever way they want without having to work too hard to
make it conform to some very narrow type.&lt;/p&gt;
&lt;p&gt;However, while easy to produce, this can sometimes be difficult to consume. For example, if you wanted to take in that
&lt;code&gt;Input&amp;lt;Input&amp;lt;string&amp;gt;[]&amp;gt;&lt;/code&gt; and then check if there was a very specific value in it, you might have to go through and do a bunch of
work. You’d have to first deal with potentially getting a &lt;code&gt;Input&amp;lt;string&amp;gt;[]&lt;/code&gt; or a &lt;code&gt;Promise&amp;lt;Input&amp;lt;string&amp;gt;[]&amp;gt;&lt;/code&gt; or even an &lt;code&gt;Output&amp;lt;Input&amp;lt;string&amp;gt;[]&amp;gt;&lt;/code&gt;.
Then, once you even got to the underlying array, you’d have to deal with each potential element in it being a &lt;code&gt;string&lt;/code&gt;… or a &lt;code&gt;Promise&amp;lt;string&amp;gt;&lt;/code&gt;… or a… well… you get the idea. It’s not fun 😃.&lt;/p&gt;
&lt;p&gt;Up until now, we’d supplied some simple helpers to make this more manageable. Our helpers would effectively ‘unwrap’ one
layer of this sort of structure allowing you take the external value and deal with an internal value of a known shape.
So it effectively worked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function unwrap&amp;lt;T&amp;gt;(input: Input&amp;lt;T&amp;gt;): T { /* ... */ }
// so, you could then write:
const ipAddresses: Input&amp;lt;string&amp;gt;[] = unwrap(vpc.ipAddresses);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This helped things somewhat. But it still meant that as you processed that array you had to then unwrap each and every
element. As our data model has grown, and we’ve tackled more and more complex domains, we started seeing code start to
form the programming &lt;a href="https://en.wikipedia.org/wiki/Pyramid_of_doom_(programming)"&gt;Pyramid of Doom&lt;/a&gt;, where you just kept
on going deeper and deeper, unwrapping all the way. We started seeing this happen so often that we wanted to see if we
there was anything we could do to make life much nicer for people using our APIs and using these helpers.&lt;/p&gt;
&lt;p&gt;Our core goal was to change our ‘unwrap’ function from just peeling away one layer of this onion, to peeling away &lt;em&gt;all&lt;/em&gt;
layers. So, for example, if you started with an &lt;code&gt;Input&amp;lt;Input&amp;lt;string&amp;gt;[]&amp;gt;&lt;/code&gt; you would just get back a plain old &lt;code&gt;string[]&lt;/code&gt;
that you could use completely naturally in all the ways you’d expect. We were initially uncertain if we could make
this work, but thanks to the power of JavaScript and the expressivity of TypeScript, we think we found a great solution
here.&lt;/p&gt;
&lt;p&gt;We knew that providing the implementation such a function itself was not going to the true hard part. After all, we
already had the code to peel back one layer. So peeling back all the layers was more a matter of just having the function
call itself recursively when encountering things like arrays (or other objects that themselves contained &lt;code&gt;Input&lt;/code&gt;s).&lt;/p&gt;
&lt;p&gt;The real hard part came in when were trying figure out how would we even express this in a type-safe manner TypeScript.
After all, what’s the type signature when you effectively want to say:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function unwrap(input: Input&amp;lt;...MaybeHasInputsOrPromisesOrOutputsArbitrarilyDeep...&amp;gt;): ThatThingWithAllTheDeepInputsPromisesAndOutputsErased;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was a big head scratcher for a while. But after exploring some of the powerful new features that TypeScript has
been bringing about in recent releases, we finally found a great way to manage it. In the end, it ends up looking
something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type primitive = string | number | boolean | undefined | null;
type Unwrap&amp;lt;T&amp;gt; =
T extends Promise&amp;lt;infer U&amp;gt; ? UnwrapSimple&amp;lt;U&amp;gt; :
T extends Output&amp;lt;infer U&amp;gt; ? UnwrapSimple&amp;lt;U&amp;gt; :
UnwrapSimple&amp;lt;T&amp;gt;;
type UnwrapSimple&amp;lt;T&amp;gt; =
T extends primitive ? T :
T extends Array&amp;lt;infer U&amp;gt; ? UnwrappedArray&amp;lt;U&amp;gt; :
T extends object ? UnwrappedObject&amp;lt;T&amp;gt; :
never;
interface UnwrappedArray&amp;lt;T&amp;gt; extends Array&amp;lt;Unwrap&amp;lt;T&amp;gt;&amp;gt; {}
type UnwrappedObject&amp;lt;T&amp;gt; = {
[P in keyof T]: Unwrap&amp;lt;T[P]&amp;gt;;
};
function unwrap&amp;lt;T&amp;gt;(val: T): Unwrap&amp;lt;T&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s a bit of a doozy containing a bunch of type definitions and the final function declaration that uses them. It
makes use of many advanced TypeScript typing features. Including ‘Union Types’ (the ‘primitive’ type), ‘Mapped Types’
(which ‘UnwrappedObject’ is an example of), ‘Conditional Types’ (the types with &lt;code&gt;?&lt;/code&gt; and &lt;code&gt;:&lt;/code&gt; in them) and ‘Inferred Types’
(the use of &lt;code&gt;infer U&lt;/code&gt; in several places). You can read more about these in the
&lt;a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html"&gt;Advanced Types&lt;/a&gt; documentation.&lt;/p&gt;
&lt;p&gt;Put together, the &lt;code&gt;Unwrap&amp;lt;T&amp;gt;&lt;/code&gt; itself is effectively a recursively defined structural pattern matching type. You can see
it reference itself once you walk into &lt;code&gt;UnwrappedArray&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;UnwrappedObject&amp;lt;T&amp;gt;&lt;/code&gt; types. As it recurses, it matches
the type it has and then ‘peels away’ the wrappings that have been caused by &lt;code&gt;Promise&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;Output&amp;lt;T&amp;gt;&lt;/code&gt;. As it hits
Arrays and Objects, it dives deeper, unwrapping along the way. This is all manageable in TypeScript because it does this
sort of matching and unwrapping in a lazy fashion, allowing us to express what is effectively an infinitely large
pattern-matching type, without it having any trouble understanding it or applying it!&lt;/p&gt;
&lt;p&gt;It amazes us how well this works and how expressive TypeScript is here. As you can see here, TypeScript completely
understands what’s going on and will give you all the great type-checking support and tooling that we know and love:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/advanced-typescript-type-ftw/completion-list.png" alt="TypeScript completion list"&gt;&lt;/p&gt;
&lt;p&gt;Since adding support for this, we’ve been able to start using this new function across our own codebase and we’ve seen
things get dramatically simpler. The pyramid-of-doom goes away and code becomes much cleaner and easier to read and
maintain. JavaScript helped us write this system easily, but thanks to TypeScript we can express these complex intents
and have the safety and understanding to do this sort of thing with confidence.&lt;/p&gt;
&lt;p&gt;A special thanks to &lt;a href="https://github.com/danielrosenwasser"&gt;@DanielRosenwasser&lt;/a&gt; over from the TypeScript team for
putting up with my constant pestering on how to work through this and deal with some especially thorny areas!&lt;/p&gt;</description><author>Cyrus Najmabadi</author><category>typescript</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></channel></rss>