<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Bradley</title>
    <description>The latest articles on DEV Community by Daniel Bradley (@danielrbradley).</description>
    <link>https://dev.to/danielrbradley</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F332319%2F10d33ba2-703f-4efa-84f8-6e4306025768.jpeg</url>
      <title>DEV Community: Daniel Bradley</title>
      <link>https://dev.to/danielrbradley</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danielrbradley"/>
    <language>en</language>
    <item>
      <title>Better Builds with Make: A Beginner's Guide</title>
      <dc:creator>Daniel Bradley</dc:creator>
      <pubDate>Mon, 14 Aug 2023 11:16:12 +0000</pubDate>
      <link>https://dev.to/danielrbradley/better-builds-with-make-a-beginners-guide-1n0</link>
      <guid>https://dev.to/danielrbradley/better-builds-with-make-a-beginners-guide-1n0</guid>
      <description>&lt;p&gt;If you've ever found yourself repeatedly typing commands to build and manage your projects, it might be time to explore a build automation tool like Make. In this article, we'll cover the basics of Make, which can significantly streamline your development process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The evolution of a project's build process often follows a familiar pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initial Setup&lt;/strong&gt;: Dump all your code into a repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic Instructions&lt;/strong&gt;: Add a README with rough build instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Script Overload&lt;/strong&gt;: Include scripts for common tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Bottlenecks&lt;/strong&gt;: Encounter slow builds due to unnecessary rebuilds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enter Build Systems&lt;/strong&gt;: Switch to a more efficient build system like Make.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's dive into Make and how it can make your builds better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Make's Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Targets and Rules
&lt;/h3&gt;

&lt;p&gt;In the world of Make, a &lt;strong&gt;target&lt;/strong&gt; represents a desired outcome, like a file you want to create. A &lt;strong&gt;rule&lt;/strong&gt; defines how to fulfill a target. Unlike some other build tools that use the concept of "tasks", Make focuses on generating files from other files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies and Execution
&lt;/h3&gt;

&lt;p&gt;Make is clever when deciding whether to rebuild a target or not. It compares the timestamps of files to determine if a target needs an update. It rebuilds a target when either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The target file doesn't exist.&lt;/li&gt;
&lt;li&gt;The target file is older than any of its dependencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a target isn't a real file, it's considered a &lt;a href="https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html"&gt;"phony" target&lt;/a&gt;, and Make will always run its associated rule.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;target&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;prerequites&lt;/span&gt;
    commands
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules are defined as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Target&lt;/strong&gt;: file path, followed by a colon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prerequisites&lt;/strong&gt;: files or targets required before running this rule, followed by a newline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commands&lt;/strong&gt;: Shell commands to be executed, indented by a tab.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html"&gt;Read more on the rule syntax in the docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variables
&lt;/h3&gt;

&lt;p&gt;Variables in Make help you avoid repetition. You can use variables to store values, filenames, or even results from commands. Variables can be used in target names, prerequisites and commands.&lt;/p&gt;

&lt;p&gt;Variables are assigned using an equals sign (&lt;code&gt;=&lt;/code&gt;) and their names are case-sensitive.&lt;/p&gt;

&lt;p&gt;Common expressions are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Simple values.&lt;/li&gt;
&lt;li&gt;Listing files using the &lt;code&gt;wildcard&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;Calling a shell script (e.g. &lt;code&gt;cat&lt;/code&gt; or &lt;code&gt;find&lt;/code&gt;).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; digital-tortoise
&lt;span class="nv"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; src/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;shell&lt;/span&gt; &lt;span class="nb"&gt;cat &lt;/span&gt;assets.txt&lt;span class="nf"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.gnu.org/software/make/manual/html_node/Using-Variables.html"&gt;Read more on using variables in the docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running &lt;code&gt;make&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;make [target]&lt;/code&gt; to build a specific target.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can specify multiple targets, separated by spaces.&lt;/li&gt;
&lt;li&gt;If you don't specify a target, the first rule in the makefile is the default target.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.gnu.org/software/make/manual/html_node/Running.html"&gt;Read more on running Make in the docs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Compiling and Running a Program
&lt;/h2&gt;

