<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Chris Smith</title><link>https://www.pulumi.com/blog/author/chris-smith/</link><description>Pulumi blog posts: Chris Smith.</description><language>en-us</language><pubDate>Mon, 24 Jan 2022 00:00:00 +0000</pubDate><item><title>Six Things You Might Not Know About the Pulumi Service</title><link>https://www.pulumi.com/blog/six-things-about-pulumi-service/</link><pubDate>Mon, 24 Jan 2022 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/six-things-about-pulumi-service/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/six-things-about-pulumi-service/index.png" /&gt;
&lt;p&gt;As a reader of this blog, you&amp;rsquo;ve probably heard of the &lt;a href="https://www.pulumi.com/product/pulumi-service/"&gt;Pulumi Service&lt;/a&gt;, the default state-management &lt;a href="https://www.pulumi.com/docs/iac/concepts/state-and-backends/"&gt;backend&lt;/a&gt; of the Pulumi CLI. If that&amp;rsquo;s the case, there&amp;rsquo;s also a good chance you&amp;rsquo;ve heard of a number of the Service&amp;rsquo;s key features, like helping you organize your &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/projects-and-stacks/"&gt;projects and stacks&lt;/a&gt;, collaborate with others with the help of &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/admin/organizations/"&gt;organizations&lt;/a&gt;, or handle sensitive data securely with built-in support for &lt;a href="https://www.pulumi.com/docs/concepts/secrets/"&gt;encrypted secrets&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What you might not know, though, is that we&amp;rsquo;re adding new features to the Pulumi Service all the time, and that some of these features can be fairly easy to miss. So in this post, we&amp;rsquo;ll highlight a handful of the features you might &lt;em&gt;not&lt;/em&gt; be aware of, and that we think make it even easier to manage your infrastructure with Pulumi.&lt;/p&gt;
&lt;p&gt;Let’s get started!&lt;/p&gt;
&lt;h2 id="feature-1-stack-tags"&gt;Feature 1: Stack Tags&lt;/h2&gt;
&lt;p&gt;The first thing we&amp;rsquo;ll cover is the &lt;a href="https://www.pulumi.com/docs/concepts/stack#stack-tags"&gt;Pulumi stack tags&lt;/a&gt; feature. Stack tags are key/value pairs that are associated with your Pulumi stack. You can use them for any number of purposes, from keeping things organized to even flagging whether or not it&amp;rsquo;s okay for a script to reclaim the stack&amp;rsquo;s resources.&lt;/p&gt;
&lt;p&gt;While you can create, edit, and remove stack tags using the Pulumi CLI (see &lt;a href="https://www.pulumi.com/docs/iac/cli/commands/pulumi_stack_tag"&gt;&lt;code&gt;pulumi stack tag&lt;/code&gt;&lt;/a&gt;), you can also do so in the Pulumi Console:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150612443-b0b187e1-6329-42ca-816f-01de1bc5d4ff.gif" alt="Adding and removing stack tags in the Service "&gt;&lt;/p&gt;
&lt;p&gt;Once set, you can quickly filter stacks on your organization’s Projects tab as well.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150613454-1554b763-2e8b-42e9-80ce-e629048bb1b9.png" alt="Filtering stacks by tag name"&gt;&lt;/p&gt;
&lt;h2 id="feature-2-link-updates-to-your-cicd-pipeline"&gt;Feature 2: Link Updates to your CI/CD pipeline&lt;/h2&gt;
&lt;p&gt;Another helpful thing the Pulumi Service can do is link the Pulumi stack update to the CI/CD job or run that was used to perform it, as well as to the specific source commit:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150612892-f8e84597-2ce2-4687-8acc-236a57f6c6a4.png" alt="The stack update header, showing links to GitHub source commit"&gt;&lt;/p&gt;
&lt;p&gt;When Pulumi updates a stack, it will store some information about the local machine state, such the current git SHA if the stack resides in a &lt;code&gt;git&lt;/code&gt; repository. It is with this data that the Pulumi Service links to relevant services where possible.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re curious to know all of the information Pulumi has for a stack update, you can navigate to the Environment tab for that update:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150613104-510c755f-180e-4f0c-a2cb-e9f9816649e6.png" alt="The Environment tab of a stack update"&gt;&lt;/p&gt;
&lt;h2 id="feature-3-custom-update-messages"&gt;Feature 3: Custom Update Messages&lt;/h2&gt;
&lt;p&gt;One of the most useful pieces of additional data stored with each update is the &lt;code&gt;message&lt;/code&gt; property. By default, Pulumi will use whatever the latest &lt;code&gt;git&lt;/code&gt; commit message is. But did you know you can customize that message from the command line?&lt;/p&gt;
&lt;p&gt;By passing the &lt;code&gt;--message&lt;/code&gt; (or &lt;code&gt;-m&lt;/code&gt;) flag to &lt;code&gt;pulumi up&lt;/code&gt;, you can provide a custom update message, which can help explain the context for why you made a particular infrastructure change:&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 --message &lt;span class="s2"&gt;&amp;#34;Release the hounds\!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150614494-b1c6aef1-aed0-4de5-a815-f4f8ddcef48a.png" alt="An update in the Service with a custom message"&gt;&lt;/p&gt;
&lt;h2 id="feature-4-viewing-resource-changes"&gt;Feature 4: Viewing Resource Changes&lt;/h2&gt;
&lt;p&gt;Another thing you might have missed is that the Pulumi Service supports multiple views for a stack update&amp;rsquo;s logs.&lt;/p&gt;
&lt;p&gt;The standard view mimics the Pulumi CLI&amp;rsquo;s output. However, you can switch to the &lt;em&gt;Diff&lt;/em&gt; or &lt;em&gt;Diagnostic&lt;/em&gt; views, too.&lt;/p&gt;
&lt;p&gt;The Diff view displays all of the properties of modified resources, along with their previous/updated values. This can be helpful when trying to understand exactly what happened for a stack update.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150618931-5881cc70-36ab-4e46-b01c-53c13b8edfa1.png" alt="The diff view, showing resource-property changes"&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Diagnostic view&lt;/em&gt; just outputs so-called “diagnostic” messages from the Pulumi update. Most commonly, this is where you will see the output of things like calls to JavaScript’s &lt;code&gt;console.log(...)&lt;/code&gt; or Python’s &lt;code&gt;print(...)&lt;/code&gt; functions. It&amp;rsquo;ll also include the output of dependent tools, like the output when building Docker containers.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150619125-8067b1c6-843d-4f52-999c-69f8e184870e.png" alt="The diagnostic view, showing the output of a Docker image build"&gt;&lt;/p&gt;
&lt;h2 id="feature-5-stack-resource-visualization"&gt;Feature 5: Stack Resource Visualization&lt;/h2&gt;
&lt;p&gt;Another cool feature of the Pulumi Service is being able to visualize a stack’s resources and their relationships to one another. This is a really important feature!&lt;/p&gt;
&lt;p&gt;One of the advantages of using an infrastructure as code tool like Pulumi is that you aren’t just creating a flat list of resources. Instead, by the virtue of how the code and resource graph is structured, you are building a graph of resources.&lt;/p&gt;
&lt;p&gt;This means that Pulumi has a semantic understanding of not only &lt;em&gt;what&lt;/em&gt; resources are in your stack but also how they relate to one another.&lt;/p&gt;
&lt;p&gt;If you navigate to a stack’s Resources tab, by default you see them listed in a table. You can filter this list by resource type, or by providing a custom filter:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150619318-9d452d1f-dc30-4907-af6a-9941c4c5fa68.png" alt="Filtering the resources list with a string matching the resource type (e.g., &amp;ldquo;bucket&amp;rdquo;)"&gt;&lt;/p&gt;
&lt;p&gt;However, in order to see the expanded resource graph, click the Graph View button. This will switch to rendering the resources as an interactive graph.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150619812-e32419b9-db31-489f-b132-df59b4da2c9f.png" alt="All resources in a stack, rendered as a graph"&gt;&lt;/p&gt;
&lt;p&gt;Notice here that the resources that are part of a &lt;a href="https://www.pulumi.com/docs/concepts/resources/#components"&gt;&lt;em&gt;component&lt;/em&gt;&lt;/a&gt; are grouped together. Also, you can double click on those nodes in the graph to expand or collapse them!&lt;/p&gt;
&lt;h2 id="feature-6-drilling-into-resource-properties"&gt;Feature 6: Drilling into Resource Properties&lt;/h2&gt;
&lt;p&gt;But you don’t need to just settle for a &amp;ldquo;top-level view&amp;rdquo; of your resources. You can click on any specific resource on the Resources tab, and see all of the properties of that resource.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150619959-7025d772-14e5-45a7-b660-78a1fc8d769b.png" alt="The Resource details view, showing all properties of a TargetGroup resource"&gt;&lt;/p&gt;
&lt;p&gt;This allows you to see all of the properties that Pulumi is tracking for that resource. It will also show you any relationships to other resources in the same stack.&lt;/p&gt;
&lt;p&gt;The Pulumi Service also links you to the resource’s cloud provider’s console where possible, so you can see even more details.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/274700/150620181-be3e7a09-3630-4898-89fa-7c7b775e1d35.png" alt="Linking directly to an ECS Cluster resource in the AWS Console"&gt;&lt;/p&gt;
&lt;div class="note note-info"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;Is your resource missing a link to the cloud provider console? With new services being launched from the myriad providers Pulumi supports every day, it’s hard to keep up. So if you would like us to add cloud provider links, please file an issue over in the &lt;a href="https://github.com/pulumi/console-requests/"&gt;pulumi/console-requests&lt;/a&gt; GitHub repository so we can update our database.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;There are plenty of other features to check out in the &lt;a href="https://www.pulumi.com/product/pulumi-service/"&gt;Pulumi Service&lt;/a&gt;, and we have exciting improvements on the way! As always, feel free to stop by the &lt;a href="https://slack.pulumi.com"&gt;Pulumi Community Slack&lt;/a&gt; to learn more, ask questions, or share anything cool you’re up to!&lt;/p&gt;</description><author>Chris Smith</author><author>Christian Nunciato</author><author>Laura Santamaria</author><category>announcements</category><category>features</category><category>cloud-engineering</category><category>policy-as-code</category><category>pulumi-cloud</category><category>infrastructure-as-code</category><category>pulumi-enterprise</category><category>continuous-delivery</category></item><item><title>Managing AWS Credentials on CI/CD: Part 3</title><link>https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-3/</link><pubDate>Thu, 14 May 2020 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-3/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/managing-aws-credentials-on-cicd-part-3/index.png" /&gt;
&lt;p&gt;This article is the third part of a series on best practices for securely managing AWS credentials on CI/CD. In this article, we cover the
last leg of the continuous delivery process to update your AWS resources and how to store sensitive data using Pulumi securely.&lt;/p&gt;
&lt;h2 id="recap"&gt;Recap&lt;/h2&gt;
&lt;p&gt;In &lt;a href="https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-1/"&gt;part 1&lt;/a&gt;, we created a dedicated IAM User with
limited privileges. Then in &lt;a href="https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-2/"&gt;part 2&lt;/a&gt;, we set up a simple,
serverless Pulumi program that periodically rotated the User&amp;rsquo;s access keys and updated the CI/CD system.&lt;/p&gt;
&lt;p&gt;In this final post, we will &lt;em&gt;use&lt;/em&gt; those AWS credentials to update cloud resources as part of
a CI/CD workflow using Pulumi.&lt;/p&gt;
&lt;p&gt;We will cover AWS&amp;rsquo;s &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html"&gt;IAM Roles&lt;/a&gt; and how they can be
used to safely manage the access Pulumi has to your AWS account. We will also cover how sensitive data in your
stack is stored within the Pulumi Service.&lt;/p&gt;
&lt;h2 id="assuming-iam-roles"&gt;Assuming IAM Roles for Performing Updates&lt;/h2&gt;
&lt;p&gt;In the previous post, we automatically create and rotate the AWS access keys for an IAM User. So it seems natural to
think that we should just use that same AWS access key to gain access to AWS resources. However, using the
access keys for an IAM User has a few major drawbacks.&lt;/p&gt;
&lt;p&gt;First, those access keys are long-lived. If we didn&amp;rsquo;t create that Pulumi program to rotate them automatically, the
key would last forever. (Meaning that a bad actor, perhaps elsewhere, or far in the future, could take advantage
of a compromised key.) Second, that access key could be used to do anything that the IAM User could. So you cannot
differentiate the set of capabilities based on the task at hand. (For example, if updating the Pulumi stack &lt;code&gt;website/staging&lt;/code&gt;,
we might need a different set of permissions than if we were updating the stack &lt;code&gt;website/production&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;The preferred alternative to using IAM User access keys is to &amp;ldquo;assume&amp;rdquo; an IAM Role. An IAM Role is similar to an IAM
User in that it grants a set of &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html"&gt;policies and permissions&lt;/a&gt;.
However, unlike an IAM User, an IAM Role does not have a password, nor can it be added to a Group and inherit any policies.
Also, the credentials for using an IAM Role are always short-lived. (Lasting no more than 12 hours, and likely much, much less.)&lt;/p&gt;
&lt;p&gt;Instead, an IAM Role defines a set of capabilities that can be performed by any principal capable of assuming it. So
in other words, you can exchange your limited access IAM User&amp;rsquo;s credentials for a specialized IAM Role. The IAM Role then
can be specifically tailored to a specific— wait for it— &lt;em&gt;role&lt;/em&gt; to perform.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t worry if that doesn&amp;rsquo;t all make sense at first; let&amp;rsquo;s look at a concrete example.&lt;/p&gt;
&lt;h3 id="desired-end-state"&gt;Desired End State&lt;/h3&gt;
&lt;p&gt;In our situation, we want to create an IAM Role &lt;code&gt;WebsiteStackUpdaterRole&lt;/code&gt; that grants permissions necessary for
Pulumi to update the &lt;code&gt;website/production&lt;/code&gt; stack. We will custom tailor this role to only grant access to the set of resources
and operations we know the stack needs.&lt;/p&gt;
&lt;p&gt;We also want to restrict who can assume the &lt;code&gt;WebsiteStackUpdaterRole&lt;/code&gt;, so that only specific users/accounts can access it.
e.g., the IAM User &lt;code&gt;cicd-bot&lt;/code&gt; but not IAM User &lt;code&gt;dwight-over-in-sales&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And just to show a more real-world example, we&amp;rsquo;ll do this using &lt;em&gt;multiple&lt;/em&gt; AWS accounts, where the &lt;code&gt;website/production&lt;/code&gt; stack&amp;rsquo;s resources are housed in a &amp;ldquo;production&amp;rdquo; AWS account, and the &lt;code&gt;cicd-bot&lt;/code&gt; IAM User is defined in a
&lt;a href="https://blog.coinbase.com/you-need-more-than-one-aws-account-aws-bastions-and-assume-role-23946c6dfde3"&gt;bastion AWS account&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="creating-the-iam-role"&gt;Creating the IAM Role&lt;/h3&gt;
&lt;p&gt;The following snippet creates an &lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/iam/role/"&gt;IAM Role&lt;/a&gt; resource using Pulumi. The
most important input property of which is the &lt;code&gt;assumeRolePolicy&lt;/code&gt; document, which defines &lt;em&gt;who&lt;/em&gt; can assume this role.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a situation where Pulumi&amp;rsquo;s ability to declare the policy document in code is super-useful, as it makes it
much easier to reference other resources (e.g., another AWS account&amp;rsquo;s ID) as well as supporting inline comments.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// IAM Role used by cicd-bot to update the website stack.
&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;websiteStackUpdaterRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;WebsiteStackUpdaterRole&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;WebsiteStackUpdaterRole&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;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Broad access for updating Pulumi stacks in the &amp;#39;production&amp;#39; AWS account.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;maxSessionDuration&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&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 class="c1"&gt;// 30 minutes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;assumeRolePolicy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Statement&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Principal&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// We grant the &amp;#34;bastion account&amp;#34;, which defines IAM Users the ability to assume this role.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// The IAM Policies in that other AWS account will then determine which users, groups, etc.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// will be able to use the `sts:AssumeRole` action on this IAM Role.
&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;AWS&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`arn:aws:iam::&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;awsAccountIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bastionAccount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;:root`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="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;// But as an additional precaution against misconfiguration in the bastion account, we add
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// some additional checks to determine who can access this policy.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Condition&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;ForAllValues:StringEquals&amp;#34;&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;aws:PrincipalTag/access-to-production&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;true&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;sts:ExternalId&amp;#34;&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;contoso/website/production&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;contoso/website/staging&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sts:AssumeRole&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;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;There&amp;rsquo;s a lot packed into that IAM Role&amp;rsquo;s configuration.&lt;/p&gt;
&lt;p&gt;First, the &amp;ldquo;principal&amp;rdquo; (who can assume the role) references a specific AWS account. This is an example of how you
can associate one AWS account with another. (And is best practice for isolating data between different departments, etc.)
We&amp;rsquo;ll cover how to configure the AWS bastion account side to use this role later.&lt;/p&gt;
&lt;p&gt;The next thing to call out is the use of the &lt;code&gt;Condition&lt;/code&gt; element of the policy document. To assume the role,
we require two checks to be performed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;That the principal assuming the role (e.g., IAM User) has the tag &lt;code&gt;&amp;quot;access-to-production&amp;quot;&lt;/code&gt; with value &lt;code&gt;&amp;quot;true.&amp;quot;&lt;/code&gt; This way,
we will reject any IAM User in the bastion account who isn&amp;rsquo;t tagged with &lt;code&gt;&amp;quot;access-to-production&amp;quot;&lt;/code&gt; from assuming this role.&lt;/li&gt;
&lt;li&gt;We also verify that the &amp;ldquo;external ID&amp;rdquo; (an optional parameter passed when assuming an IAM Role) matches a well-known Pulumi
stack name. This way, we can double-check that the entity using the IAM Role is doing so to use a specific stack.
(This is less of a security measure and more of a general hygiene type requirement.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="iam-policy-document"&gt;IAM Policy Document&lt;/h3&gt;
&lt;p&gt;Now we have an IAM Role; we need to attach a policy document that defines what permissions are granted.&lt;/p&gt;
&lt;p&gt;This is the &lt;strong&gt;most important step&lt;/strong&gt; since it defines precisely what the CI/CD bot can able to do.&lt;/p&gt;
&lt;p&gt;If we define the policy too broadly, then it is a general risk. For example, what if a compromised NPM package in your
Pulumi program did something nefarious. If the policy is defined too narrowly, then your &lt;code&gt;pulumi up&lt;/code&gt; might fail because
the necessary permissions weren&amp;rsquo;t available. When in doubt, err on the side of having a more restrictive policy.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It can be advantageous to have an identical &amp;ldquo;staging&amp;rdquo; and &amp;ldquo;production&amp;rdquo; instance of the same Pulumi stack,
so you can detect any permission issues before you update the user-facing production stack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It can take some time to winnow down the specific set of permissions necessary since that is ultimately specific to
the Pulumi program. For example, if the stack governs the networking configuration, then you can simply not deny
to the &lt;code&gt;s3:*&lt;/code&gt; actions.&lt;/p&gt;
&lt;p&gt;Unfortunately, Pulumi doesn&amp;rsquo;t currently support generating a reasonable IAM Policy document for your stack. For a
good introduction to some of the &amp;ldquo;worst-case scenarios&amp;rdquo; that could arise from a misconfigured IAM Policy,
see &lt;a href="https://know.bishopfox.com/blog/privilege-escalation-in-aws"&gt;this blog post from BishopFox&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The following is a hypothetical IAM policy that restricts the operations performed to just those for the
AWS CloudFront and S3 products. (Which might be sufficient for
&lt;a href="https://www.pulumi.com/blog/serving-a-static-website-on-aws-with-pulumi/"&gt;updating a CDN-based website hosted on AWS&lt;/a&gt;, but
likely not much else.)&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;// Policy document defining the permissions granted to the WebsiteStackUpdaterRole.
&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;websiteUpdaterPolicyDocument&lt;/span&gt;: &lt;span class="kt"&gt;aws.iam.PolicyDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Statement&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Grant access to all AWS S3 actions, but scoped to just the specific S3 buckets
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// used for the given Pulumi stack.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Sid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;GrantS3Access&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;Effect&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;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 class="nx"&gt;Resource&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="c1"&gt;// Content bucket.
&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;arn:aws:s3:::website-bucket&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;arn:aws:s3:::website-bucket/*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// S3 bucket which stores the access logs.
&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;arn:aws:s3:::website-logs-bucket&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;arn:aws:s3:::website-logs-bucket/*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="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;// Grant access to update a CloudFront distribution. For more information, 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://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/access-control-managing-permissions.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Sid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;GrantCloudFrontAccess&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;Effect&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Action&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;cloudfront:CreateDistribution&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;cloudfront:DeleteDistribution&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;cloudfront:GetDistribution&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;cloudfront:GetDistributionConfig&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;cloudfront:ListDistributions&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;cloudfront:UpdateDistribution&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;cloudfront:ListCloudFrontOriginAccessIdentities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// The specific CloudFront distribution used in the stack.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;arn:aws:cloudfront::0123456789:distribution/A123B1CABWZZ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&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;websiteUpdaterPolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;WebsiteUpdaterRolePolicy&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;WebsiteUpdaterRolePolicy&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;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Policy granting the permissions needed for updating the website/production Pulumi stack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;: &lt;span class="kt"&gt;websiteUpdaterPolicyDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&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;resourceOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To actually associate the IAM Policy document with the new IAM Role, we need to create a
&lt;a href="https://www.pulumi.com/registry/packages/aws/api-docs/iam/policyattachment/#policyattachment"&gt;PolicyAttachment&lt;/a&gt;
associating the two.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rolePolicyAttachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RolePolicyAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;WebsiteStackUpdaterRolePolicyAttachment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;role&lt;/span&gt;: &lt;span class="kt"&gt;websiteStackUpdaterRole&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;policyArn&lt;/span&gt;: &lt;span class="kt"&gt;websiteUpdaterPolicy.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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Reminder, we defined the IAM Role in our production AWS account, which allows any principal in the AWS
bastion account (with the right tag, external ID) to assume that role. We then need to update the bastion
account to grant the &lt;code&gt;sts:AssumeRole&lt;/code&gt; action on that IAM Role so it can be used! That isn&amp;rsquo;t covered in this post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="assuming-the-iam-role"&gt;Assuming the IAM Role&lt;/h3&gt;
&lt;p&gt;Finally! With everyone in place, we can now &lt;em&gt;use&lt;/em&gt; the IAM Role.&lt;/p&gt;
&lt;p&gt;The following is a set of commands you can run on your CI/CD worker to exchange the low-privilege IAM User
credentials for the specific IAM Role we just created in the production AWS account. But there are many
alternatives for programmatically assuming an IAM Role, such as using the
&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html"&gt;&lt;code&gt;AWS_PROFILE&lt;/code&gt; environment variable&lt;/a&gt;)
or the &lt;a href="https://github.com/coinbase/assume-role"&gt;&lt;code&gt;assume-role&lt;/code&gt;&lt;/a&gt; tool.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Use the AWS credentials passed to the CI system and available as environment variables.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# These are the low-privilege credentials that automatically get rotated.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws configure &lt;span class="nb"&gt;set&lt;/span&gt; aws_access_key_id &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&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;aws configure &lt;span class="nb"&gt;set&lt;/span&gt; aws_secret_access_key &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&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;aws configure &lt;span class="nb"&gt;set&lt;/span&gt; region &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="p"&gt;-west-2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&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;&lt;span class="c1"&gt;# Assume the desired IAM Role. Note how we pass the stack name in the --external-id parameter,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# as required to assume the role.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;readonly&lt;/span&gt; &lt;span class="nv"&gt;CREDS_JSON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;aws sts assume-role &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --role-arn &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARN_OF_IAM_ROLE_TO_ASSUME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --role-session-name &lt;span class="s2"&gt;&amp;#34;&amp;lt;useful description of the context, such as CI/CD job ID&amp;gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --external-id &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGET_STACK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&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;# The `aws sts assume-role` command just returns a JSON blob. So we now tease out the actual&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# access key and set the corresponding environment variables.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CREDS_JSON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s2"&gt;&amp;#34;.Credentials.AccessKeyId&amp;#34;&lt;/span&gt; --raw-output&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CREDS_JSON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s2"&gt;&amp;#34;.Credentials.SecretAccessKey&amp;#34;&lt;/span&gt; --raw-output&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;AWS_SESSION_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CREDS_JSON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s2"&gt;&amp;#34;.Credentials.SessionToken&amp;#34;&lt;/span&gt; --raw-output&lt;span class="k"&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;# If everything worked correctly, then running the get-caller-identity command will refer 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;# the IAM Role and not our IAM User.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Assumed AWS identity:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws sts get-caller-identity
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="secrets-in-pulumi"&gt;Storing Sensitive Data using Pulumi&lt;/h2&gt;
&lt;p&gt;As a developer, the most critical security threat is access to your cloud account since that’s ultimately where your or your customer’s data is stored. But beyond that, there are places where Pulumi stores sensitive data.&lt;/p&gt;
&lt;p&gt;Pulumi goes to great lengths to safeguard your data. Here we review where Pulumi interacts with your sensitive data and what you can do to safeguard it better.&lt;/p&gt;
&lt;h3 id="secrets-in-configuration"&gt;Secrets in Configuration&lt;/h3&gt;
&lt;p&gt;A Pulumi stack can have configuration data associated with it, which allows you to parameterize
cloud resources. For example, a &lt;code&gt;machine-size&lt;/code&gt; configuration key might determine which class of
virtual machine to use. A dev stack might only need a &lt;code&gt;&amp;quot;t2.small&amp;quot;&lt;/code&gt; but your production instance
might need something more powerful.&lt;/p&gt;
&lt;p&gt;Configuration data is stored in a file like &lt;code&gt;Pulumi.website-production.yaml&lt;/code&gt; should be checked in
along with the source code for a stack, so that builds and stack updates are reproducible.&lt;/p&gt;
&lt;p&gt;However, what if those configuration settings contain secrets? Like you need to store an API key
to use a 3rd party API.&lt;/p&gt;
&lt;p&gt;Pulumi supports &lt;a href="https://www.pulumi.com/docs/concepts/config/#secrets"&gt;encrypting sensitive configuration data&lt;/a&gt;.
You just need to add the &lt;code&gt;--secret&lt;/code&gt; flag.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Securely add an API key to the stack&amp;#39;s configuration.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pulumi config &lt;span class="nb"&gt;set&lt;/span&gt; api-key &lt;span class="s2"&gt;&amp;#34;hunter2&amp;#34;&lt;/span&gt; --secret
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For stacks hosted on the &lt;a href="https://app.pulumi.com/signin"&gt;Pulumi Service&lt;/a&gt;, the default is that your configuration
data is encrypted using a key specific to your stack. (So the ciphertext stored in the &lt;code&gt;Pulumi.yaml&lt;/code&gt; file
is safe to check into your source tree, since it cannot be copied/decrypted for another stack.)&lt;/p&gt;
&lt;h3 id="secrets-in-checkpoint-files"&gt;Secrets in Checkpoint Files&lt;/h3&gt;
&lt;p&gt;Pulumi keeps track of your cloud resources in a something called a &lt;a href="https://www.pulumi.com/docs/iac/concepts/state-and-backends/"&gt;checkpoint file&lt;/a&gt;,
and that too might contain sensitive information. For example, a Pulumi resource might have a &lt;code&gt;&amp;quot;password&amp;quot;&lt;/code&gt; output property.&lt;/p&gt;
&lt;p&gt;Pulumi &lt;a href="https://www.pulumi.com/docs/concepts/resources#additionalsecretoutputs"&gt;has support&lt;/a&gt; to mark that resource
output as &amp;ldquo;secret&amp;rdquo; and make sure that it is encrypted within the checkpoint file. (So if you were to look at the checkpoint file
contents via &lt;a href="https://www.pulumi.com/docs/iac/cli/commands/pulumi_stack_export"&gt;&lt;code&gt;pulumi stack export&lt;/code&gt;&lt;/a&gt;, you would not be able to recover
the data.&lt;/p&gt;
&lt;p&gt;Just like for secret configuration values, the default for stacks hosted on the Pulumi Service is to encrypt
this data using a key that Pulumi manages and is specific to your stack.&lt;/p&gt;
&lt;h3 id="custom-secrets-providers"&gt;Custom Secrets Providers&lt;/h3&gt;
&lt;p&gt;We at Pulumi take great care in safeguarding your data, especially configuration or checkpoint data that is marked as sensitive.
However, you might feel more comfortable if your stack&amp;rsquo;s data were encrypted using a key that &lt;em&gt;you&lt;/em&gt; controlled. (So even if the
data stored in the Pulumi Service were available, it would be useless without your specific key.)&lt;/p&gt;
&lt;p&gt;If that&amp;rsquo;s the case, then you can use a configurable secrets provider, and swap out the default &amp;ldquo;Pulumi Service managed&amp;rdquo; encryption
scheme for your own. (And we won&amp;rsquo;t take it personally, promise.)&lt;/p&gt;
&lt;p&gt;When you create a new stack using &lt;a href="https://www.pulumi.com/docs/iac/cli/commands/pulumi_stack_init/"&gt;pulumi stack init&lt;/a&gt;, you can optionally
specify a &lt;code&gt;--secrets-provider&lt;/code&gt; flag. That will determine where and how secrets get managed on your stack.&lt;/p&gt;
&lt;p&gt;For example, to use your own KMS key for encrypting data, you can pass the secrets provider
&lt;code&gt;&amp;quot;awskms://alias/ExampleAlias?region=us-east-1&amp;quot;&lt;/code&gt;. The Pulumi command-line will work and behave the same. Except that
whenever it needs to encrypt or decrypt some data, it will refer to the custom provider.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As you would expect, if you do use a custom secrets provider and lose access to your encryption key, then there is
no way to recover the encrypted data stored on your stack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For more information on custom secret providers, see
&lt;a href="https://www.pulumi.com/blog/peace-of-mind-with-cloud-secret-providers/"&gt;Peace of Mind with Cloud Secret Providers&lt;/a&gt; or
&lt;a href="https://www.pulumi.com/blog/managing-secrets-with-pulumi/"&gt;Managing Secrets with Pulumi&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Securely managing any sensitive data is challenging to get right, especially if there are many moving parts, such as when
coordinating access between your AWS account, CI/CD provider, and your Pulumi stack.&lt;/p&gt;
&lt;p&gt;In this blog series, we walked through the best practices in creating, securing, and managing access to an AWS account using
IAM Users and Roles. We then reviewed the specifics of where and how Pulumi stores sensitive data, in case you want to take
more control over your data.&lt;/p&gt;
&lt;p&gt;If you have any other questions, corrections, or other ideas you&amp;rsquo;d like to discuss about Pulumi, Continuous Delivery, or
security, please join the conversation over on the &lt;a href="https://slack.pulumi.com"&gt;Pulumi Community Slack&lt;/a&gt;.&lt;/p&gt;</description><author>Chris Smith</author><author>Sophia Parafina</author><category>continuous-delivery</category><category>security</category><category>secrets</category></item><item><title>Managing AWS Credentials on CI/CD: Part 2</title><link>https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-2/</link><pubDate>Thu, 26 Mar 2020 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-2/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/managing-aws-credentials-on-cicd-part-2/index.png" /&gt;
&lt;p&gt;This article is the second part of a series on best practices for securely managing AWS credentials on CI/CD. In this article, we go in-depth on providing AWS credentials securely to a 3rd party and introduce a Pulumi program to automate rotating access keys.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; These recommendations do not apply if you are running a CI/CD system within your
AWS account, e.g., running a Jenkins server on EC2 or using &lt;a href="https://aws.amazon.com/codedeploy/"&gt;AWS CodeDeploy&lt;/a&gt;.
In those cases, please refer to AWS&amp;rsquo;s documentation for how to
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html"&gt;assume IAM Roles when running on an EC2 instance&lt;/a&gt;
instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="providing-iam-credentials"&gt;Provide IAM credentials to your CI/CD system&lt;/h2&gt;
&lt;p&gt;In the &lt;a href="https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-1/"&gt;first post&lt;/a&gt; in our series, we created a dedicated IAM User to perform updates to AWS resources within your CI/CD system. The next step is to pass the AWS access keys for that user to your CI/CD system.&lt;/p&gt;
&lt;p&gt;We need to take great caution. &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html"&gt;AWS&amp;rsquo;s documentation&lt;/a&gt; states, &lt;strong&gt;&amp;ldquo;Do not provide your access keys to a third party&amp;rdquo;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Providing AWS access keys to any system is dangerous because of the risk that a bad actor could obtain those keys and do something nefarious. Even if your CI/CD service takes great care to protect your secrets, those AWS credentials could be inadvertently exposed in debugging output, system logs, or in some other way.&lt;/p&gt;
&lt;p&gt;This is one of the main advantages of performing continuous deployment from within your AWS account. For example, running Jenkins on an EC2 instance you manage, or using the AWS CodeDeploy service. Because when performing a deployment from within an AWS compute environment, you can use built-in mechanisms to obtain credentials securely.&lt;/p&gt;
&lt;p&gt;However, there are many reasons to use a hosted service for performing your CI/CD, such as developer productivity, ease of use, performance, or simply because you don&amp;rsquo;t want to recreate your existing deployment workflows.&lt;/p&gt;
&lt;p&gt;Ultimately, it is essential to understand the risks involved and how to mitigate them.&lt;/p&gt;
&lt;h2 id="properly-storing-credentials"&gt;Properly Storing Credentials&lt;/h2&gt;
&lt;p&gt;If you choose to provide your CI/CD with credentials, the most important thing is to mark them as secret. Your CI/CD provider typically has built-in support for handling &amp;ldquo;secure variables&amp;rdquo; or other sensitive information. As opposed to general, configuration data or environment variables.&lt;/p&gt;
&lt;p&gt;User-supplied configuration values are typically write-only and only accessible by the CI/CD worker jobs at runtime. For example, Travis CI has &lt;a href="https://circleci.com/docs/2.0/env-vars/#overview"&gt;encrypted environment variables&lt;/a&gt; or &lt;a href="https://circleci.com/docs/2.0/env-vars/#overview"&gt;secure environment variables&lt;/a&gt; in CircleCI.&lt;/p&gt;
&lt;p&gt;Please consult your CI/CD provider&amp;rsquo;s documentation for how to pass and store sensitive information appropriately.&lt;/p&gt;
&lt;h3 id="using-a-secrets-service"&gt;Why not use a &amp;ldquo;secrets manager&amp;rdquo; service?&lt;/h3&gt;
&lt;p&gt;A common question when discussing is how to pass access keys to a CI/CD system securely:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rather than giving your CI/CD provider AWS credentials, why not have your CI/CD system obtain credentials from a specialized &amp;ldquo;secrets manager&amp;rdquo; service?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In other words, if you choose &lt;em&gt;not&lt;/em&gt; to trust your CI/CD system with this data, can you &lt;em&gt;instead&lt;/em&gt; trust some other system dedicated for securely storing and retrieving sensitive information?&lt;/p&gt;
&lt;p&gt;It sounds like a good idea on the surface but doesn&amp;rsquo;t seem to make your data any more secure.&lt;/p&gt;
&lt;p&gt;Abstractly, the difference here is that the credentials are provided on-demand, rather than being available to the CI/CD job when it starts (and stored via the CI/CD provider). Instead, your CI/CD job would obtain credentials from the &amp;ldquo;secrets manager&amp;rdquo; only when needed.&lt;/p&gt;
&lt;p&gt;There are some advantages to this approach, such as providing a clear audit trail for access and more control over the distribution of sensitive information.&lt;/p&gt;
&lt;p&gt;However, the secrets provider system needs to be presented with some form of credentials. And &lt;em&gt;those&lt;/em&gt; credentials need to be available to your CI/CD environment. So using a secrets manager leaves you in the same place you started, i.e., needing to provide sensitive data to your CI/CD provider.&lt;/p&gt;
&lt;p&gt;Also, by adding a dependency on a secrets manager, you introduce additional risks. Not only do you need to be even more security-conscious about that secrets manager, but it also needs to be highly available. Any outage for that service would mean that you would be unable to perform deployments!&lt;/p&gt;
&lt;p&gt;So it does not seem that using a secrets manager to dole out AWS credentials to your CI/CD system is a good practice to follow. Or, at the very least, makes some tradeoffs without fundamentally making your approach to CI/CD any more or less secure.&lt;/p&gt;
&lt;p&gt;Instead, it is better to focus on other ways to reduce the risk if those AWS credentials do get exposed.&lt;/p&gt;
&lt;h2 id="automating-key-rotation"&gt;Automate Rotating and Revoking AWS Credentials&lt;/h2&gt;
&lt;p&gt;When providing credentials to a 3rd party, rather than hoping it will be 100% secure forever (which is impossible), we can make those credentials &lt;em&gt;volatile&lt;/em&gt;. If we regularly invalidate and rotate the credentials supplied to the CI/CD system, we can dramatically reduce the impact of any accidental disclosure. Even if the IAM User&amp;rsquo;s credentials were leaked, by the time they were discovered and used, they would no longer be valid.&lt;/p&gt;
&lt;p&gt;The AWS security blog describes how to &lt;a href="https://aws.amazon.com/blogs/security/how-to-rotate-access-keys-for-iam-users/"&gt;rotate access keys for IAM Users&lt;/a&gt;, by using the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a second access key in addition to the one in use.&lt;/li&gt;
&lt;li&gt;Update all your applications to use the new access key.&lt;/li&gt;
&lt;li&gt;Change the state of the previous access key to inactive.&lt;/li&gt;
&lt;li&gt;Verify everything is still working as expected.&lt;/li&gt;
&lt;li&gt;Delete the inactive access key.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That all sounds simple enough, but it&amp;rsquo;s certainly tedious. And if you need to repeat the process across dozens, if not hundreds of different CI/CD pipelines, you need a better solution. Fortunately, Pulumi provides an excellent and extensible way for writing a serverless program to
automate rotating credentials.&lt;/p&gt;
&lt;p&gt;The next few sections describe a simple infrastructure application for automating AWS IAM credential rotation. You can see the full application on GitHub at
&lt;a href="https://github.com/chrsmith/pulumi-aws-travis-cicd-demo"&gt;chrsmith/pulumi-aws-travis-cicd-demo&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="aws-credential-rotator-9000"&gt;AWS Credential Rotator 9000&lt;/h3&gt;
&lt;p&gt;The affectionally titled &lt;em&gt;AWS Credential Rotator 9000&lt;/em&gt; is a simple, serverless Pulumi application for rotating AWS access keys.&lt;/p&gt;
&lt;p&gt;It creates an AWS lambda function that&amp;rsquo;s triggered on a regular schedule, e.g., every 30 minutes, and performs the next step in the sequence for rotating access keys as outlined above.&lt;/p&gt;
&lt;p&gt;First, it creates a new access key and pushes the new value out. On the next iteration, it marks the older access key as &amp;ldquo;inactive.&amp;rdquo; On the following iteration, the inactive key is deleted. The process repeats, generating a new key and removing the inactive key.&lt;/p&gt;
&lt;h3 id="periodically-invoking-an-aws-lambda"&gt;Periodically Invoking an AWS Lambda&lt;/h3&gt;
&lt;p&gt;The heart of the application is triggering it to execute on a fixed interval. Thankfully this is super-easy to do using Pulumi since it allows you to seamlessly blend your &amp;ldquo;cloud infrastructure&amp;rdquo; with &amp;ldquo;code&amp;rdquo; in a natural way. The user guide for Pulumi Crosswalk for AWS has more information on &lt;a href="https://www.pulumi.com/docs/iac/clouds/aws/guides/lambda/"&gt;serverless eventing&lt;/a&gt;
if you would like to learn more.&lt;/p&gt;
&lt;p&gt;The following snippet is the core part of the credential rotator app. We define a function to handle the logic of key rotation in &lt;code&gt;rotateIAMUserKeys&lt;/code&gt;. Then we create an AWS Lambda resource
named &lt;code&gt;lambda&lt;/code&gt;. Finally, the &lt;code&gt;triggerSchedule&lt;/code&gt; resource invokes our lambda on a fixed schedule, thereby ensuring that the key rotation process goes on indefinitely. You can see the full code
&lt;a href="https://github.com/chrsmith/pulumi-aws-travis-cicd-demo/blob/master/infrastructure/key-rotator/iam-key-rotator.ts"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;rotateIAMUserKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iamUser&lt;/span&gt;: &lt;span class="kt"&gt;aws.iam.User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onNewCredentials&lt;/span&gt;&lt;span class="o"&gt;:&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;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;: &lt;span class="kt"&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="nx"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span 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;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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;aws.cloudwatch.EventRuleEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;keyRotatorLambda&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rotateIAMUserKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userToRotate&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;newKey&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;newSecret&lt;/span&gt;: &lt;span class="kt"&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&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="s2"&gt;&amp;#34;onNewCredentials callback called!&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;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;`A new AWS access key &amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newKey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;&amp;#34; has been created.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span 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;role&lt;/span&gt;: &lt;span class="kt"&gt;lambdaRole&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nodejs10.x&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="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;triggerSchedule&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;keyRotatorScheduler&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rate(1 hour)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lambdaCallbackFn&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;When writing reusable infrastructure components in Pulumi however, it is helpful to organize things into a &lt;a href="https://www.pulumi.com/docs/concepts/resources#custom-resources"&gt;custom resource&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, we can bundle together the AWS Lambda, CloudWatcn schedule, and the associated IAM policies into a single conceptual resource &lt;code&gt;AccessKeyRotator&lt;/code&gt;. Bundling resources allows for the code reuse.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rotator&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;AccessKeyRotator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rotator&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;interval&lt;/span&gt;: &lt;span class="kt"&gt;config.require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rate&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;user&lt;/span&gt;: &lt;span class="kt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;credentialPusher&lt;/span&gt;: &lt;span class="kt"&gt;demoTravisCIPusher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;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;h3 id="pushing-credentials"&gt;Pushing Credentials&lt;/h3&gt;
&lt;p&gt;With the mechanics of updating an AWS access key out of the way, the next step is to notify dependent systems what the new access key should be. The key rotator app has a &lt;code&gt;CredentialPusher&lt;/code&gt; abstraction to provide a pluggable way for you to send credentials to where they need to be. The example on GitHub only supports updating a Travis CI project (See &lt;a href="https://github.com/chrsmith/pulumi-aws-travis-cicd-demo/blob/master/infrastructure/key-rotator/credential-pusher-travis.ts"&gt;&lt;code&gt;credential-pusher-travis.ts&lt;/code&gt;&lt;/a&gt;.), but it could be easily extended to support other CI/CD
systems.&lt;/p&gt;
&lt;p&gt;The following snippet shows how the &lt;code&gt;CredentialPusher&lt;/code&gt; is configured in the key rotator app. We load a Travis CI access token from the
&lt;a href="https://www.pulumi.com/docs/concepts/config/"&gt;Pulumi stack&amp;rsquo;s configuration&lt;/a&gt;, and hard-code the specific set of projects and encrypted environment variables to
store the new access key.&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 &amp;#34;credential pusher&amp;#34; is the component that pushes new AWS IAM credentials out to 3rd parties
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// as the older ones get rotated. For demonstration purposes, this updates the Travis CI settings
&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 the chrsmith/pulumi-aws-travis-cicd-demo repo. But you can imagine another implementation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// that pushes the new IAM credentials to GitLab CI, or updating multiple CI/CD pipelines.
&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;demoTravisCIPusher&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;CredentialPusher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TravisCIPusher&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;accessKey&lt;/span&gt;: &lt;span class="kt"&gt;config.require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;travis-ci-token&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;projects&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;chrsmith/pulumi-aws-travis-cicd-demo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// In the Travis CI configuration for that GitHub repo, there are two
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// environment variables for storing the AWS credentials. So whenever 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;// AWS credentials get rotated, the job&amp;#39;s configuration settings will be
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// updated to reflect the new values.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;accessKeyIDLocation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;AWS_ACCESS_KEY_ID&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;secretAccessKeyLocation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;AWS_SECRET_ACCESS_KEY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="demo"&gt;Demo&lt;/h3&gt;
&lt;p&gt;To demonstrate the access keys rotation, we can examine the log files generated from AWS Lambda. These can be accessed from the command-line using the &lt;a href="https://www.pulumi.com/docs/iac/cli/commands/pulumi_logs"&gt;&lt;code&gt;pulumi logs&lt;/code&gt;&lt;/a&gt; command.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a summary of the output for clarity:&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; START RequestId: 9406913e-a9d0-46a4-b861-2efdee508b2b Version: &lt;span class="nv"&gt;$LATEST&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 9406913e-a9d0-46a4-b861-2efdee508b2b INFO IAM User has &lt;span class="m"&gt;2&lt;/span&gt; keys:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 9406913e-a9d0-46a4-b861-2efdee508b2b INFO - AKIASHIVKXX3QEQNZNX5 &lt;span class="o"&gt;[&lt;/span&gt;Active&lt;span class="o"&gt;]&lt;/span&gt; Tue Mar &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;2020&lt;/span&gt; 12:37:03 GMT+0000 &lt;span class="o"&gt;(&lt;/span&gt;Coordinated Universal Time&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 9406913e-a9d0-46a4-b861-2efdee508b2b INFO - AKIASHIVKXX3SA2K3ME7 &lt;span class="o"&gt;[&lt;/span&gt;Inactive&lt;span class="o"&gt;]&lt;/span&gt; Tue Mar &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;2020&lt;/span&gt; 09:37:03 GMT+0000 &lt;span class="o"&gt;(&lt;/span&gt;Coordinated Universal Time&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 9406913e-a9d0-46a4-b861-2efdee508b2b INFO Deleting older, inactive access key AKIASHIVKXX3SA2K3ME7 &lt;span class="o"&gt;[&lt;/span&gt;Inactive&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 9406913e-a9d0-46a4-b861-2efdee508b2b INFO Key rotation step complete.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; END RequestId: 9406913e-a9d0-46a4-b861-2efdee508b2b
&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; START RequestId: 05644e4a-81a4-4197-8ad7-fc3517998270 Version: &lt;span class="nv"&gt;$LATEST&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO IAM User has &lt;span class="m"&gt;1&lt;/span&gt; keys:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO - AKIASHIVKXX3QEQNZNX5 &lt;span class="o"&gt;[&lt;/span&gt;Active&lt;span class="o"&gt;]&lt;/span&gt; Tue Mar &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;2020&lt;/span&gt; 12:37:03 GMT+0000 &lt;span class="o"&gt;(&lt;/span&gt;Coordinated Universal Time&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO Created new key key AKIASHIVKXX3YX4IXRVH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO Pushing out the new key to 3rd party services...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO Pushing new credentials to Travis CI project &lt;span class="s1"&gt;&amp;#39;chrsmith/pulumi-aws-travis-cicd-demo&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO Updating env var &lt;span class="s1"&gt;&amp;#39;AWS_ACCESS_KEY_ID&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;b246ab3b-c4cb-e76f-a6fc-50f4cbf451c0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO Updated AWS access key ID. Got response code &lt;span class="o"&gt;(&lt;/span&gt;200&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO Updating env var &lt;span class="s1"&gt;&amp;#39;AWS_SECRET_ACCESS_KEY&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;ba9ac4be-fbb3-4da9-c5b8-35a1afe9e02b&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO Updated AWS secret access key. Got response code &lt;span class="o"&gt;(&lt;/span&gt;200&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 05644e4a-81a4-4197-8ad7-fc3517998270 INFO Key rotation step complete.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; END RequestId: 05644e4a-81a4-4197-8ad7-fc3517998270
&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; START RequestId: ca204959-32a7-44a8-ba20-738d139542ba Version: &lt;span class="nv"&gt;$LATEST&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ca204959-32a7-44a8-ba20-738d139542ba INFO IAM User has &lt;span class="m"&gt;2&lt;/span&gt; keys:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ca204959-32a7-44a8-ba20-738d139542ba INFO - AKIASHIVKXX3YX4IXRVH &lt;span class="o"&gt;[&lt;/span&gt;Active&lt;span class="o"&gt;]&lt;/span&gt; Tue Mar &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;2020&lt;/span&gt; 15:37:03 GMT+0000 &lt;span class="o"&gt;(&lt;/span&gt;Coordinated Universal Time&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ca204959-32a7-44a8-ba20-738d139542ba INFO - AKIASHIVKXX3QEQNZNX5 &lt;span class="o"&gt;[&lt;/span&gt;Active&lt;span class="o"&gt;]&lt;/span&gt; Tue Mar &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;2020&lt;/span&gt; 12:37:03 GMT+0000 &lt;span class="o"&gt;(&lt;/span&gt;Coordinated Universal Time&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ca204959-32a7-44a8-ba20-738d139542ba INFO Invalidating older access key AKIASHIVKXX3QEQNZNX5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ca204959-32a7-44a8-ba20-738d139542ba INFO Key rotation step complete.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; END RequestId: ca204959-32a7-44a8-ba20-738d139542ba
&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; START RequestId: de753fb4-0720-4f4f-a7aa-ad6b9d4c8832 Version: &lt;span class="nv"&gt;$LATEST&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; de753fb4-0720-4f4f-a7aa-ad6b9d4c8832 INFO IAM User has &lt;span class="m"&gt;2&lt;/span&gt; keys:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; de753fb4-0720-4f4f-a7aa-ad6b9d4c8832 INFO - AKIASHIVKXX3YX4IXRVH &lt;span class="o"&gt;[&lt;/span&gt;Active&lt;span class="o"&gt;]&lt;/span&gt; Tue Mar &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;2020&lt;/span&gt; 15:37:03 GMT+0000 &lt;span class="o"&gt;(&lt;/span&gt;Coordinated Universal Time&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; de753fb4-0720-4f4f-a7aa-ad6b9d4c8832 INFO - AKIASHIVKXX3QEQNZNX5 &lt;span class="o"&gt;[&lt;/span&gt;Inactive&lt;span class="o"&gt;]&lt;/span&gt; Tue Mar &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="m"&gt;2020&lt;/span&gt; 12:37:03 GMT+0000 &lt;span class="o"&gt;(&lt;/span&gt;Coordinated Universal Time&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; de753fb4-0720-4f4f-a7aa-ad6b9d4c8832 INFO Deleting older, inactive access key AKIASHIVKXX3QEQNZNX5 &lt;span class="o"&gt;[&lt;/span&gt;Inactive&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; de753fb4-0720-4f4f-a7aa-ad6b9d4c8832 INFO Key rotation step complete.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; END RequestId: de753fb4-0720-4f4f-a7aa-ad6b9d4c8832
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, the AWS credentials for the IAM User are automatically updated every few hours. And whenever a new access key is created, the value is pushed out to the impacted Travis CI projects automatically.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we covered some of the things to consider when providing AWS credentials to your CI/CD system. (In short, be cautious and follow best practices.)&lt;/p&gt;
&lt;p&gt;We then showed how relatively easy it is to stand up a Pulumi infrastructure application for automatically rotating AWS access keys and updating your CI/CD system. By rotating credentials, you can limit the impact if the value is inadvertently disclosed.&lt;/p&gt;
&lt;p&gt;At this point, we&amp;rsquo;ve now given access keys to a CI/CD system for a low-privilege AWS IAM User account. But we still can&amp;rsquo;t &lt;em&gt;do&lt;/em&gt; anything with it. The IAM User whose credentials we have given to the CI/CD system doesn&amp;rsquo;t have access to your production AWS account.&lt;/p&gt;
&lt;p&gt;In the next post, we&amp;rsquo;ll go into the details about IAM Roles, and they can be used to securely and temporarily gain access to additional resources. This is how our low-privilege IAM User can access needed to update production data for your CI/CD pipelines.&lt;/p&gt;</description><author>Chris Smith</author><author>Sophia Parafina</author><category>continuous-delivery</category><category>security</category><category>secrets</category></item><item><title>Managing AWS Credentials on CI/CD: Part 1</title><link>https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-1/</link><pubDate>Thu, 12 Mar 2020 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/managing-aws-credentials-on-cicd-part-1/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/managing-aws-credentials-on-cicd-part-1/index.png" /&gt;
&lt;p&gt;Continuous delivery requires providing highly sensitive credentials to your
deployment pipeline. Understanding the risks, mitigations, and best practices
for handling those credentials can be difficult. In this guide, we describe the
best practices for providing AWS credentials to a CI/CD system and to securely
automate updating your cloud infrastructure using Pulumi.&lt;/p&gt;
&lt;p&gt;You’ve heard about &lt;em&gt;Infrastructure as Code&lt;/em&gt; and decided to try your hand at using Pulumi to manage cloud infrastructure using familiar programming languages. Naturally, you’re curious and eager to start updating your cloud infrastructure in your CI/CD pipeline – but then panic hits&amp;hellip; Is updating your cloud infrastructure secure? If you start doing cloud deployments in your CI/CD system, doesn’t that mean you need to give it your production credentials? Aren’t those the most sensitive things your company has?!?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Take a deep breath&lt;/strong&gt;. It’s going to be OK. You &lt;em&gt;can&lt;/em&gt; securely provide AWS credentials to your CI/CD system and practice continuous delivery. This is the first post
in a series going in-depth on how to do just that.&lt;/p&gt;
&lt;p&gt;The goal of this article series is to give you a clear understanding of AWS credential management and how that relates to using Pulumi within a CI/CD environment.
Once you have the AWS credentials in-place, you can then follow our &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/"&gt;Continuous Delivery&lt;/a&gt; guide for
configuring your specific CI/CD service, whether you want to use &lt;a href="https://circleci.com"&gt;CircleCI&lt;/a&gt;, &lt;a href="https://docs.gitlab.com/ee/topics/build_your_application.html"&gt;GitLab CI&lt;/a&gt;, or
&lt;a href="https://travis-ci.org"&gt;Travis CI&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; These recommendations do not apply if you are running your own CI/CD system within your
AWS account, e.g., running a Jenkins server on EC2 or using &lt;a href="https://aws.amazon.com/codedeploy/"&gt;AWS CodeDeploy&lt;/a&gt;.
In those cases, please refer to AWS&amp;rsquo;s documentation for how to
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html"&gt;assume IAM Roles when running on an EC2 instance&lt;/a&gt;
instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;The recommendations in this series describe a general &amp;ldquo;one-size fits most&amp;rdquo; approach for credential
management, which requires a minimal amount of work to configure and maintain. Depending on your
specific environment, needs, and constraints, there may be a better alternative for your use case.&lt;/p&gt;
&lt;h2 id="create-new-iam-user"&gt;Create a dedicated IAM User for your CI/CD&lt;/h2&gt;
&lt;p&gt;The first step for securely automating CI/CD is to create a dedicated IAM User for use in your CI/CD
pipelines. (Sometimes referred to as a &amp;ldquo;robot account.&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;The following code snippet shows how to create a new AWS IAM User using Pulumi. (The code is in TypeScript,
but you could another language like Python, C#, or Go as well.)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cicdUser&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cicd-bot&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;tags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;purpose&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Account used to perform Pulumi stack updates on CI/CD.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, that IAM User doesn&amp;rsquo;t have permissions to do anything. The recommended best practice
is to &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#use-groups-for-permissions"&gt;use groups to assign permissions to IAM Users&lt;/a&gt;.
So next, we will create a &amp;ldquo;Pulumi Stack updaters&amp;rdquo; IAM Group, and add our robot account as a member.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pulumiStackUpdaters&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;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PulumiStackUpdaters&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;groupMembership&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupMembership&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cicdUserMembership&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;group&lt;/span&gt;: &lt;span class="kt"&gt;group.name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="group-permissions"&gt;Group Permissions&lt;/h3&gt;
&lt;p&gt;The next part is where things start to get tricky. We now need to grant permissions to this IAM
Group&amp;rsquo;s members so they can access various APIs and resources. (For example, if your Pulumi program
will need to create or update EC2 instances, then you will need to provide AWS credentials that
contain the &lt;code&gt;ec2:StopInstances&lt;/code&gt; or &lt;code&gt;ec2:StartInstances&lt;/code&gt; action.)&lt;/p&gt;
&lt;p&gt;But rather than grant the newly created &lt;code&gt;cicd-bot&lt;/code&gt; IAM User those permissions, we will
instead only grant them access to the &lt;code&gt;sts:AssumeRole&lt;/code&gt; action. This is an AWS API that allows an IAM
User to gain temporary credentials that have a different set of permissions associated with them.
(That is, the permissions associated with an &lt;em&gt;IAM Role&lt;/em&gt;.)&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll go into more detail about the exact difference between an IAM User and an IAM Role in the next
post in the series since it isn&amp;rsquo;t entirely obvious why one would be more secure than the other.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentAwsIdentity&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;getCallerIdentity&lt;/span&gt;&lt;span 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;groupPolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pulumiStackUpdatersPolicy&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;group&lt;/span&gt;: &lt;span class="kt"&gt;group.name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Statement&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="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;// Allow anybody (i.e., members of the group) to call the sts:AssumeRole API.
&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 allows them to &amp;#34;assume the role&amp;#34; of a more permissive IAM Role
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// when they go to update a stack later.
&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;sts:AssumeRole&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// This is the set of resources that the &amp;#34;sts:AssumeRole&amp;#34; operation could be
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// performed on, which is to say any IAM role in the current AWS account.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt;: &lt;span class="kt"&gt;pulumi.interpolate&lt;/span&gt; &lt;span class="sb"&gt;`arn:aws:iam::&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentAwsIdentity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;:role/*`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Sid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it for the first post in our series. We&amp;rsquo;ve taken the first step and created an IAM User specifically for performing updates to AWS resources using Pulumi.&lt;/p&gt;
&lt;p&gt;In the next post, we will review over the security concerns when passing AWS credentials to your CI/CD provider, as well as show a Pulumi program that you can use to rotate the AWS credentials for a user automatically.&lt;/p&gt;</description><author>Chris Smith</author><author>Sophia Parafina</author><category>continuous-delivery</category></item><item><title>Pulumi Service Improvements, February 2020</title><link>https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/</link><pubDate>Mon, 02 Mar 2020 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/pulumi-service-improvements_02-2020/index.png" /&gt;
&lt;p&gt;We&amp;rsquo;ve been hard at work making it easier to manage stacks, permissions, and organizations in the Pulumi Service. Adding
new features like first-class support for stack tags, deep links into CI/CD providers, and downloadable checkpoint files.&lt;/p&gt;
&lt;p&gt;In this post, we showcase what&amp;rsquo;s new!&lt;/p&gt;
&lt;h2 id="first-class-support-for-tags"&gt;First-class Support for Tags&lt;/h2&gt;
&lt;p&gt;Pulumi has had support for &lt;a href="https://www.pulumi.com/docs/concepts/stack#stack-tags"&gt;stack tags&lt;/a&gt; for a while, enabling
you to add attributes to your stacks with custom data such as the &lt;code&gt;account-id&lt;/code&gt; or &lt;code&gt;environment&lt;/code&gt;. But previously the data was
only available on the command-line, via the &lt;code&gt;pulumi stack tag&lt;/code&gt; command (&lt;a href="https://www.pulumi.com/docs/iac/cli/commands/pulumi_stack_tag/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve added first-class support for stack tags in the Pulumi Service as well. You can create, update, and delete tags from within the console.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/update-tags-in-console.png"
alt="Updating stack tags within the Pulumi Service"&gt;
&lt;/figure&gt;
&lt;p&gt;Making it easier to create and edit tag data is just the beginning. We&amp;rsquo;ve started to add new search/filtering capabilities
based on stack tags. For example, you can now use filters to rapidly add stacks to &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/access-management/teams/"&gt;Pulumi teams&lt;/a&gt;.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/filter-stacks-by-tag.png"
alt="Filtering stacks by their tags"&gt;
&lt;/figure&gt;
&lt;p&gt;We will continue wiring stack tags throughout other parts of the Pulumi Service, so stay tuned! Also, this new feature was
added by Pulumi&amp;rsquo;s first (and extremely awesome) intern, Tasia (👋)!&lt;/p&gt;
&lt;h2 id="deep-linking-into-cicd-systems"&gt;Deep Linking into CI/CD Systems&lt;/h2&gt;
&lt;p&gt;Many people practice &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/"&gt;continuous delivery using Pulumi&lt;/a&gt;, automatically
updating stacks in response to code check-ins or merges. We&amp;rsquo;ve now added support for deep links from the Pulumi Service back
into your CI/CD tool of choice. So, for example, you&amp;rsquo;ll now see links to the &lt;a href="https://circleci.com"&gt;Circle CI job&lt;/a&gt; or
&lt;a href="https://travis-ci.com"&gt;Travis CI build&lt;/a&gt; from the Pulumi Service.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/deep-linking-cicd-providers.png"
alt="Link to the Travis CI Build from the Pulumi Service"&gt;
&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Pulumi supports a variety of CI/CD providers, but if yours isn&amp;rsquo;t listed in &lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/"&gt;our CI/CD guide&lt;/a&gt;
&lt;a href="https://slack.pulumi.com"&gt;let us know&lt;/a&gt; or &lt;a href="https://github.com/pulumi/pulumi/blob/master/sdk/go/common/util/ciutil"&gt;contribute it&lt;/a&gt;
on your own.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="pretty-printing-json--multi-configuration"&gt;Pretty Printing JSON / Multi Configuration&lt;/h2&gt;
&lt;p&gt;We now pretty print JSON configuration values and have much better support for viewing multi-line data.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/uploads/content/blog/pulumi-service-improvements_02-2020/pretty-print-multiline-json.gif"
alt="JSON and mutiline configuration values"&gt;
&lt;/figure&gt;
&lt;h2 id="download-earlier-checkpoints"&gt;Download Earlier Checkpoints&lt;/h2&gt;
&lt;p&gt;The most important job of the Pulumi Service is to maintain a durable, accurate snapshot of your cloud resource data.
While in most cases your &lt;a href="https://www.pulumi.com/docs/iac/concepts/state-and-backends"&gt;stack&amp;rsquo;s checkpoint data&lt;/a&gt; is a low-level detail
you don&amp;rsquo;t need to worry about; in some advanced scenarios, you may need to inspect or edit it manually.&lt;/p&gt;
&lt;p&gt;You can now download a stack’s checkpoint file directly from the Pulumi Service. You can get the
same data from the command-line, using &lt;code&gt;pulumi stack export&lt;/code&gt; (&lt;a href="https://www.pulumi.com/docs/iac/cli/commands/pulumi_stack_export/"&gt;documentation&lt;/a&gt;,
which now supports a &lt;code&gt;--version&lt;/code&gt; flag to export older checkpoint files too.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/download-checkpoint-file.png"
alt="Download stack checkpoints from the Pulumi Service"&gt;
&lt;/figure&gt;
&lt;h2 id="reverse-stack-permissions-view"&gt;Reverse Stack Permissions View&lt;/h2&gt;
&lt;p&gt;The Enterprise Edition supports &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/projects-and-stacks#stack-permissions"&gt;role-based access control&lt;/a&gt;
using &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/access-management/teams/"&gt;teams&lt;/a&gt;. But a common problem we&amp;rsquo;ve heard from people in large organizations
is that it can be difficult to review exactly &lt;em&gt;&lt;em&gt;what&lt;/em&gt;&lt;/em&gt; access someone has to a stack and &lt;em&gt;&lt;em&gt;why&lt;/em&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Fortunately, the Pulumi Service now has the ability for organization administrators to see all stacks a particular member has access to.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/list-of-accessible-stacks.png"
alt="Stacks an organization member has access to"&gt;
&lt;/figure&gt;
&lt;p&gt;They can even go one step further, review the specific access granted from various sources.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/stack-access-grants.png"
alt="Sources granting stack permissions"&gt;
&lt;/figure&gt;
&lt;h2 id="log-rendering-performance"&gt;Log Rendering Performance&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ve made some changes that dramatically improve the Pulumi Service&amp;rsquo;s performance when rendering large update logs.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/uploads/content/blog/pulumi-service-improvements_02-2020/log-rendering-perf.gif"
alt="Log rendering performance"&gt;
&lt;/figure&gt;
&lt;h2 id="audit-logs"&gt;Audit Logs&lt;/h2&gt;
&lt;p&gt;Pulumi Enterprise customers typically have many stacks and teams. Multiple teams and stacks can make it difficult to review what&amp;rsquo;s currently in-motion and to understand a sequence of events a few days or few weeks in the past.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/auditing-your-organizations-infrastructure-as-code-activity/"&gt;As we announced earlier&lt;/a&gt; we launched the ability to view
and download audit logs with all the changes within an organization.&lt;/p&gt;
&lt;figure class="md:max-w-lg"&gt;&lt;img src="https://www.pulumi.com/blog/pulumi-service-improvements_02-2020/audit-logs.png"
alt="Audit logs of recent changes within an organization"&gt;
&lt;/figure&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Phew! As I said, we&amp;rsquo;ve been busy this month working on the Pulumi Service. And there is plenty more awesomeness still in the pipe!
If you want to get a peek at other things in-development, check out the &lt;a href="https://www.pulumi.com/blog/pulumi-2-0-roadmap/"&gt;Pulumi 2.0 Roadmap&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As always, we love to hear if you have any feedback, suggestions, or ideas for other improvements we could make to the
Pulumi Service. Just let us know on the &lt;a href="https://slack.pulumi.com"&gt;Pulumi Community Slack&lt;/a&gt;, Twitter &lt;a href="https://twitter.com/pulumicorp"&gt;@PulumiCorp&lt;/a&gt;,
or &lt;a href="https://github.com/pulumi/pulumi"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;</description><author>Chris Smith</author><category>features</category></item><item><title>Getting to ChatOps with Pulumi Webhooks</title><link>https://www.pulumi.com/blog/getting-to-chatops-with-pulumi-webhooks/</link><pubDate>Wed, 23 Jan 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/getting-to-chatops-with-pulumi-webhooks/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/getting-to-chatops-with-pulumi-webhooks/index.png" /&gt;
&lt;p&gt;Today we are delighted to announce the availability of Webhooks on
&lt;a href="https://app.pulumi.com/signin"&gt;Pulumi&lt;/a&gt;. Webhooks are a very common mechanism
to enable teams to be notified or react to events. In Pulumi&amp;rsquo;s case,
this means: notifications of infrastructure changes (be it on
Kubernetes, AWS, or any other cloud); responding to those changes as
part of &amp;lsquo;ChatOps&amp;rsquo;; or other build pipelines, to improve the delivery of
cloud native infrastructure.&lt;/p&gt;
&lt;p&gt;Pulumi Webhooks are available for the Team and Enterprise editions of
Pulumi. If you&amp;rsquo;re keen to try them out, start a trial of
&lt;a href="https://app.pulumi.com/site/trial"&gt;Team Edition here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ChatOps &amp;mdash; the idea of conversation-driven collaboration &amp;mdash; is an
increasingly common pattern, and as such we&amp;rsquo;ve gotten a lot of requests
to integrate Pulumi with cloud services such as Slack, GitHub, and
Twillio to facilitate that. These requests are rooted in users wanting
to have more awareness of changes being made to their cloud
infrastructure or wanting to build extensions to automate continuous
delivery pipelines. Pulumi Webhooks offer the flexibility to design
these notification and response systems as required for your team.&lt;/p&gt;
&lt;h2 id="using-pulumi-webhooks"&gt;Using Pulumi Webhooks&lt;/h2&gt;
&lt;p&gt;You can attach webhooks directly to stacks or organizations. Stack
webhooks will send notifications whenever changes to a stack are made
(&lt;code&gt;pulumi up&lt;/code&gt;) or simply previewed (&lt;code&gt;pulumi preview&lt;/code&gt;). Webhooks attached
to an organization will be sent the events for every stack within the
organization.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/getting-to-chatops-with-pulumi-webhooks/pulumi-webhooks.png" alt="Pulumi webhooks"&gt;&lt;/p&gt;
&lt;p&gt;When events occur, we&amp;rsquo;ll send a HTTP &lt;code&gt;POST&lt;/code&gt; request to any registered
listeners. Webhooks can then be used to send notifications to Slack or
SMS, start running automated tests, or maybe even update another stack!&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve been using Pulumi webhooks internally for raising awareness when
our test and production environments get updated. A common sight after
submitting some code is:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/getting-to-chatops-with-pulumi-webhooks/pulumi-webhooks-2.png" alt="webhooks2"&gt;&lt;/p&gt;
&lt;h2 id="building-a-pulumi-webhook-handler-using-pulumi"&gt;Building a Pulumi Webhook handler using&amp;hellip; Pulumi!&lt;/h2&gt;
&lt;p&gt;Pulumi is perfect for setting up cloud infrastructure, and so what
better tool to use for handling notifications from Pulumi than Pulumi!&lt;/p&gt;
&lt;p&gt;In this example, we&amp;rsquo;ll create a simple Pulumi program that stands up a
webhook handler that will receive webhook requests and echo them to
Slack. You can find the full code in the
&lt;a href="https://github.com/pulumi/examples/"&gt;examples repo&lt;/a&gt;, under
&lt;a href="https://github.com/pulumi/examples/tree/master/aws-ts-pulumi-webhooks"&gt;aws-ts-pulumi-webhooks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The code is very straight forward, thanks to the
&lt;a href="https://github.com/pulumi/pulumi-cloud"&gt;@pulumi/cloud&lt;/a&gt; package.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/pulumi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cloud&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/cloud&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&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;stackConfig&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;slackToken&lt;/span&gt;: &lt;span class="kt"&gt;config.require&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="nx"&gt;slackChannel&lt;/span&gt;: &lt;span class="kt"&gt;config.require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;slackChannel&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;webhookHandler&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;HttpEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pulumi-webhook-handler&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;webhookHandler&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;/&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;_&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="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 class="s2"&gt;&amp;#34;🍹 Pulumi Webhook Responder🍹&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="nx"&gt;webhookHandler&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;/&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;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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webhookID&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;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pulumi-webhook-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webhookKind&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;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pulumi-webhook-kind&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;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;`Received webhook from Pulumi &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;webhookID&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;webhookKind&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&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="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;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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prettyPrintedPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="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 class="nx"&gt;stackConfig&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;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="kt"&gt;stackConfig.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;`Pulumi Service Webhook (`&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;webhookKind&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`)`&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="nx"&gt;prettyPrintedPayload&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;```&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;as_user&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="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 class="sb"&gt;`posted to Slack channel &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stackConfig&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&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;webhookHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But to stand up the cloud application, we&amp;rsquo;ll need to create the stack
and set some configuration. And finally deploy it using &lt;code&gt;pulumi up&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pulumi stack init pulumi-webhook-handler
Created stack 'pulumi-webhook-handler'
$ pulumi config set cloud:provider aws
$ pulumi config set aws:region us-west-2
$ pulumi config set slackChannel #ops-notificatinos
$ pulumi config set slackToken ****** --secret
$ pulumi up --yes
Updating (robot-co/pulumi-webhook-handler):
... snip ...
Outputs:
url: &amp;quot;https://6tfwcc3vg3.execute-api.us-west-2.amazonaws.com/stage/&amp;quot;
Resources:
~ 1 updated
15 unchanged
Duration: 4s
Permalink: https://app.pulumi.com/robot-co/deploytron-9000/updates/6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The stack emits the URL to the webhook handler in the stack&amp;rsquo;s outputs.
That URL is what will receive and process Pulumi webhooks.&lt;/p&gt;
&lt;p&gt;With the infrastructure in-place, we just need to register the webhook
in the Pulumi Service. Organization administrators can do so under
the organization&amp;rsquo;s &lt;strong&gt;SETTINGS&lt;/strong&gt; tab.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/getting-to-chatops-with-pulumi-webhooks/pulumi-webhooks-3.png" alt="webhooks3"&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it! As stacks are created, updated, and so on, the webhook
handler will be notified and you can start building any custom actions
you can think of.&lt;/p&gt;
&lt;p&gt;There are three kinds of webhook events.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Event Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Trigger&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stack&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fired whenever a stack is created or deleted within an organization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stack_update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fired when a stack is updated. (Be it from &lt;code&gt;pulumi up&lt;/code&gt;, &lt;code&gt;pulumi refresh&lt;/code&gt;, or &lt;code&gt;pulumi destroy&lt;/code&gt;.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stack_preview&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fired whenever changes to a stack are previewed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="tell-us-what-you-think"&gt;Tell us what you think&lt;/h2&gt;
&lt;p&gt;Get started with Pulumi Webhooks on the &lt;a href="https://app.pulumi.com/signin"&gt;Pulumi app&lt;/a&gt;, and let us know how you&amp;rsquo;re using them so
we can continue to extend their capabilities. We&amp;rsquo;d love to hear your
ideas, as well as any feedback you have on the
&lt;a href="https://slack.pulumi.com"&gt;Pulumi Community Slack&lt;/a&gt;.&lt;/p&gt;</description><author>Chris Smith</author><category>continuous-delivery</category><category>features</category></item><item><title>Reusable CI/CD components with CircleCI Orbs for Pulumi</title><link>https://www.pulumi.com/blog/reusable-cicd-components-with-circleci-orbs-for-pulumi/</link><pubDate>Wed, 07 Nov 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/reusable-cicd-components-with-circleci-orbs-for-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/reusable-cicd-components-with-circleci-orbs-for-pulumi/index.png" /&gt;
&lt;p&gt;This morning &lt;a href="https://circleci.com/blog/announcing-orbs-technology-partner-program/"&gt;CircleCI announced the launch&lt;/a&gt;
of &lt;a href="https://circleci.com/orbs/"&gt;CircleCI Orbs&lt;/a&gt; which enable you to
create reusable components for CircleCI workflows. Orbs enable you to
simplify your CI/CD configuration by reusing existing orb jobs or
commands, in much the same way Pulumi enables you to simplify the
delivery of your cloud native infrastructure by sharing and reusing
&lt;a href="https://www.pulumi.com/blog/creating-and-reusing-cloud-components-using-package-managers/"&gt;existing components&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pulumi is proud to be a &lt;a href="https://circleci.com/partners/"&gt;CircleCI technology partner&lt;/a&gt;, and we were excited to get a
head start on seeing how orbs could make it easier to take Pulumi into
production within CircleCI. The &lt;a href="https://circleci.com/orbs/registry/orb/pulumi/pulumi"&gt;Pulumi Orbs for CircleCI&lt;/a&gt; are
available &lt;em&gt;today&lt;/em&gt; for you to start using.&lt;/p&gt;
&lt;h2 id="pulumi-orbs"&gt;Pulumi Orbs&lt;/h2&gt;
&lt;p&gt;You can see the Pulumi orbs in-action in the following CircleCI
&lt;code&gt;config.yml&lt;/code&gt;. It&amp;rsquo;s a simple job to build and update a JavaScript-based
stack.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: 2.1
Orbs:
pulumi: pulumi/pulumi@1.0.0
jobs:
build:
docker:
- image: circleci/node:7.10
working_directory: ~/repo
steps:
- checkout
- pulumi/login
- run:
command: |
npm install
npm run build
- pulumi/update:
stack: website-prod
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, the Pulumi orbs (and fixed version) are referenced by the name
&lt;code&gt;pulumi/pulumi@1.0.0&lt;/code&gt;. Then, the two Pulumi orbs &lt;code&gt;pulumi/login&lt;/code&gt; and
&lt;code&gt;pulumi/update&lt;/code&gt; are invoked.&lt;/p&gt;
&lt;p&gt;CircleCI orbs can be parameterized, so you can set the version of the
Pulumi client to download, whether the update should skip its preview,
and so on. (&lt;a href="https://github.com/pulumi/circleci#orb-reference"&gt;Full orb reference&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/reusable-cicd-components-with-circleci-orbs-for-pulumi/circleci-ui.png" alt="circleci ui"&gt;&lt;/p&gt;
&lt;p&gt;With other CI/CD systems, you&amp;rsquo;d typically need to write one-off Bash
scripts to download the client from &lt;a href="https://get.pulumi.com"&gt;https://get.pulumi.com&lt;/a&gt;, add it to
the current &lt;code&gt;$PATH&lt;/code&gt; and so on. Orbs allow for simpler, more declarative
CI/CD workflows.&lt;/p&gt;
&lt;p&gt;The source code for the Pulumi orbs is available &lt;a href="https://github.com/pulumi/circleci"&gt;on GitHub&lt;/a&gt;,
if you have any suggestions or feedback.&lt;/p&gt;
&lt;h2 id="other-integrations"&gt;Other Integrations&lt;/h2&gt;
&lt;p&gt;Of course Pulumi&amp;rsquo;s integration with CircleCI doesn&amp;rsquo;t end with orbs.&lt;/p&gt;
&lt;p&gt;Newer versions of the Pulumi command-line client will start associating
CircleCI build metadata with stack updates, so you you will soon see
links from &lt;a href="https://app.pulumi.com/signin"&gt;app.pulumi.com&lt;/a&gt; to any stack updates or previews
that happened during a CircleCI workflow.&lt;/p&gt;
&lt;p&gt;Also, if you are using CircleCI with GitHub, you can consider installing
the
&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/github-app/"&gt;Pulumi GitHub application&lt;/a&gt;. The Pulumi
GitHub app will surface the results of any previews or updates from your
CI/CD on the source GitHub pull request. It&amp;rsquo;s always good to know if a
pull request is going to lead to changes to your cloud infrastructure!&lt;/p&gt;
&lt;p&gt;We are continuing to make Pulumi the best tool for the &amp;ldquo;Continuous
Deployment&amp;rdquo; part of CI/CD workflows, and with the release of CircleCIs
Orbs, it&amp;rsquo;s just that much easier.&lt;/p&gt;
&lt;p&gt;Having trouble? Questions? Join our &lt;a href="https://slack.pulumi.com/"&gt;community Slack&lt;/a&gt;
or &lt;a href="https://www.pulumi.com/contact/"&gt;drop us a line&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://circleci.com/orbs/"&gt;Get Started with Circle CI Orbs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><author>Chris Smith</author><category>continuous-delivery</category></item><item><title>Creating and Reusing Cloud Components using Package Managers</title><link>https://www.pulumi.com/blog/creating-and-reusing-cloud-components-using-package-managers/</link><pubDate>Thu, 09 Aug 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/creating-and-reusing-cloud-components-using-package-managers/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/creating-and-reusing-cloud-components-using-package-managers/index.png" /&gt;
&lt;p&gt;Hello! A few weeks back I wrote
a post on &lt;a href="https://www.pulumi.com/blog/serving-a-static-website-on-aws-with-pulumi/"&gt;serving static websites on AWS with Pulumi&lt;/a&gt;
detailing how to host a static website on AWS. Pulumi allowed me to wire four
different AWS products together in only 200 lines of code. It would be a
shame, however if I needed to copy and paste that code every time I
wanted to to stand up a new website. Instead, we can package up, share,
and reuse our code just like any other Node.js library. It just so
happens that this one can be used to create cloud infrastructure.&lt;/p&gt;
&lt;h2 id="creating-a-package"&gt;Creating a Package&lt;/h2&gt;
&lt;p&gt;To create a reusable package for the static website example, I simply
put the code into a new
&lt;a href="https://github.com/chrsmith/static-website-aws"&gt;GitHub repo&lt;/a&gt; and
&lt;a href="https://www.npmjs.com/package/static-website-aws"&gt;publish it to NPM&lt;/a&gt;.
We&amp;rsquo;ll walk through exactly how to do that below, but afterwards anyone
who wants to stand up a static website on AWS can just reuse that
package.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;static-website-aws&lt;/code&gt; package exports a &lt;code&gt;StaticWebsite&lt;/code&gt; class, which
is a logical, reusable cloud &amp;ldquo;component&amp;rdquo; resource. This class offers a
simple constructor that internally does all of the hard work of creating
the assortment of AWS cloud resources in the prior blog post &amp;ndash; an S3
Bucket and objects per piece of static content, CloudFront Distribution,
and Route53 DNS A-Record &amp;ndash; and exposes them afterwards as readonly
properties:&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="cm"&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; * Static website using Amazon S3, CloudFront, and Route53.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;StaticWebsite&lt;/span&gt; &lt;span class="kr"&gt;extends&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;ComponentResource&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;contentBucket&lt;/span&gt;: &lt;span class="kt"&gt;aws.s3.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="kr"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;logsBucket&lt;/span&gt;: &lt;span class="kt"&gt;aws.s3.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="kr"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;cdn&lt;/span&gt;: &lt;span class="kt"&gt;aws.cloudfront.Distribution&lt;/span&gt;&lt;span class="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;readonly&lt;/span&gt; &lt;span class="nx"&gt;aRecord?&lt;/span&gt;: &lt;span class="kt"&gt;aws.route53.Record&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;    * Creates a new static website hosted on AWS.
&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 name  The _unique_ name of the resource.
&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 contentArgs  The arguments to configure the content being served.
&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 domainArgs  The arguments to configure the domain and DNS settings.
&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 opts  A bag of options that control this resource&amp;#39;s behavior.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&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;constructor&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;contentArgs&lt;/span&gt;: &lt;span class="kt"&gt;ContentArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;domainArgs?&lt;/span&gt;: &lt;span class="kt"&gt;DomainArgs&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&gt;&lt;/span&gt;&lt;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 class is placed into an NPM package as usual, including a
&lt;a href="https://github.com/chrsmith/static-website-aws/blob/master/package.json"&gt;package.json&lt;/a&gt;
file that gives it a name and a version. After doing this, we can
&lt;a href="https://docs.npmjs.com/getting-started/publishing-npm-packages"&gt;publish it to NPM&lt;/a&gt;
simply by running&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ npm publish
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All the complexities of creating, configuring, and wiring up the
necessary AWS resources &amp;ndash; admittedly only 200 lines of code, but still
intricate &amp;ndash; has now been hidden beneath an ultra-simple component
resource named &lt;code&gt;StaticWebsite&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s how you create a new package. The great thing about packages,
however, is that you define them once and then use them a lot. So now
let&amp;rsquo;s turn to see see how standing up a new CDN-backed website can now
become just a few lines of very simple, understandable code.&lt;/p&gt;
&lt;h2 id="reusing-infrastructure-as-code"&gt;Reusing Infrastructure as Code&lt;/h2&gt;
&lt;p&gt;To use this new package, head on over to a Pulumi program, or
&lt;a href="https://www.pulumi.com/docs/get-started/"&gt;create a new one&lt;/a&gt;
(for instance, with &lt;code&gt;pulumi new aws-typescript&lt;/code&gt;).
Then just add a reference like any other dependency:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ npm install static-website-aws
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At that point, we can import our &lt;code&gt;StaticWebsite&lt;/code&gt; class from the
&lt;code&gt;static-website-aws&lt;/code&gt; package like usual in Node.js programs, either
using require&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;swa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;static-website-aws&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;or using the new ES6 module import syntax, supported by TypeScript and
modern JavaScript&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StaticWebsite&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;static-website-aws&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;Afterwards, we&amp;rsquo;ll just &lt;code&gt;new&lt;/code&gt; up a &lt;code&gt;StaticWebsite&lt;/code&gt; object, which
internally creates everything for us. Of course, to create a useful
website, we&amp;rsquo;ll need to supply some interesting parameters . In this
case, let&amp;rsquo;s just pass a path to the content on disk (which will
automatically get uploaded into S3 objects during a &lt;code&gt;pulumi up&lt;/code&gt;) in
addition to a path to a custom 404 HTML page:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StaticWebsite&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;static-website-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="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;website&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;StaticWebsite&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;browserhack&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;pathToContent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./browserhack&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;custom404Path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/404.html&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;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cloudfrontDomain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;website&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cdn&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;As soon as we have our program, we can stand it up with a single
&lt;code&gt;pulumi up&lt;/code&gt;  command:&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will show us the entire resource graph so that we can see what it&amp;rsquo;s
creating internally and once it&amp;rsquo;s done the resulting domain name will be
printed out:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/creating-and-reusing-cloud-components-using-package-managers/pulumi-cli-output.png" alt="pulumi cli output"&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s then curl it to see whether it worked:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ curl $(pulumi stack output cloudfrontDomain)`[&amp;lt;!doctype html&amp;gt;
]{style=&amp;#34;color: red; font-family: Menlo, Monaco, Consolas, &amp;#39;Courier New&amp;#39;, monospace; font-size: 13px;&amp;#34;}[&amp;lt;!--
vim: set sw=2 ts=2 et : --&amp;gt;
]{style=&amp;#34;color: red; font-family: Menlo, Monaco, Consolas, &amp;#39;Courier New&amp;#39;, monospace; font-size: 13px;&amp;#34;}[&amp;lt;html&amp;gt;
]{style=&amp;#34;color: red; font-family: Menlo, Monaco, Consolas, &amp;#39;Courier New&amp;#39;, monospace; font-size: 13px;&amp;#34;}[
 &amp;lt;head&amp;gt;
]{style=&amp;#34;color: red; font-family: Menlo, Monaco, Consolas, &amp;#39;Courier New&amp;#39;, monospace; font-size: 13px;&amp;#34;}[...
etc, etc, etc
...]{style=&amp;#34;color: red; font-family: Menlo, Monaco, Consolas, &amp;#39;Courier New&amp;#39;, monospace; font-size: 13px;&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Huzzah! (Note that new CloudFront Domains can take a while to spin
up; if you get an error &amp;ldquo;Could not resolve host: xxx.cloudfront.net&amp;rdquo;,
just wait a little bit and try again.)&lt;/p&gt;
&lt;p&gt;The full source code for this example is available at
&lt;a href="https://github.com/chrsmith/browserhack-demo"&gt;chrsmith/browserhack-demo&lt;/a&gt;.
This program deploys an instance of &lt;a href="http://coolwanglu.github.io/BrowserHack/"&gt;BrowserHack&lt;/a&gt;
(a web-based port of the seminal console game &lt;a href="https://www.nethack.org/"&gt;NetHack&lt;/a&gt;).
The repo also includes an example of assigning a custom domain name and
&lt;a href="https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html"&gt;ACM-managed SSL certificate&lt;/a&gt;,
useful capabilities supported by &lt;code&gt;StaticWebsite&lt;/code&gt; but omitted here for
brevity .&lt;/p&gt;
&lt;h2 id="resource-components"&gt;Resource Components&lt;/h2&gt;
&lt;p&gt;We glossed over what components are earlier on. This is an advanced
concept, so feel free to skip this section; but if you want to
understand how components work under the hood, read on.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;StaticWebsite&lt;/code&gt; type extends &lt;code&gt;pulumi.ComponentResource&lt;/code&gt;. A
&lt;code&gt;ComponentResource&lt;/code&gt; is a Pulumi-managed resource that aggregates other
resources into a higher-level abstraction.&lt;/p&gt;
&lt;p&gt;Pulumi will manage creating and updating the underlying resources like
normal, and you will see updates to them in the resource tree. The
difference is that by using the &lt;code&gt;ComponentResource&lt;/code&gt; abstraction we can
present those resources as logically grouped into a new, static website
type.&lt;/p&gt;
&lt;p&gt;This parent/child relationship between resources is declared using the
optional &lt;code&gt;pulumi.ResourceOptions&lt;/code&gt; type passed to Pulumi resources.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a code snippet from the code, where we create the S3 bucket and
specify its logical parent resource. Every child resource created is
passed &lt;code&gt;defaultResourceOptions&lt;/code&gt;, which sets the parent property to this
(the instance of StaticWebsite ).&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;// Default resource options for this component&amp;#39;s child resources.
&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;defaultResourceOptions&lt;/span&gt;: &lt;span class="kt"&gt;pulumi.ResourceOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;:&lt;span class="kt"&gt;this&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;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 logs bucket to store CloudFront request logs.
&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logsBucket&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="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;-logs`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&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;acl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;private&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;defaultResourceOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;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;Components are a powerful construct in Pulumi, and let you build larger
first class resources out of smaller ones. There are other benefits,
including seeing resource attribution in the CLI tree view, and the
richer visualization in the &lt;a href="https://app.pulumi.com/signin"&gt;Pulumi Service&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/creating-and-reusing-cloud-components-using-package-managers/pulumi-resource-visualization.png" alt="pulumi app graph"&gt;&lt;/p&gt;
&lt;p&gt;If you want to learn more about components, see the
&lt;a href="https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/pulumi/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="package-everything"&gt;Package Everything!&lt;/h2&gt;
&lt;p&gt;In this blog, you&amp;rsquo;ve seen how Pulumi&amp;rsquo;s code-centric approach to
infrastructure can make you more productive programming the cloud.
&amp;ldquo;Code&amp;rdquo; has a lot of advantages over static configuration languages, for
example rich IDE support. Another big advantage of using code, however,
is that it allows you to create useful abstractions that can be reused
in other programs&lt;/p&gt;
&lt;p&gt;With Pulumi being able to take advantage of existing package managers,
it&amp;rsquo;s super easy to create high-quality reusable components. We
demonstrated NPM here for Node.js programs, but Pulumi also supports
PyPI for Python, and traditional library techniques for Go.&lt;/p&gt;
&lt;p&gt;But don&amp;rsquo;t just take our words for it. Recently Mikhail Shilkov
&lt;a href="https://mikhail.io/2018/08/aws-lambda-warmer-as-pulumi-component/"&gt;blogged&lt;/a&gt;
about creating a Pulumi component to keep AWS Lambda functions warm. And
James Nugent &lt;a href="https://github.com/jen20/pulumi-aws-vpc"&gt;open sourced a component&lt;/a&gt;
that creates a properly subnetted AWS VPC. This is a great way to encode and share best
practices broadly. Over time, we look forward to seeing the many other
battle-tested components the community creates for common cloud
applications and infrastructure needs.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve created a Pulumi component that is useful or want some design
advice, come and join us in the &lt;a href="https://slack.pulumi.com/"&gt;Pulumi Community
Slack&lt;/a&gt; &amp;ndash; we&amp;rsquo;d love to hear from you!&lt;/p&gt;</description><author>Chris Smith</author><category>packages</category></item><item><title>Serving a Static Website on AWS with Pulumi</title><link>https://www.pulumi.com/blog/serving-a-static-website-on-aws-with-pulumi/</link><pubDate>Tue, 17 Jul 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/serving-a-static-website-on-aws-with-pulumi/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/serving-a-static-website-on-aws-with-pulumi/index.png" /&gt;
&lt;p&gt;Hello! This post covers using &lt;a href="https://www.pulumi.com/"&gt;Pulumi&lt;/a&gt; to create the
infrastructure for serving a static website on AWS. The full source code
for this example is &lt;a href="https://github.com/pulumi/examples/blob/master/aws-ts-static-website/index.ts"&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Setting up the infrastructure to serve a static website doesn&amp;rsquo;t sound
like it would be all that difficult, but when you consider HTTPS
certificates, content distribution networks, and attaching it to a
custom domain, integrating all the components can be quite daunting.&lt;/p&gt;
&lt;p&gt;Fortunately this is a task where Pulumi really shines. Pulumi&amp;rsquo;s
code-centric approach not only makes configuring cloud resources easier
to do and maintain, but it also eliminates the pain of integrating
multiple products together.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t a hypothetical benefit of using the Pulumi programming model.
We use a setup similar to the one described in this post for powering
our own static websites, like &lt;a href="https://www.pulumi.com/"&gt;www.pulumi.com&lt;/a&gt;
and &lt;a href="https://get.pulumi.com"&gt;get.pulumi.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;The architecture we will use for the website is to follow AWS &amp;ldquo;&lt;a href="https://aws.amazon.com/architecture/"&gt;Web Application Hosting&lt;/a&gt;&amp;rdquo;
reference architecture (&lt;a href="https://media.amazonwebservices.com/architecturecenter/AWS_ac_ra_web_01.pdf"&gt;pdf&lt;/a&gt;).
This integrates several AWS products:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/s3/"&gt;Amazon S3&lt;/a&gt;, used to store the
website&amp;rsquo;s contents&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/cloudfront/"&gt;Amazon CloudFront&lt;/a&gt;, a CDN
improving site performance&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/route53/"&gt;Amazon Route53&lt;/a&gt;, for managing DNS&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/certificate-manager/"&gt;Amazon Certificate Manager&lt;/a&gt;, for serving
content over HTTPS&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-it-works"&gt;How it Works&lt;/h2&gt;
&lt;p&gt;If you are already familiar with static hosting on AWS, feel free to go
directly to our &lt;a href="https://github.com/pulumi/examples"&gt;examples repository&lt;/a&gt; on GitHub, in the
aws-ts-static-website folder.&lt;/p&gt;
&lt;p&gt;If you are new to AWS, we&amp;rsquo;ll break down how to configure each component
using Pulumi.&lt;/p&gt;
&lt;h2 id="uploading-static-content-to-s3"&gt;Uploading static content to S3&lt;/h2&gt;
&lt;p&gt;Amazon S3 is a service for storing and retrieving files. This is where
we will store the website&amp;rsquo;s contents, and it is done by just creating an
S3 bucket resources and then crawling the www-directory when the Pulumi
program executes and creating an S3 bucket object resource for each
file.&lt;/p&gt;
&lt;p&gt;The main thing to point out here is that we set the contentType property
for each bucket object. This way, the right HTTP headers are returned so
that files are interpreted the right way. (e.g. images are rendered as
images.) The process for inferring the MIME type based on file extension
is done by the &lt;a href="https://www.npmjs.com/package/mime"&gt;mime NPM package&lt;/a&gt;.
No need to write the code on your own, rather we can just reuse an
existing library - written in a different programming language, no less! - from our TypeScript application.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const contentBucket = new aws.s3.Bucket(&amp;quot;contentBucket&amp;quot;,
{
...
});
...
const webContentsRootPath = path.join(process.cwd(), config.pathToWebsiteContents);
crawlDirectory(
webContentsRootPath,
(filePath: string) =&amp;gt; {
const relativeFilePath = filePath.replace(webContentsRootPath + &amp;quot;/&amp;quot;, &amp;quot;&amp;quot;);
const contentFile = new aws.s3.BucketObject(
relativeFilePath,
{
key: relativeFilePath,
acl: &amp;quot;public-read&amp;quot;,
bucket: contentBucket,
contentType: mime.getType(filePath) || undefined,
source: new pulumi.asset.FileAsset(filePath),
},
{
parent: contentBucket,
});
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the S3 bucket populated with the website&amp;rsquo;s contents, we need to be
able to serve it.&lt;/p&gt;
&lt;p&gt;S3 supports the ability to serve a website directly, but S3 when used
alone doesn&amp;rsquo;t support serving the content over HTTPS. Also, by serving
the content via a content distribution network, like Amazon CloudFront,
content can be served much faster by caching resources across the world.
Rather than requiring every web request to go directly to the source S3
bucket.&lt;/p&gt;
&lt;h2 id="setting-up-a-cloudfront-cdn-backed-by-s3"&gt;Setting up a CloudFront CDN backed by S3&lt;/h2&gt;
&lt;p&gt;There are a lot of details to configuring a CloudFront distribution,
from caching policies to rendering custom error pages. While some of
these details cannot be avoided, Pulumi programs being just code, allows
for using using constants like tenMinutes rather than the number 600.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const distributionArgs: aws.cloudfront.DistributionArgs = {
enabled: true,
aliases: [ config.targetDomain ],
...
// A CloudFront distribution can configure different cache behaviors based on the request path.
// Here we just specify a single, default cache behavior which is just read-only requests to S3.
defaultCacheBehavior: {
targetOriginId: contentBucket.arn,
viewerProtocolPolicy: &amp;quot;redirect-to-https&amp;quot;,
allowedMethods: [&amp;quot;GET&amp;quot;, &amp;quot;HEAD&amp;quot;, &amp;quot;OPTIONS&amp;quot;],
cachedMethods: [&amp;quot;GET&amp;quot;, &amp;quot;HEAD&amp;quot;, &amp;quot;OPTIONS&amp;quot;],
forwardedValues: {
cookies: { forward: &amp;quot;none&amp;quot; },
queryString: false,
},
minTtl: 0,
defaultTtl: tenMinutes,
maxTtl: tenMinutes,
},
...
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="attaching-it-to-a-domain-via-route53"&gt;Attaching it to a domain via Route53&lt;/h2&gt;
&lt;p&gt;Finally, we hook up our domain to the CloudFront distribution. We just
create an alias (A) record that aliases our own domain (e.g.
&lt;a href="https://www.pulumi.com/"&gt;www.pulumi.com&lt;/a&gt;) to the CloudFront distribution
(e.g. &lt;code&gt;dhy4niicdm7ba.cloudfront.net&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;There is a little extra processing we need to do to get the Amazon
Route53 Hosted Zone ID for the domain. But we can do that directly in
the Pulumi program, and taking advantage of TypeScript&amp;rsquo;s async/await
support.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Creates a new Route53 DNS record pointing the domain to the CloudFront distribution.
async function createAliasRecord(
targetDomain: string, distribution: aws.cloudfront.Distribution): Promise {
const domainParts = getDomainAndSubdomain(targetDomain);
const hostedZone = await aws.route53.getZone({ name: domainParts.parentDomain });
return new aws.route53.Record(
targetDomain,
{
name: domainParts.subdomain,
zoneId: hostedZone.zoneId,
type: &amp;quot;A&amp;quot;,
aliases: [
{
name: distribution.domainName,
zoneId: distribution.hostedZoneId,
evaluateTargetHealth: true,
},
],
});
}
const aRecord = createAliasRecord(config.targetDomain, cdn);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the Pulumi program is run, the DNS record is created and after a
few minutes to allow for worldwide propagation, the website is live. No
need to manually log into the AWS console, enter various DNS records,
etc. You can create and populate the S3 bucket, setup the CloudFront
CDN, and attach it to Route53 all within the same Pulumi program.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;With around &lt;a href="https://github.com/pulumi/examples/blob/master/aws-ts-static-website/index.ts"&gt;200 lines of code&lt;/a&gt;
we were able to integrate four different AWS products using Pulumi to
host a static website, served over HTTPS and from a world-wide CDN. Of
course there are other ways to host static websites too, and some
products or services can do all of that without needing any code at all.&lt;/p&gt;
&lt;p&gt;But the benefit of using Pulumi for this is that you are in control of
your infrastructure. If later you need to add more functionality, e.g.
require authentication or serving some routes dynamically, you can just
write a little more code to configure CloudFront. If you wanted to setup
a testing or staging environment on a different domain, that would just
be a matter of running the same Pulumi program in a different stack.&lt;/p&gt;
&lt;p&gt;Pulumi opens up a lot of possibilities and we are excited to see what
sorts of things people build using it. If you are interested in using
Pulumi for more sophisticated website hosting, or just have questions
about serving static files like described here, feel free to ask away on
our &lt;a href="https://slack.pulumi.com/"&gt;Pulumi Community Slack&lt;/a&gt;.&lt;/p&gt;</description><author>Chris Smith</author><category>aws</category><category>typescript</category></item></channel></rss>