&lt;p&gt;Let's work through a basic example to solidify your understanding. Imagine you have a simple program written in a &lt;code&gt;src&lt;/code&gt; folder that you want to compile and run to generate an output file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Our default rule runs the "build" and "generate" rules
&lt;/span&gt;&lt;span class="nl"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build generate&lt;/span&gt;

&lt;span class="c"&gt;# Friendly aliases for running specific commands
&lt;/span&gt;&lt;span class="nl"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;bin/my-program&lt;/span&gt;
&lt;span class="nl"&gt;run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;out/result.txt&lt;/span&gt;

&lt;span class="c"&gt;# Tell make that the "all", "build" and "run" targets are 
# "phony" and aren't really files or folders.
&lt;/span&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;all build run&lt;/span&gt;

&lt;span class="c"&gt;# 1st target compiles our src into the bin directory
&lt;/span&gt;&lt;span class="nl"&gt;bin/my-program&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;src/*&lt;/span&gt;
    &lt;span class="c"&gt;# A pretend compiler which takes "src" and "out" args&lt;/span&gt;
    compile &lt;span class="nt"&gt;--src&lt;/span&gt; src/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;--out&lt;/span&gt; bin/my-program

&lt;span class="c"&gt;# 2nd step uses our program to generate a text output
&lt;/span&gt;&lt;span class="nl"&gt;out/result.txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;bin/my-program&lt;/span&gt;
    bin/my-program &lt;span class="nt"&gt;--write-to&lt;/span&gt; out/result.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling Non-File Targets
&lt;/h2&gt;

&lt;p&gt;Sometimes, you need to handle targets that aren't single files. You can create sentinel files to represent these targets and their last update times.&lt;/p&gt;

&lt;p&gt;My preference is to organize all these sentinel files into a single &lt;code&gt;.make&lt;/code&gt; folder. This directory should be ignored and not committed by Git.&lt;/p&gt;

&lt;p&gt;There's a few tricks we'll use here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Variables are computed before any rules run, so we create a variable we won't actually use to run a script to ensure our &lt;code&gt;.make&lt;/code&gt; folder exists before we try to write files to it.&lt;/li&gt;
&lt;li&gt;There's a special variable &lt;code&gt;$@&lt;/code&gt; which is the name of the current target being run. We use this in the last command of our rule to update the timestamp of our sentinel file (&lt;code&gt;.make/test&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;We prefix our &lt;code&gt;touch&lt;/code&gt; command with &lt;code&gt;@&lt;/code&gt; to avoid printing it to the output.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ensure the .make folder exists when starting make
&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;shell&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .make&lt;span class="nf"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Add a phony target for "test" as an alias
&lt;/span&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;
&lt;span class="nl"&gt;test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;.make/test&lt;/span&gt;

&lt;span class="nl"&gt;.make/test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;# Run our testing tool&lt;/span&gt;
    run-tests &lt;span class="nt"&gt;--all&lt;/span&gt;
    &lt;span class="c"&gt;# Mark .make/test as up-to-date&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Example: Compiling TypeScript
&lt;/h2&gt;

&lt;p&gt;Let's see how you can use Make to manage a TypeScript project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ensure the .make folder exists when starting make
&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;shell&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .make&lt;span class="nf"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Find all our source files
&lt;/span&gt;&lt;span class="nv"&gt;SRC&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; src/&lt;span class="k"&gt;*&lt;/span&gt;.ts&lt;span class="nf"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Default to only building
&lt;/span&gt;&lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;
&lt;span class="c"&gt;# Alias dist as build
&lt;/span&gt;&lt;span class="nl"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;dist&lt;/span&gt;
&lt;span class="c"&gt;# Alias dist sentinel target
&lt;/span&gt;&lt;span class="nl"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;.make/dist&lt;/span&gt;
&lt;span class="c"&gt;# Alias package output file
&lt;/span&gt;&lt;span class="nl"&gt;pack&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;bin/package.tgz&lt;/span&gt;
&lt;span class="c"&gt;# Mark aliases as phony (not real files)
&lt;/span&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;default build dist pack&lt;/span&gt;

&lt;span class="c"&gt;# Install packages if definitions changed
&lt;/span&gt;&lt;span class="nl"&gt;.make/node_modules&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;package.json yarn.lock tsconfig.json&lt;/span&gt;
    yarn &lt;span class="nb"&gt;install&lt;/span&gt;
    &lt;span class="c"&gt;# Mark .make/node_modules as up-to-date&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;

&lt;span class="c"&gt;# Build dist directory from source and packages
&lt;/span&gt;&lt;span class="nl"&gt;.make/dist&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(SRC) .make/node_modules&lt;/span&gt;
    yarn tsc &lt;span class="nt"&gt;--outDir&lt;/span&gt; dist
    &lt;span class="c"&gt;# Mark .make/dist as up-to-date&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;

&lt;span class="c"&gt;# Build package zip from dist directory
&lt;/span&gt;&lt;span class="nl"&gt;bin/package.tgz&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;.make/dist&lt;/span&gt;
    &lt;span class="nb"&gt;cd &lt;/span&gt;dist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;--gzip&lt;/span&gt; &lt;span class="nt"&gt;-cf&lt;/span&gt; bin/package.tgz .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Makefile installs dependencies, compiles TypeScript to JavaScript, and packages the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips and Recap
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use Meaningful Targets&lt;/strong&gt;: Name targets by their output file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Sentinel Files&lt;/strong&gt;: For non-file targets, create sentinel files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage Dependencies&lt;/strong&gt;: Model dependencies using file targets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provide Phony Targets&lt;/strong&gt;: Create helpful phony targets for easier use.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Make is a powerful tool to streamline your build process. By understanding its concepts and syntax, you can significantly improve your development workflow.&lt;/p&gt;

&lt;p&gt;For an extended guide to makefiles I'd recommend downloading the free &lt;a href="https://makefile.site/#handbook"&gt;Modern Make Handbook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a deeper dive, consult &lt;a href="https://www.gnu.org/software/make/manual/html_node/"&gt;the official Make documentation&lt;/a&gt;. Happy building!&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Common Pitfalls To Avoid
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Each command is run in its own shell by default&lt;/strong&gt;. This means that setting an environment variable won't work unless you chain the commands together with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The &lt;code&gt;wildcard&lt;/code&gt; function isn't recursive&lt;/strong&gt;. Use &lt;code&gt;$(shell find ...)&lt;/code&gt; as an alternative.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Beware of version-specific features&lt;/strong&gt;. There's lots of fancy features in the docs, but MacOS ships with a very old version (3.81 from 2006) so won't work out of the box. Either stick to the basic features or ask MacOS users to install a newer version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't use directories as targets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't use committed files as targets.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why avoid using directories as targets?
&lt;/h3&gt;

&lt;p&gt;Using a directory as a target is generally fine if you're only creating the directory itself. However, if you're also creating files within that directory, the issue arises. The problem is that the "atomic" nature of the target, which ensures it's either fully updated or not, is compromised when dealing with directories.&lt;/p&gt;

&lt;p&gt;Targets are considered "atomic" based on timestamps, which determine if something is up-to-date. To maintain this atomicity, timestamps should only be updated after the entire target is successfully completed. When you create a directory as part of the target and it's successful, the timestamp becomes up-to-date. If subsequent commands fail after the directory creation (such as when writing files), the target remains up-to-date despite the failure, and it won't be rerun.&lt;/p&gt;

&lt;p&gt;Assuming the directory already exists and updating the timestamp at the end could lead to problems if the directory is accidentally deleted. Manually recreating the directory would give it a newer timestamp, causing the target not to run. While you can use the force option in "make" to work around this, it's not an ideal user experience compared to using sentinel files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why avoid using committed files as targets?
&lt;/h3&gt;

&lt;p&gt;The issue with using committed files as targets stems from the expectation that committed files are manually maintained sources. This is the same problem as the question "Why shouldn't target files be edited?" When committed files are treated as targets, it can create confusion when version control updates the file itself and therefore treats the files as already up-to-date after a checkout.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>programming</category>
      <category>learning</category>
      <category>development</category>
    </item>
    <item>
      <title>Replacing Build Servers With Pulumi + AWS</title>
      <dc:creator>Daniel Bradley</dc:creator>
      <pubDate>Tue, 11 Feb 2020 09:00:50 +0000</pubDate>
      <link>https://dev.to/danielrbradley/replacing-build-servers-with-pulumi-aws-28fm</link>
      <guid>https://dev.to/danielrbradley/replacing-build-servers-with-pulumi-aws-28fm</guid>
      <description>&lt;p&gt;Tired of restarting your Jenkins box because something's broken?&lt;br&gt;Don't like configuring your builds using a slow web UI?&lt;br&gt;&lt;em&gt;Ditch your flaky Jenkins box and use AWS CodeBuild configured via Pulumi!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the plan, it's a little inception-y, so hang tight...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a GitHub repository&lt;/li&gt;
&lt;li&gt;Describe an AWS CodeBuild project using TypeScript that will watch itself&lt;/li&gt;
&lt;li&gt;Deploy the the infrastructure using Pulumi&lt;/li&gt;
&lt;li&gt;Watch it deploy itself as we push changes!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5fgpu3jdpo1hwdunas0.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5fgpu3jdpo1hwdunas0.jpeg" alt="Yo dawg I heard you liked CodeBuild, so I built CodeBuild in CodeBuild so you can configure your CodeBuild projects via CodeBuild" width="500" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't panic, it should become clearer as we get into the code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing The Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://aws.amazon.com/codebuild/" rel="noopener noreferrer"&gt;AWS CodeBuild&lt;/a&gt;&lt;/strong&gt; - &lt;em&gt;Build and test code with continuous scaling. Pay only for the build time you use.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;CodeBuild is very similar to many other build services available but has the added benefit of being tightly integrated into the AWS ecosystem e.g. billing, permissions, automation.&lt;/p&gt;

&lt;p&gt;We'll also be using the AWS CLI, so &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html" rel="noopener noreferrer"&gt;go install that&lt;/a&gt; if you've not already got it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.pulumi.com" rel="noopener noreferrer"&gt;Pulumi&lt;/a&gt;&lt;/strong&gt; - &lt;em&gt;Modern infrastructure as code using real languages.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sign up for an account - it's free to use for personal use. The account will manage the state of your project deployments.&lt;/p&gt;

&lt;p&gt;I'll be using TypeScript here, but you're also able to use a number of other polular languages to achive the same result with Pulumi.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pulumi Project Setup
&lt;/h2&gt;

&lt;p&gt;Follow through the Pulumi &lt;a href="https://www.pulumi.com/docs/get-started/aws/" rel="noopener noreferrer"&gt;getting started guide for AWS&lt;/a&gt; to install the CLI tools, configure your environment and create a blank &lt;code&gt;aws-typescript&lt;/code&gt; project called &lt;code&gt;build-setup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; As an example we'll pretend we're pushing it to GitHub at &lt;code&gt;https://github.com/danielrbradley/build-setup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should now have an &lt;code&gt;index.ts&lt;/code&gt; file with something that looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/pulumi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;awsx&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/awsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create an AWS resource (S3 Bucket)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-bucket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Export the name of the bucket&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bucketName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The First CodeBuild Project
&lt;/h2&gt;

&lt;p&gt;Delete the lines that created the S3 bucket and exported the name of the bucket - we don't need them.&lt;/p&gt;

&lt;p&gt;Here's what we need to setup a CodeBuild project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/pulumi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;awsx&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/awsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildProject&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;codebuild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build-setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;serviceRole&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TODO&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GITHUB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://github.com/danielrbradley/build-setup.git&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LINUX_CONTAINER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;computeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BUILD_GENERAL1_SMALL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws/codebuild/standard:3.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NO_ARTIFACTS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down line-by-line:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;const buildProject = new aws.codebuild.Project('build-setup'&lt;/code&gt;
This creates us a new Pulumi resource representing a CodeBuild project. Creating this doesn't immediately create the resource in AWS but describes to Pulumi what we will want to deploy in the future.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;serviceRole: 'TODO'&lt;/code&gt; We'll skip over this right now and fix it below.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;source: {...}&lt;/code&gt; - where should CodeBuild get the source code to build? We're using GitHub, but you can also use other sources too.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;environment: {...}&lt;/code&gt; What kind of computer do you need for running your build - Linux or Windows, small &amp;amp; cheap or more powerful, the operating system (a docker image)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;artifacts&lt;/code&gt; Where should any output files be written? This first build won't have any.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Permissions
&lt;/h2&gt;

&lt;p&gt;Back to that &lt;code&gt;serviceRole&lt;/code&gt; property. When the build is run, the role we specify here defines what in our AWS account is made accessed to the build job. Because we're running the build inside AWS, we don't need to use access keys, it inherits all the access of the role.&lt;/p&gt;

&lt;p&gt;Create a new role for your build to run as. Add this before your CodeBuild project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildRole&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="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build-setup-role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;assumeRolePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2012-10-17&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;codebuild.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sts:AssumeRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This defines a role to be created and specifies through the "assume role policy" that only the CodeBuild service is allowed to use this role.&lt;/p&gt;

&lt;p&gt;Now, we need to specify what CodeBuild will be allowed to do when acting as this role. There's two ways we can do this: define our own "inline policy" listing specific services, actions and resources; or attach an existing policy to the role. Here we'll go for the latter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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="nc"&gt;RolePolicyAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build-setup-policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buildRole&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;policyArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arn:aws:iam::aws:policy/AdministratorAccess&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt; To keep the example simple and concise, we're just going to give the role administrator access. I would exercise caution in using this exact approach as it means that anyone who can pushes code to your GitHub repository can change absolutely anything in your AWS account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Reading line-by-line:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;new aws.iam.RolePolicyAttachment('build-setup-policy'&lt;/code&gt;
We're creating a resource which 'attaches' a policy to a role and giving that attachment the name &lt;code&gt;build-setup-policy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role: buildRole,&lt;/code&gt; The role you want to attach to - which is the role created in the previous step. This can either be a role object or a string containing a role &lt;a href="https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html" rel="noopener noreferrer"&gt;ARN&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;policyArn: ...&lt;/code&gt; The &lt;a href="https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html" rel="noopener noreferrer"&gt;ARN&lt;/a&gt; string of the policy to attach. &lt;code&gt;AdministratorAccess&lt;/code&gt; is an AWS managed, built-in policy giving complete unrestricted access to your AWS account.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now update your &lt;code&gt;buildProject&lt;/code&gt;, &lt;code&gt;serviceRole&lt;/code&gt; property to point to your new &lt;code&gt;buildRole&lt;/code&gt;'s &lt;code&gt;arn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildProject&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;codebuild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build-setup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;serviceRole&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buildRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;//---------- SNIP ----------//&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authenticating CodeBuild with GitHub
&lt;/h2&gt;

&lt;p&gt;Go to GitHub and &lt;a href="https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line" rel="noopener noreferrer"&gt;create a "personal access token"&lt;/a&gt;. When creating the token, you'll need to tick the &lt;code&gt;repo&lt;/code&gt; and &lt;code&gt;admin:repo_hook&lt;/code&gt; scopes.&lt;/p&gt;

&lt;p&gt;Pulumi has built-in configuration and even supports encrypting individual variables within the project. Copy the created access token and, in your command line, run the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pulumi config &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--secret&lt;/span&gt; github-token YOUR_SECRET_PERSONAL_ACCESS_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add a credentials resource in your &lt;code&gt;index.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&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="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;codebuild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SourceCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;github-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;authType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PERSONAL_ACCESS_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;serverType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GITHUB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requireSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;github-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new source credential resource with the name 'github-token' containing your GitHub Personal Access Token. The &lt;code&gt;pulumi.Config()&lt;/code&gt; class lets us read the config we just saved using your command line, and decrypt the secret's value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triggering Builds
&lt;/h2&gt;

&lt;p&gt;If you deployed this now you'd get a build that you could manually start and would build whatever's in your repository. However, it would be more useful if it automatically started building as soon as you pushed new code to GitHub!&lt;/p&gt;

&lt;p&gt;To listen for changes from GitHub we need a "webhook". Add the following resource to build on each new commit pushed to master...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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;codebuild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Webhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build-setup-webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buildProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;filterGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EVENT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUSH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HEAD_REF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refs/heads/master&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line-by-line again...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define the resource type to create and give it a name&lt;/li&gt;
&lt;li&gt;Pass the name of the CodeBuild project to trigger&lt;/li&gt;
&lt;li&gt;Filter to only the events you're interested in: when a commit is pushed to the &lt;code&gt;master&lt;/code&gt; branch&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Authenticating CodeBuild with Pulumi
&lt;/h2&gt;

&lt;p&gt;For Pulumi to work in an automated environment you need to &lt;a href="https://app.pulumi.com/danielrbradley/settings/tokens" rel="noopener noreferrer"&gt;create a new Pulumi "Access Token"&lt;/a&gt;. Copy the token and let's use Pulumi's encrypted config again to store it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pulumi config &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--secret&lt;/span&gt; pulumi-access-token YOUR_PULUMI_ACCESS_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWS SSM Parameter Store is a great way to store sensitive values like this within your infrastructure. Let's create a resource to hold the secret value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pulumiAccessToken&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;ssm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pulumi-access-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;String&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requireSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pulumi-access-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need the access token in the build environment. Let's change the &lt;code&gt;buildProject&lt;/code&gt; resource to load the token from the SSM parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildProject&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;codebuild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build-setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//---------- SNIP ----------//&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//---------- SNIP ----------//&lt;/span&gt;
    &lt;span class="na"&gt;environmentVariables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PARAMETER_STORE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PULUMI_ACCESS_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pulumiAccessToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;//---------- SNIP ----------//&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  CodeBuild Build Specification
&lt;/h2&gt;

&lt;p&gt;The final step of configuration is to tell CodeBuild how to build our project.&lt;/p&gt;

&lt;p&gt;CodeBuild will automatically look for a file called &lt;code&gt;buildspec.yml&lt;/code&gt; at the root of your repository - let's create that now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;

&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runtime-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;nodejs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl -fsSL https://get.pulumi.com | sh&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PATH=$PATH:/root/.pulumi/bin&lt;/span&gt;
  &lt;span class="na"&gt;pre_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pulumi login --non-interactive&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pulumi up --non-interactive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running through the sections line-by-line:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the Node.js v12.x runtime&lt;/li&gt;
&lt;li&gt;Download and install Pulumi&lt;/li&gt;
&lt;li&gt;Make &lt;code&gt;pulumi&lt;/code&gt; available on the &lt;code&gt;$PATH&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Restore packages using NPM&lt;/li&gt;
&lt;li&gt;Log in to Pulumi (uses the &lt;code&gt;PULUMI_ACCESS_TOKEN&lt;/code&gt; environment variable)&lt;/li&gt;
&lt;li&gt;Run Pulumi deploy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;--non-interactive&lt;/code&gt; option is available on all Pulumi CLI commands to ensure that it doesn't prompt for input at any stage which would cause the build to hang and timeout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our first deployment
&lt;/h2&gt;

&lt;p&gt;Right, that's all the coding done! Now to do our first deployment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your command line&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;pulumi up&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You'll get a preview of what it's about to do, then select "Yes" to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;The deployment should only take a couple of minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You created a GitHub project containing a TypeScript file which contains the definitions of a CodeBuild project to create.&lt;/li&gt;
&lt;li&gt;This CodeBuild project is configured to watch for changes to the GitHub project and re-deploy itself on each change.&lt;/li&gt;
&lt;li&gt;Deployed the first version from your local machine.&lt;/li&gt;
&lt;li&gt;Now you can add a few lines of code and push it to GitHub to setup whole new build pipelines!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Getting to the point of the first deploy takes some work, but once you're up and running this is a very efficient and elegant process for managing build projects. At work we've been testing this setup for around a year and have 28 projects configured using this method. The feedback from every developer has been overwhelmingly positive compared to our old Jenkins setup.&lt;/p&gt;

&lt;p&gt;From here, there's many interesting avenues to explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding more repositories to build&lt;/li&gt;
&lt;li&gt;Testing changes in pull requests&lt;/li&gt;
&lt;li&gt;Using CloudWatch and Lambda to monitor builds and alert you to failures&lt;/li&gt;
&lt;li&gt;Use CloudWatch scheduled triggers for nightly build tasks&lt;/li&gt;
&lt;li&gt;Abstracting the code to reduce the amount of code you have to write for each new GitHub repository you want to build&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Would love to hear about where you take this!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>pulumi</category>
      <category>typescript</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
