<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Work in Progress</title>
 <link href="http:///atom.xml" rel="self"/>
 <link href="http:///"/>
 <updated>2025-01-16T16:07:59-05:00</updated>
 <id>http:///</id>
 <author>
   <name></name>
   <email></email>
 </author>

 
 <entry>
   <title>Securing Spring Web Application with OAuth2</title>
   <link href="http:///posts/spring-app-with-oauth2-login/"/>
   <updated>2025-01-16T00:00:00-05:00</updated>
   <id>http:///posts/spring-app-with-oauth2-login</id>
   <content type="html">&lt;p&gt;&lt;img src=&quot;/assets/2025-01-16-old-padlock.jpg&quot; alt=&quot;Image of Antique Lock&quot; class=&quot;right&quot; width=&quot;200&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s not uncommon to punt on account management when starting a new project and, instead, to add a database table to store accounts. The next step is to fill that table in with some sample data and move on to working on real features.&lt;/p&gt;

&lt;p&gt;This might seem like an easy solution but inevitably this ends up becoming a core part of the application. It won’t be long before we find ourselves being asked to implement an administrative section where new accounts can be created and passwords changed. Eventually we’ll be tasked with coding up the ability to validate email address and reset passwords. The work around maintaining these accounts seems to never end!&lt;/p&gt;

&lt;p&gt;Lucky for us there is an alternative: make someone else manage these accounts. 😉 At first this might seem like more work but I assure you, it is not. Vendors are lining up to implement all of these annoying features; things like changing passwords and validating email addresses or changing the account name, and from our point-of-view they are doing this for free. People interested in using your application might then register with their (Google, GitHub, Microsoft, etc.) account leaving you free and clear to focus on what your application actually does.&lt;/p&gt;

&lt;h2 id=&quot;how-does-it-work&quot;&gt;How Does It Work?&lt;/h2&gt;

&lt;p&gt;To keep things contained and manageable we’re going to use &lt;a href=&quot;https://www.keycloak.org/documentation&quot;&gt;Keycloak&lt;/a&gt; instead of forcing you to register with Google or GitHub or whatever. We won’t spend a lot of time on managing Keycloak as that’s kind of out-of-scope but we will be giving you a place to start if you decide to use Keycloak for account management.&lt;/p&gt;

&lt;p&gt;Next we’ll setup &lt;a href=&quot;https://docs.spring.io/spring-security/reference/servlet/oauth2/login/core.html&quot;&gt;Spring’s OAuth2 Login support&lt;/a&gt;, forcing everyone that visits our application to be logged in. The way this works is that when someone hits our application, Spring will look to see if they are logged into our OAuth2 provider. If they are not, it will forward the browser to the provider so that it can handle logging the person in. Once logged in successfully the OAuth2 provider will then forward the browser back to our application, providing the account information to our application.&lt;/p&gt;

&lt;p&gt;Lastly when people logout of our application we’ll clear their session data, we will also let our OAuth2 provider know so that it can invalidate that session for the account. This will keep things neat and tidy, once someone logs out they will need to log into the provider again in order to use our application.&lt;/p&gt;

&lt;h2 id=&quot;checkout-the-github-project&quot;&gt;Checkout the GitHub Project&lt;/h2&gt;

&lt;p&gt;To spare you the hassle of setting up Keycloak and a database server and creating a test account, I’ve done all of that work for you. All you need to do is checkout the project that goes with this article. It has a &lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;Docker Compose&lt;/a&gt; script and some seed data for Keycloak that will get you started.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/cmiles74/minimal-spring-oauth2login&quot;&gt;https://github.com/cmiles74/minimal-spring-oauth2login&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick a location on your machine and then go ahead and clone the project.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;git clone https://github.com/cmiles74/minimal-spring-oauth2login
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Aside from the demo project you will need the following installed and setup and working on your workstation.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.docker.com&quot;&gt;Docker or Docker Desktop&lt;/a&gt; (if you’re on Linux, Docker, otherwise Docker Desktop)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://adoptium.net/temurin/releases/&quot;&gt;Java JDK&lt;/a&gt;, version 21 or later&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com&quot;&gt;Git&lt;/a&gt; for source control management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it! If you’re a Java developer (and you should be if you are interested in this article at all) then you probably have all of this stuff setup and working already. 😉&lt;/p&gt;

&lt;h2 id=&quot;bring-up-the-supporting-services&quot;&gt;Bring Up the Supporting Services&lt;/h2&gt;

&lt;p&gt;Once you have the project checked out you’ll need to copy the default Docker environment variables file over. There’s no need to customize these but once you finish this article you’ll know where they are if you want to change them.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;cp env-sample .env
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With that done you can bring up the Docker stack.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;docker compose up
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will download the PostgreSQL and Keycloak images, bring up their containers and then (in the case of Keycloak) load in some seed data. This seed data includes the “realm” and a demonstration account. The credentials for that demo account are…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Account Name:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;someone&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Account Password:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;password&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keycloak will be running on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8080&lt;/code&gt;, you can check it out at the following URL:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://localhost:8080/&quot;&gt;http://localhost:8080/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The credentials for the Keycloak administrator account on the “master” realm are…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Account Name:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Account Password:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;put-the-pieces-together&quot;&gt;Put the Pieces Together&lt;/h2&gt;

&lt;p&gt;Typically in a tutorial like this I’d make you create the files by hand and type in (or copy and paste) their contents. This one is a little different in that I needed to have you clone the repository to get the Docker stack and the sample data. Instead we’ll look over the key files and talk about their contents.&lt;/p&gt;

&lt;h3 id=&quot;add-spring-boot-dependencies&quot;&gt;Add Spring Boot Dependencies&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is add the &lt;a href=&quot;https://spring.io/projects/spring-boot&quot;&gt;Spring Boot&lt;/a&gt; dependencies that we know we’ll need to the project. If you look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.gradle&lt;/code&gt; file at the root of the project, you can see a pretty short and stock project. Down in the dependencies section you can see the four that we’ve added.&lt;/p&gt;

&lt;div class=&quot;language-gradle highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;implementation&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;org.springframework.boot:spring-boot-starter&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;implementation&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;org.springframework.boot:spring-boot-starter-web&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;implementation&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;org.springframework.boot:spring-boot-starter-thymeleaf&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;implementation&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;org.springframework.boot:spring-boot-starter-oauth2-client&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;developmentOnly&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;org.springframework.boot:spring-boot-devtools&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;testImplementation&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;org.springframework.boot:spring-boot-starter-test&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;testRuntimeOnly&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;org.junit.platform:junit-platform-launcher&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spring-boot-starter&lt;/code&gt; because we’re using Spring Boot, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;starter-web&lt;/code&gt; package let’s us build a web application. We then pull in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start-thymeleaf&lt;/code&gt; so that we can display some simple pages. Lastly we add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spring-boot-start-oauth2-client&lt;/code&gt;, this is the package that enables us to communicate with an OAuth2 provider.&lt;/p&gt;

&lt;h3 id=&quot;setup-our-application-configuration&quot;&gt;Setup our Application Configuration&lt;/h3&gt;

&lt;p&gt;Our Spring application will need to know what to call itself, what port it should run on and how to communicate with our OAuth2 provider. All of this is configured in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.yml&lt;/code&gt; file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/main/resources&lt;/code&gt; directory. The interesting bit here is the OAuth2 provider configuration.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;oauth2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;registration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;keycloak-client&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;keycloak-provider&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;client-id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${OAUTH2_CLIENT_ID}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;client-secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${OAUTH2_CLIENT_SECRET}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;authorization-grant-type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;authorization_code&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openid,profile&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;keycloak-provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;issuer-uri&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${OAUTH2_ISSUER_URL}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This configuration points our application at our local Keycloak server but all OAuth2 providers are configured in a similar manner. Take note of the stanza under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registration&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provider&lt;/code&gt;, we are naming our OAuth2 “registration” and provider (“keycloak-client” and “keycloak-provider”, respectively).&lt;/p&gt;

&lt;p&gt;Our client identifier and secret are coming from your OAuth2 provider (and then stored in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; file at the root of the project). The authorization grant type and scope will likely be the same for all providers. Lastly the issuer URI lets our application know where to collect information about the provider; this should be provided to you by your OAuth2 provider.&lt;/p&gt;

&lt;h3 id=&quot;configure-spring-security&quot;&gt;Configure Spring Security&lt;/h3&gt;

&lt;p&gt;Next we need to tell &lt;a href=&quot;https://docs.spring.io/spring-security/reference/index.html&quot;&gt;Spring Security&lt;/a&gt; how we’d like it to secure our application. We’ll have it require security for all pages (except the “goodbye” page) and let it know that we want to use OAuth2 to handle logging people in. This is in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SecurityConfig.java&lt;/code&gt; file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/main/java/com.nervestaple.demo&lt;/code&gt; directory.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nd&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@EnableWebSecurity&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SecurityConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClientRegistrationRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientRegistrationRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setClientRegistrationRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;ClientRegistrationRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientRegistrationRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clientRegistrationRepository&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientRegistrationRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SecurityFilterChain&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;securityFilterChain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;HttpSecurity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requestMatchers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/goodbye&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;permitAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;anyRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authenticated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;oauth2Login&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Customizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;withDefaults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logoutSuccessHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oidcLogoutSuccessHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
                                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;invalidateHttpSession&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clearAuthentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OidcClientInitiatedLogoutSuccessHandler&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;oidcLogoutSuccessHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OidcClientInitiatedLogoutSuccessHandler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;successHandler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OidcClientInitiatedLogoutSuccessHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientRegistrationRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;successHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setPostLogoutRedirectUri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://localhost:8085/goodbye&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;successHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When someone logs into our application their information is stored in the client registration repository. We keep it simple here, if you want to learn more about how this works then &lt;a href=&quot;https://docs.spring.io/spring-authorization-server/reference/guides/how-to-dynamic-client-registration.html&quot;&gt;checkout the Spring documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next we configure the security for our application with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SecurityFilterChain&lt;/code&gt;. This code is not so bad, you can see that we require authorization on all URLs except &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/goodbye&lt;/code&gt;, then we configure &lt;a href=&quot;https://docs.spring.io/spring-security/reference/servlet/oauth2/login/core.html&quot;&gt;Spring’s OAuth2 Login&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oauth2login&lt;/code&gt;) with default settings. Lastly we configure logout, in addition to clearing the session and authentication data we &lt;em&gt;also&lt;/em&gt; invoke our OIDC logout success handler and redirect to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/goodbye&lt;/code&gt; URL.&lt;/p&gt;

&lt;p&gt;With this configuration, two URLs are added to our application by Spring’s OAuth2 Login component.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://localhost:8085/oauth2/authorization/keycloak-client&quot;&gt;http://localhost:8085/oauth2/authorization/keycloak-client&lt;/a&gt;, this endpoint checks to see if the browser is logged into our provider and forwards the browser to the provider for login if they are not&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://localhost:8085/oauth2/code/keycloak-client&quot;&gt;http://localhost:8085/oauth2/code/keycloak-client&lt;/a&gt;, this endpoint handles the response from our OAuth2 provider and forwards the browser back to our application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last thing of interest is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidcLogooutSuccessfulHandler&lt;/code&gt;, this bit of code is looking up the logged in account in our client registration repository and using that information to contact our OAuth2 provider to log them out of this particular session (the OIDC back-channel logout). If that’s successful then the browser is forwarded to our goodbye page.&lt;/p&gt;

&lt;h3 id=&quot;who-is-logged-in&quot;&gt;Who is Logged In?&lt;/h3&gt;

&lt;p&gt;The rest of our skeleton web application is in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HomeController.java&lt;/code&gt; file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/main/java/com.nervestaple.demo&lt;/code&gt; directory. Here is were we setup our two endpoints (that serve HTML pages), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/goodbye&lt;/code&gt;. These are pretty dull and routine, the only interesting bit is in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;home&lt;/code&gt; method.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nd&quot;&gt;@GetMapping&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;home&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Model&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SecurityContextHolder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAuthentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oidcUser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;OidcUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authentication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getPrincipal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;accountName&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oidcUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getPreferredUsername&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;index&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We are requiring the browser be authenticated with our OAuth2 provider before they can view this page, we can get the security context and authentication data without fear. We then cast this to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OidcUser&lt;/code&gt; (we only support OIDC authentication in this demo app, a more complex app would need to take more care) and then digout the name of the account that’s logged in so that we can say “hello”.&lt;/p&gt;

&lt;h3 id=&quot;building-and-starting-the-application&quot;&gt;Building and Starting the Application&lt;/h3&gt;

&lt;p&gt;Building the application is easy with &lt;a href=&quot;https://gradle.org&quot;&gt;Gradle&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;./gradlew build
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a small application, it should build quickly and without issue. Now you can run the application.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;java -jar build/libs//demo-0.0.1-SNAPSHOT.jar
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Go ahead and visit the application!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://localhost:8085/&quot;&gt;http://localhost:8085/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should be immediately directed to the login page of our OAuth2 provider (Keycloak in this case). Type in the credentials for the demo account…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Account Name:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;someone&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Account Password:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;password&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2025-01-16-keycloak-login.png&quot; alt=&quot;OAuth2 Provider Login Form&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And you should now be directed to our page where the application says “hello” to you.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2025-01-16-app-hello.png&quot; alt=&quot;Our Application&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you click on the logout link your session with the OAuth2 provider will be ended and you’ll be dropped at the goodbye page. If you go back to the application you will need to login once again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2025-01-16-app-goodbye.png&quot; alt=&quot;Our Application Says Goodbye&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;summing-up&quot;&gt;Summing Up&lt;/h2&gt;

&lt;p&gt;We have very little code but we’ve done quite a lot! We have said goodbye to account management, instead letting our OAuth2 provider handle that for us. We have a functional and reasonably attractive log in and out process, which is also pretty secure to boot.&lt;/p&gt;

&lt;p&gt;One thing to note is that we are not depending on the OAuth2 provider to handle things like idle log out. Once the browser comes back from the OAuth2 provider with a valid token we log them into our application, from then on our application takes responsibility for deciding when someone has been idle for long enough that they need to be logged out (this is a function of Spring Security and there are various knobs to adjust this).&lt;/p&gt;

&lt;p&gt;In my opinion this is a reasonable way to do things; when your OAuth2 provider provides a token to your application that token is used both to identify the account as well as to access &lt;em&gt;other services provided by the OAuth2 provider.&lt;/em&gt; That is, the expiration time on that token is meant to indicate when it’s no longer valid &lt;em&gt;for use with the OAuth2 provider,&lt;/em&gt; it’s not saying anything about how your application functions. Likewise the refresh token provided is meant to be used if the expiration time has passed and more requests need to be made of the OAuth2 provider; in practice it’s best to get all communication with the provider out of the way immediately after the login.&lt;/p&gt;

&lt;p&gt;I hope you find this tutorial useful! If you have any questions or comments or if you find any errors or mistakes please let me know ASAP. 🙂&lt;/p&gt;

&lt;hr /&gt;

</content>
 </entry>
 
 <entry>
   <title>Getting Started with Medley Interlisp</title>
   <link href="http:///posts/getting-started-interlisp/"/>
   <updated>2024-07-08T00:00:00-04:00</updated>
   <id>http:///posts/getting-started-interlisp</id>
   <content type="html">&lt;p&gt;&lt;img src=&quot;/assets/2024-07-08-medley-interlisp-logo.png&quot; alt=&quot;Medley Logo&quot; class=&quot;right&quot; width=&quot;200&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I have long been interested in computing environments where the development tools are an integral part of the whole system. This started out ages and ages ago, when I started learning to write software in &lt;a href=&quot;https://en.wikipedia.org/wiki/Java_%28programming_language%29&quot;&gt;Java&lt;/a&gt;. When Sun released the first version of Java it was often compared to &lt;a href=&quot;https://www.computinghistory.org.uk/sec/16320/Smalltalk-80/&quot;&gt;Smalltalk&lt;/a&gt; (in fact, &lt;a href=&quot;https://en.wikipedia.org/wiki/VisualAge&quot;&gt;IBM’s first Java IDE&lt;/a&gt; was written in Smalltalk). At that time the Smalltalk environments were more compelling and easier to use than Java and I was somewhat smitten.&lt;/p&gt;

&lt;p&gt;I later discovered that the Lisp and Smalltalk environments of the 1980s were borrowing and swapping ideas and that they were very different from the “integrated development environment” (IDE) that I was using to write Java code. In these environments the developer was meant to write code that &lt;em&gt;extended&lt;/em&gt; the system in some way. Applications you developed essentially became part of the environment and to a very real degree you built the tools that you needed to get your work done. Those tools could in turn be used by other developers and you could build on any functionality you ran across. This idea &lt;a href=&quot;https://themkat.net/2021/11/30/emacs_fun_useless.html&quot;&gt;lives on in Emacs&lt;/a&gt; but I’m hard pressed to think of other tools that embrace pervasive extension.&lt;/p&gt;

&lt;p&gt;Contrast this with computing today where we have so much more power at our fingertips but it’s much more difficult to put it to use (even if you are using Emacs 😉). Neither Windows or MacOS comes with development tools installed and both have spent a lot of time and energy making it difficult to customize the environment in any meaningful way, most people simply grow accustomed to whichever one they are using. For almost any task you need to install a separate application and each application (and it’s output) is deliberately silo-d off from every other. Even if you download their development tools, scaffolding up a new project is a time consuming endeavor that few have the motivation to pursue.&lt;/p&gt;

&lt;p&gt;I can’t help but wonder if we will reach some inflection point and people will demand more flexibility out of their computing environments and the software they use. Why isn’t there a tool that let’s us manipulate our data, regardless of which application might have created it? Why is it so difficult to compose a sort of uber-document that includes text from a word processor, a couple of spreadsheets and interactive charts? With the pervasive networking environment we all live in, why so much frustration moving data from my laptop to my phone or, heaven forbid, someone else’s phone?&lt;/p&gt;

&lt;p&gt;Taking a look at some of these old environments, like Medley, might give us some ideas of how things might be made to work more for us. Ways we can put the personal back in “personal computing”.&lt;/p&gt;

&lt;h2 id=&quot;getting-started-with-medley-interlisp&quot;&gt;Getting Started with Medley Interlisp&lt;/h2&gt;

&lt;p&gt;In any case &lt;a href=&quot;https://interlisp.org/&quot;&gt;Medley Interlisp&lt;/a&gt; is one of these environments that you, the developer, are meant to mold to meet your needs. It comes with extensive documentation and it’s clearly designed to soak up all of your time and imagination. There is an ongoing project to get Medley running on current hardware and steady progress is being made every day, presently it’s at the point where you can run it on the major operating systems. When I started playing with it there wasn’t much in the way of tutorials, this articles tries to lay a little groundwork so that you can get started and, at the very least, get an idea of how it works.&lt;/p&gt;

&lt;p&gt;You can run &lt;a href=&quot;https://interlisp.org/software/access-online/&quot;&gt;Medley in your browser&lt;/a&gt; or &lt;a href=&quot;https://interlisp.org/software/install-and-run/&quot;&gt;download and run Medley locally&lt;/a&gt;. I installed it locally but it doesn’t make much difference to this tutorial. If you do take the online route, you will need to create an account to save your data between sessions.&lt;/p&gt;

&lt;p&gt;Once you have Medley installed you can invoke the “medley.sh” or “medley.bat” script to start the environment. A directory named “il” will be created in the root of your home directory, the state of the environment and files you create will be stored there by default.&lt;/p&gt;

&lt;h2 id=&quot;writing-a-simple-program&quot;&gt;Writing a Simple Program&lt;/h2&gt;

&lt;p&gt;Sad though it may be, it took me quite a while and plenty of Googling and YouTube watching in order to write a handful of functions and get them saved to disk! Lucky for you, reading this article is much simpler. To get you accustomed to the environment we’ll write a couple of simple functions to “greet” the person (you) and then save them to disk and load them back.&lt;/p&gt;

&lt;h3 id=&quot;the-executive&quot;&gt;The Executive&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-07-08-medley-executive.png&quot; alt=&quot;The Medley Executive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Medley provides a window with a read-eval-print (REPL) loop that you may use to interact with the environment, it’s called the “executive”. You can type in some code and when you press the return key it’s executed by the environment and then you are prompted to type in more code.&lt;/p&gt;

&lt;p&gt;The Medley environment supports multiple languages, in the picture above the “Exec” window is ready to accept Interlisp code; that’s the language we’re using in this article. If your “Exec” window is set for something else (i.e. Xerox Common Lisp, XCL) then you will want to create a new one.&lt;/p&gt;

&lt;p&gt;To create a new Exec window, right-click on any empty space on your Medley desktop; a pop-up window with a list of applications you may run will appear. Slide down to the “EXEC” option and then over to the right to choose “Interlisp”. When you let go of your mouse button you will select the location and size of the Exec window by left-clicking and dragging until the window is the size you want.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-07-08-medley-new-exec-window.png&quot; alt=&quot;New Interlisp Exec Window&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you need to move your window, right click on the window and then choose the “Move” option. Once you let go of the mouse button you can choose the new window location by left clicking wherever you want the upper-left corner of the window to be located. You can change the size of the window by left-clicking on the bottom-right corner of the window and simply dragging it around.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-07-08-medley-move-window.png&quot; alt=&quot;Move a Window&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;simple-prompt-function&quot;&gt;Simple Prompt Function&lt;/h3&gt;

&lt;p&gt;We need a function that will prompt people for input, we’ll then use this to ask people their name. Go ahead and type this code in at the “Exec” window.&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEFINEQ&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SIMPLE-PROMPT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROMPTFORWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;prompt&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;NIL&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;NIL&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;NIL&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;NIL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CHARCODE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Interlisp is case sensitive and most of the functions are in all caps as this was “the way” in the 1980s. To try to make the code easier to read I’ve written the variables out in lower case. Here we define a new function, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIMPLE-PROMPT&lt;/code&gt;, that accepts a string of text for the prompt and then passes it onto the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROMPTFORWORD&lt;/code&gt; function with a bunch of flags indicating that we want to collect input from the person at the console until they hit the return key.&lt;/p&gt;

&lt;p&gt;Once you type in the code hit the return key, it will immediately be evaluated and ready for use. Go ahead and give it a try…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;(SIMPLE-PROMPT &quot;What is up? &quot;)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pretty neat!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-07-08-medley-simple-prompt.png&quot; alt=&quot;SIMPLE-PROMPT example&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;saving-your-work&quot;&gt;Saving Your Work&lt;/h3&gt;

&lt;p&gt;Now that we have our function handy we want to save it to a file so that we can have it with us always. Interlisp has a function that will let you link functions in your environment with files on disk. Type the following at the REPL…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;(FILES?)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The loose functions in your environment will be listed and you will be asked if you want to “say where the above go ?” which is Medley for choosing a save location; type “y” to agree. Next you will be asked where to save the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIMPLE-PROMPT&lt;/code&gt; function, type in “GREETING.LISP”. Medley will ask if you want to create a new file, press “y” to agree.&lt;/p&gt;

&lt;p&gt;You might think the file is now present on disk but there’s one more step! Right now the function is linked to a file but that is all. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAKEFILES&lt;/code&gt; function handles writing the functions out to the file. Type the following in the “Exec” window to save the function to disk.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;(MAKEFILES &apos;GREETING.LISP)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now the file is on disk, it should be in the “il” folder in your home directory.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-07-08-medley-function-saved.png&quot; alt=&quot;Save Function to Disk&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;loading-your-work&quot;&gt;Loading Your Work&lt;/h3&gt;

&lt;p&gt;When you need to load your functions back in, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOAD&lt;/code&gt; function is there for you.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;(LOAD &apos;GREETING.LISP)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once loaded the functions will be immediately available.&lt;/p&gt;

&lt;h3 id=&quot;reading-the-documentation&quot;&gt;Reading the Documentation&lt;/h3&gt;

&lt;p&gt;Medley provides a bunch of documentation and it’s really pretty good, this is how I found the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROMPTFORWORD&lt;/code&gt; function. To open a new documentation window, right click on some empty Medley desktop space and choose “DINFO”. Click somewhere to anchor the new DInfo window, &lt;em&gt;two windows&lt;/em&gt; will appear! One will have the text of the documentation and the other a dynamic table of contents.&lt;/p&gt;

&lt;p&gt;The scrolling of the windows is a little odd, you need to hover with the mouse pointer just a little past the left-hand margin of the DInfo window and the scrollbar will appear. You can click with the left mouse button to scroll down and the right mouse button to scroll up.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-07-08-medley-dinfo.png&quot; alt=&quot;DInfo with Scrollbar&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The IRM window has a dynamic table of contents, you can click on any of the items and the DInfo window will display the matching documentation. I found information on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROMPTFORWORD&lt;/code&gt; by clicking on “User Input/Output Packages” and then clicking on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROMPTFORWORD&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;write-the-greet-function&quot;&gt;Write the Greet Function&lt;/h3&gt;

&lt;p&gt;We are almost done, the only thing left is to write our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GREET&lt;/code&gt; function. This function will use our prompt function to get the person’s name and then it will say “hello” to them.&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEFINEQ&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GREET&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LET&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SIMPLE-PROMPT&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;What is your name? &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PRINT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CONCAT&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, &quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FILES?&lt;/code&gt; function to link the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GREET&lt;/code&gt; function to your “GREETING.LISP” function and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAKEFILES&lt;/code&gt; to dump it out to disk.&lt;/p&gt;

&lt;h3 id=&quot;editing-a-function&quot;&gt;Editing a Function&lt;/h3&gt;

&lt;p&gt;Eventually you will need to edit an existing function. Medley has an editor you may use called &lt;a href=&quot;https://drive.google.com/file/d/12LW5zCZauJvC63NRMJhjNv5qJkuuCflb/view&quot;&gt;“SEDIT” (Structure Editor)&lt;/a&gt;. You can edit your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GREET&lt;/code&gt; function like so…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;ED (GREET)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You will need to select the location and size of the editor window by left clicking somewhere and dragging to set the window’s size. A new SEdit window will appear with your function’s code, ready for editing.&lt;/p&gt;

&lt;p&gt;Like everything else it doesn’t work exactly as you might expect. The documentation linked above should be enough to get you started.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

&lt;hr /&gt;
</content>
 </entry>
 
 <entry>
   <title>PICO-8 Animation and Collision Detection</title>
   <link href="http:///posts/pico8-animation-collision/"/>
   <updated>2023-01-15T00:00:00-05:00</updated>
   <id>http:///posts/pico8-animation-collision</id>
   <content type="html">&lt;p&gt;In &lt;a href=&quot;https://twitch.nervestaple.com/posts/pico8-intro/&quot; title=&quot;PICO-8 Introduction&quot;&gt;my last article&lt;/a&gt;  we spent some time getting our game shelled out, throwing a sprite for the player on the screen and moving it around a bit. What would really snazz that up, I think, would be a little animation of the player and ending the game if the player hits a hazard of some kind.&lt;/p&gt;

&lt;p&gt;In this article we’ll add some animation to snazz up our player, introduce a hazard and then add some collision detection so that we can end the game if the player happens to smack into the hazard. Not a challenging game but it does have a beginning, middle and an end!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-07-pico8-banner.png&quot; alt=&quot;PICO-8&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;animating-the-player&quot;&gt;Animating the Player&lt;/h2&gt;

&lt;p&gt;What we would like is for the player to animate while it is moving around the screen. We’d like one set of animations while it’s moving along the “x” axis and another set while it moves along the “y” axis. To keep things simple we will only have two frames for each animation.&lt;/p&gt;

&lt;p&gt;To make this work we’re going to setup the simplest possible animation system. We’ll have one variable that keeps track of each “tick” of the game (each cycle through our game loop); every time we complete a game loop we’ll increment the tick counter. When we reach 16 ticks we’ll start back at 0 and start all over again.&lt;/p&gt;

&lt;p&gt;This simple system will give us 16 possible frames of animation, one per game loop cycle. That’s more than we need and it gives us a little room to grow if we decide to get clever.&lt;/p&gt;

&lt;h3 id=&quot;create-our-tick-counter&quot;&gt;Create Our Tick Counter&lt;/h3&gt;

&lt;p&gt;We’re going to build on &lt;a href=&quot;https://twitch.nervestaple.com/posts/pico8-intro/&quot; title=&quot;PICO-8 Introduction&quot;&gt;the code from the previous article&lt;/a&gt;, either catch up by reading through it or just copy the relevant code out so that we’re all on the same page.&lt;/p&gt;

&lt;p&gt;Let’s start by adding two variables to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init_actors&lt;/code&gt; function: one for the current tick and one for our maximum number of ticks. The code below will get this done.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init_actors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;atk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;atk&lt;/code&gt; variable stands for “animation tick” and starts off at zero. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;atx&lt;/code&gt; variable stores our maximum number of ticks and we set it to fifteen, giving us sixteen ticks in total.&lt;/p&gt;

&lt;p&gt;Next we need to increment and reset our tick counter. Let’s add a function to manage our animation counters.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_anim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;atk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atx&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;atk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Simple, right? We update our tick counter and if we hit our maximum number of ticks then we reset it back to zero.&lt;/p&gt;

&lt;h3 id=&quot;creating-more-player-sprites&quot;&gt;Creating More Player Sprites&lt;/h3&gt;

&lt;p&gt;Next we need more sprites for our player, we want two to indicate movement horizontally and two more for vertical animation. Use the first four cells of sprites. You may also copy your sprite with the dotted square looking tool and paste it into the next cell to save time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-15-more-sprites.png&quot; alt=&quot;More Sprites&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’ve altered my sprites to change the direction the player is looking, I also change the fringe at the bottom of the ghost to give the impression of movement. The idea here is that the changes between the sprites don’t necessarily need to be dramatic to provide the illusion of motion.&lt;/p&gt;

&lt;p&gt;Now we can associate our new sprites with our player. After thinking it over a bit, I decided to add two arrays to the player data: one with the horizontal frames and another with the vertical. Doing it this way, we could have different animations for the various characters in the game.&lt;/p&gt;

&lt;p&gt;Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init_actors()&lt;/code&gt; function to match the excerpt below.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init_actors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;atk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt; 
  &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;alr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ve added two more variables to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ply1&lt;/code&gt; table, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alr&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt;. Both of these contain an array with the index of the sprites that make up the animation. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alr&lt;/code&gt; variable (animation left/right) references the first two sprites (0 and 1). The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt; (animation up/down) variable points to the third and fourth sprites (2 and 3).&lt;/p&gt;

&lt;p&gt;We are now tracking the animation frames (ticks) and we have references to the sprites we want to use for the animation. The only bit remaining is swapping the sprites for the player as it moves around, we’ll do this by manipulating the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ply1.spr&lt;/code&gt; variable that indicates which sprite to draw for the player.&lt;/p&gt;

&lt;h3 id=&quot;swapping-the-players-sprite&quot;&gt;Swapping the Player’s Sprite&lt;/h3&gt;

&lt;p&gt;Since we need to know which button the player is pressing to figure out which direction to move the player, we can save a little time by weaving our animation code into our button detection and movement code. We check if the left or right, or if the up or down buttons are being pressed, then we update the player’s position and alternate the player’s sprite.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
  		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One game cycle goes by too quickly to really be visible, instead we change the sprite of the player every eight ticks. If we’re on a tick less then eight then we draw the first sprite otherwise we draw the second. You can also see in the code above that we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ply.alr&lt;/code&gt; sprites if buttons 0 or 1 are pressed (left or right) and we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ply.aud&lt;/code&gt; sprites if buttons 2 or 3 (up or down) are pressed.&lt;/p&gt;

&lt;p&gt;That’s it! Run the game and give it a try, you should see your player 8-bit-ly animated as you move it around the screen. 😎&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-15-watch-the-player.gif&quot; alt=&quot;Animated Player&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;hazards-and-collisions&quot;&gt;Hazards and Collisions&lt;/h2&gt;

&lt;p&gt;Now that we have our animated player moving around the screen, the next thing we need to be able to do is detect collisions between our player and the various hazards of the game. For that we need at least one hazard!&lt;/p&gt;

&lt;h3 id=&quot;adding-a-hazard&quot;&gt;Adding a Hazard&lt;/h3&gt;

&lt;p&gt;Switch over to the sprite editor and add one more sprite, this one will be the thing that ends the game if the player were to touch it. It could be a hole or a bomb or an alligator, whatever you’d like. With that done, let’s add our hazard to the game. Let’s update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init_actors&lt;/code&gt; function to track our hazard and if the game is over (the player has collided with the hazard).&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init_actors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;atk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;alr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;hzd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;over&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hzd&lt;/code&gt; table has the “x” and “y” positions of our hazard along with the index of the sprite used to draw it on-screen. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;over&lt;/code&gt; variable lets us keep track of the status of the game, we start out with it &lt;em&gt;not&lt;/em&gt; being over.&lt;/p&gt;

&lt;p&gt;Our hazard also needs to be drawn on screen, add this function right after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw_ply1&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw_hzd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hzd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hzd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hzd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Simple right? We just draw the hazard on the screen. Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_draw&lt;/code&gt; function to call this one as well.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;draw_ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;draw_hzd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now we’ll draw the hazard every loop of the game, just like the player.&lt;/p&gt;

&lt;h3 id=&quot;detecting-collision&quot;&gt;Detecting Collision&lt;/h3&gt;

&lt;p&gt;Detecting a collision is really checking the distance between the player and the hazard, if they intersect then there’s our collision.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;collision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://pico-8.fandom.com/wiki/Abs&quot; title=&quot;PICO-8 ABS Function&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abs&lt;/code&gt;&lt;/a&gt; function returns the “absolute value”, the positive or negative information is discarded. For instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0 - 8 = -8&lt;/code&gt; but with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abs&lt;/code&gt; we just get back &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt;. For our purposes we want the distance between the two sprites, we don’t really care if it’s a positive or negative number.&lt;/p&gt;

&lt;p&gt;Once we calculate the “x” distance and the “y” we add them up and check to see if it’s less than or equal to the width or height of our sprites, which is eight. If they are eight pixels apart or less then they’ve collided.&lt;/p&gt;

&lt;p&gt;We can add our collision logic way down to the end of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update_ply1&lt;/code&gt; function. Go ahead and add the code below directly before the end of the function.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hzd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;over&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We check to see if the player has collided with our hazard and, if they have, we set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;over&lt;/code&gt; flag to “true”. This indicates the game has ended.&lt;/p&gt;

&lt;h3 id=&quot;ending-the-game&quot;&gt;Ending the Game&lt;/h3&gt;

&lt;p&gt;Now we need to let the player know that the game has ended! Let’s add a new function (right after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw_hzd&lt;/code&gt;) that displays “Game Over” on the screen.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw_over&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;over&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;game over&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As advertised, it checks to see if the game is over and, if it is, we display the game over text in the upper left of the screen.&lt;/p&gt;

&lt;p&gt;The only thing we’re missing is and end to the action when the game ends. Right now, the player could keep moving around. Add the snippet of code below to the &lt;em&gt;very beginning&lt;/em&gt; of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update_ply1&lt;/code&gt; function.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;over&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the game is over, we won’t update the player’s state. They’ll be frozen wherever they were when they collided with the hazard.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-15-game-over.png&quot; alt=&quot;Game Over&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Whew! We didn’t add much code but we’ve definitely added a lot of functionality! Our player animates as they move and we introduced a hazard into our game. We added some collision detection, ending the game if the player collides with the hazard.&lt;/p&gt;

&lt;p&gt;This might be enough to get you going on your own game. 😉&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-15-move-player.p8.png&quot; alt=&quot;PICO-8 Cartridge&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;
</content>
 </entry>
 
 <entry>
   <title>Getting Started with PICO-8</title>
   <link href="http:///posts/pico8-intro/"/>
   <updated>2023-01-07T00:00:00-05:00</updated>
   <id>http:///posts/pico8-intro</id>
   <content type="html">&lt;p&gt;I’ve been interested in fantasy consoles for a long time now but my interest has been limited to reading a blog article here and there and thinking something like “Man, that looks like fun!” After literal years of doing other things I’ve finally decided to put my own 8-bit arcade game together. I took a look at the field, tried the &lt;a href=&quot;https://pixelvision8.github.io/Website/&quot; title=&quot;Pixel Vision 8&quot;&gt;Pixel Vision 8&lt;/a&gt; and then saw the project archived, regrouped and decided to go with the  &lt;a href=&quot;https://www.lexaloffle.com/pico-8.php?page=faq&quot; title=&quot;PICO-8&quot;&gt;PICO-8&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-07-pico8-banner.png&quot; alt=&quot;PICO-8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At first I started out working with the &lt;a href=&quot;https://pixelvision8.github.io/Website/&quot; title=&quot;Pixel Vision 8&quot;&gt;Pixel Vision 8&lt;/a&gt; project but I found some bugs that were frustrating me and while I was working on fixing those bugs, I found some more bugs. While I was thinking about fixing those bugs the project was archived, there’s currently no maintainer. Becoming the maintainer of one of these projects is out-of-scope, I’m more interested in getting a game coded up, so I decided to pony up the $15.00 and purchase a PICO-8 license. I recommend you do the same, its not that much money and it helps keep the project active.&lt;/p&gt;

&lt;p&gt;It’s a pretty nice product and reasonably polished. It has handy tools for writing code, drawing sprites and tile maps, and editing music. While you could use external tools and pull in files, it’s nice to be able to do everything in one place. Plus it looks pretty dang awesome on my snazzy retro looking PC.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-07-retro-pc.jpg&quot; alt=&quot;Snazzy Retro PC&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This post will cover getting the PICO-8 application installed, drawing your first sprite and moving that sprite around on screen. My plan is to add more articles as I get the game built up. I’m warning you now that this could take a while, I have a day job as well. 😉&lt;/p&gt;

&lt;h2 id=&quot;install-the-pico-8-application&quot;&gt;Install the PICO-8 Application&lt;/h2&gt;

&lt;p&gt;Check all of the pockets of your coats and look under the sofa cushions until you’ve found $15 and then head over to the &lt;a href=&quot;https://www.lexaloffle.com/pico-8.php?#getpico8&quot; title=&quot;Get PICO-8&quot;&gt;PICO-8&lt;/a&gt; page. Once you fill in your e-mail address and hand over your dollars, you’ll end up with a license through the &lt;a href=&quot;https://www.humblebundle.com/&quot; title=&quot;Humble Store&quot;&gt;Humble&lt;/a&gt; website. You can then download the distribution for your operating system (or a ZIP of the distribution if you’re on Linux).&lt;/p&gt;

&lt;p&gt;For those of you on Windows or MacOS, there’s not much to the installation; double-click the Windows installer or copy over the MacOS application from the disk image. Those of us on Linux will have to put the distribution somewhere and either start from the command line or &lt;a href=&quot;https://gist.github.com/netvip3r/34a0f3519cfe3e0e7c44e8f1fcba76d4&quot; title=&quot;PICO-8 Desktop File&quot;&gt;create a “desktop” file&lt;/a&gt; to launch the application… Unless you are on Arch Linux, there is &lt;a href=&quot;https://aur.archlinux.org/packages/pico-8&quot; title=&quot;Arch AUR PICO-8 Package&quot;&gt;an AUR package for PICO-8&lt;/a&gt; that’s pretty easy to get working.&lt;/p&gt;

&lt;p&gt;With all of that work out of the way, you are ready to get started! Launch the PICO-8 application and you will be greeted with the welcome screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-07-pico8-welcome.png&quot; alt=&quot;PICO-8 Welcome&quot; /&gt;&lt;/p&gt;

&lt;p&gt;They’re definitely going for the old 8-bit home computer vibe, this reminds me of the Atari 800 or Commodore 64 where you’d get dropped at a BASIC interpreter. This is a bit better as the PICO-8 speaks &lt;a href=&quot;https://www.lua.org/&quot; title=&quot;Lua Programming Language&quot;&gt;Lua&lt;/a&gt; which is quite a bit more fun to work with.&lt;/p&gt;

&lt;p&gt;What you are looking at right now is the interactive console, press the “escape” key on your keyboard to switch over to “editor” mode. It should look like the image below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-07-pico8-editor.png&quot; alt=&quot;PICO-8 Editor Mode&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The icons in the upper-right hand corner represent the different editors: code, sprite, map, sound effects and music; this image is of the code editor. More information about all of these tools and how they work can be found in &lt;a href=&quot;https://www.lexaloffle.com/dl/docs/pico-8_manual.html&quot; title=&quot;PICO-8 Manual&quot;&gt;the PICO-8 manual&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;draw-a-sprite&quot;&gt;Draw a Sprite&lt;/h2&gt;

&lt;p&gt;For now, switch over the the sprite tool (the second one from the left). For those of you not in the know, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Sprite_(computer_graphics)&quot; title=&quot;Sprite&quot;&gt;sprite&lt;/a&gt; is a bitmap graphic that can easily be moved around on screen or stacked on top of or below other bitmaps. We will use this sprite to represent the player.&lt;/p&gt;

&lt;p&gt;Each sprite on the PICO-8 is 8x8 pixels in size, these sprites are displayed in rows beneath the sprite editor area. There’s one sprite there already, it kind of looks like a white star. Click on that sprite to bring it into focus and display it in the editor area.&lt;/p&gt;

&lt;p&gt;The sprites don’t have names but they each have a number: the first one (the one you’re editing) is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;, the one to the right of that would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; and the one to the right of that is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt; and so on.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-07-pico8-sprite-editor.png&quot; alt=&quot;Sprite Editor&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are several tools to make editing your sprite easier, feel free to explore them. Go ahead and edit the sprite until you have something you are somewhat satisfied with, after all, you can always go back and make changes later. This sprite will represent the player in your game.&lt;/p&gt;

&lt;h2 id=&quot;displaying-your-sprite&quot;&gt;Displaying Your Sprite&lt;/h2&gt;

&lt;p&gt;The next step is to write enough code to get your sprite to display when your game is run.&lt;/p&gt;

&lt;h3 id=&quot;the-game-loop&quot;&gt;The Game Loop&lt;/h3&gt;

&lt;p&gt;The PICO-8 lets you write your game code with &lt;a href=&quot;https://www.lua.org/&quot; title=&quot;Lua Programming Language&quot;&gt;Lua&lt;/a&gt;. It’s pretty concise and, in my opinion, pretty easy to pick up from scratch; don’t worry if you don’t know much about Lua. You can read up on the language later, this tutorial will give you enough knowledge just-in-time style to get by.&lt;/p&gt;

&lt;p&gt;Make sure you are in “editing” mode, press the “escape” key if you are not. Pick the code editor from the set of icons in the upper-right, it’s the first one and kind of looks like two parenthesis “()”. You will be welcomed by an entirely empty page of code, go ahead and bang in the code listed below.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This won’t actually do anything but it does provide a short game that does nothing! Press the “escape” key to switch back to console mode and type “run” (the &lt;a href=&quot;https://pico-8.fandom.com/wiki/Run&quot; title=&quot;PICO-8 run Function&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt;&lt;/a&gt; function will start your game) at the prompt. You should receive no errors and see nothing at all happen. When you grow tired of all the nothing, press the “escape” key to stop your game from running.&lt;/p&gt;

&lt;p&gt;These three functions are common to every game, they are called by the PICO-8 to perform the following…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Setup your game&lt;/li&gt;
  &lt;li&gt;Update the current state of your game (i.e. move the player, the bad guys, etc.)&lt;/li&gt;
  &lt;li&gt;Draw the current state of the game on screen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While the setup function (“_init”) is only called once the other two are called over and over. When the “_update” function is called it can do things like see if any buttons are pressed and move the player to a new location. After that, the “_draw” function will be called to draw the current state of the game to the display. This is called &lt;a href=&quot;https://gameprogrammingpatterns.com/game-loop.html&quot; title=&quot;Game Loop&quot;&gt;“the game loop”&lt;/a&gt;, it’s a common pattern in a lot of different kinds of video games.&lt;/p&gt;

&lt;h3 id=&quot;draw-the-player&quot;&gt;Draw the Player&lt;/h3&gt;

&lt;p&gt;While we could jam all of our code into those three functions, this style is frowned upon by sane people everywhere. Instead we’ll add some new functions to the top of our file and then call those functions where appropriate. First up is keeping track of the state of our player, add the following code to the top of your file.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init_actors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code creates a new variable named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ply1&lt;/code&gt; that keeps track of all the data about our player: their coordinates on screen (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt;) and the index of the sprite we’re using to represent them (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spr&lt;/code&gt;). The data we’ve assigned to the player is called &lt;a href=&quot;https://www.lua.org/pil/2.5.html&quot; title=&quot;Lua, Tables&quot;&gt;a “table”&lt;/a&gt;, it stores any number of keys and values and makes it easy to get at their values and update them.&lt;/p&gt;

&lt;p&gt;Now we need to call our function to setuip our player along with the rest of the game. Edit your “_init” function to match the listing below.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;init_actors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next up we want to draw the player. This will be pretty simple; we need to position the sprite in the player’s current location and then draw the sprite. Type in the code below right underneath your “initActors” function.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw_ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://pico-8.fandom.com/wiki/Spr&quot; title=&quot;PICO-8 spr Function&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spr&lt;/code&gt;&lt;/a&gt; function is provided by the PICO-8’s sprite library, it handles all of the nitty-gritty of drawing the sprite to the display. It takes three arguments, conveniently all of the data it needs is available from our player data. We provide the name (numeric index) of our sprite, the coordinate on the “x” axis and then the coordinate of the player along the “y” axis. With that information in hand, our sprite will be drawn.&lt;/p&gt;

&lt;p&gt;The last step is to update our “_draw” function to actually draw the player’s sprite. Edit your “_draw” function to look like the code listed below.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;draw_ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Your entire file of code should look something like this…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-07-pico8-code-1.png&quot; alt=&quot;Code Listing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and save the code file and run your game. You should see your sprite on the screen. 😎&lt;/p&gt;

&lt;h3 id=&quot;move-the-player&quot;&gt;Move the Player&lt;/h3&gt;

&lt;p&gt;The last step is to let the player move around by pressing the arrow keys on their keyboard. There’s only a little bit of logic to handle, we’ll add it to a new function named “update_ply1”.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The PICO-8 has a &lt;a href=&quot;https://pico-8.fandom.com/wiki/Btn&quot; title=&quot;PICO-8 btn Function&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;btn&lt;/code&gt;&lt;/a&gt; function is doing most of the work here. This function accepts the index of button type (player 0 up, player 0 down, etc.) and returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; if that button is being pressed. We then check each button we’re interested in and then (if it’s being pressed) we update the state of the player to match.&lt;/p&gt;

&lt;p&gt;To make this work, add a call to this function to our “_update” function.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;update_ply1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Try it out! You’ll notice it doesn’t work &lt;em&gt;exactly&lt;/em&gt; as expected. The problem is that we aren’t clearing the display before we draw the sprite in the next location. The &lt;a href=&quot;https://pico-8.fandom.com/wiki/Cls&quot; title=&quot;PICO-8 cls Function&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cls&lt;/code&gt;&lt;/a&gt; function will clear the screen, if we add that right before we draw the items on screen, we’ll be all set.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_draw&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;draw_ply1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Woot! If you are interested in going the extra mile, how would we change the “update_ply1” code to let the player appear on one side of the screen when they reach the other, like in &lt;a href=&quot;https://freepacman.org/&quot; title=&quot;Pac Man&quot;&gt;Pac Man&lt;/a&gt;? What would we do if we wanted the boundaries of the display to act as a wall? What might we change to make the player move faster or slower? There are many variations even for simple games. 🤯&lt;/p&gt;

&lt;h2 id=&quot;build-your-game&quot;&gt;Build Your Game&lt;/h2&gt;

&lt;p&gt;The last thing we’ll cover is building a “cartridge” that contains your game, this provides you one with one file that you can share with friends. You simply drag the cartridge file onto the PICO-8 (or executable) to load the game. With the PICO-8 we can also save the cartridge as an actual &lt;a href=&quot;https://pico-8.fandom.com/wiki/P8PNGFileFormat&quot; title=&quot;PICO-8 PNG Cartridge&quot;&gt;PNG&lt;/a&gt; image that you can view in an image viewer or web browser. It will have a screenshot as well as a little description of your game.&lt;/p&gt;

&lt;p&gt;Switch to the code editor, we’re going to add a couple of lines to the top of your game’s code. We’ll add two comments; these won’t change how your game works but they will be used to describe your game and will be part of the cartridge. Two dashes in a row (“–”) start a comment, the comments are displayed at the bottom of your cartridge image. Add one comment with the name of your game and another with your name. Make sure these are the first two lines of code in the editor!&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;--move the player&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--cmiles74&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, switch back to the console and type “run” to run the game. Move the player around a little bit and then press the “control” key along with the “7” key (control-7). You should see the PICO-8 say “captured label image” down at the bottom of the screen. You’ve just taken the screenshot that will decorate your cartridge!&lt;/p&gt;

&lt;p&gt;Now you can save your cartridge to disk. Type the following at the console to save your game.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-code&quot;&gt;save move-the-player.p8.png
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At this point your game is built! You can switch to your operating system’s file browser so that you can navigate to your cartridge directory. This will vary based on your operating system.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Operating System&lt;/th&gt;
      &lt;th&gt;Cartridge Path&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MacOS&lt;/td&gt;
      &lt;td&gt;~/Library/Application Support/pico-8/carts&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Linux&lt;/td&gt;
      &lt;td&gt;~/.lexaloffle/pico-8/carts&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Windows&lt;/td&gt;
      &lt;td&gt;%AppData%/Roaming/pico-8/carts&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;When you visit this directory you should see your cartridge listed. If you drag it over to your web browser, you’ll see what the image looks like.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2023-01-07-move-player.p8.png&quot; alt=&quot;PICO-8 Cartridge&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can load your cartridge by double-clicking it or dragging it onto your PICO-8 window. You can also email this file to friend or post it somewhere on the internet. Anyone with the PICO-8 application installed can run your game or play around with it, perhaps even making their own changes and sending you an updated cartridge.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It’s quite an achievement to get this much done so quickly, the tools provided by the PICO-8 make it easy to get straight to game building. Looking at your code, there’s not so much that it’s hard to read, hopefully it’s also pretty clear how things fit together.&lt;/p&gt;

&lt;p&gt;Good luck on your next video game! If you are looking for inspiration, type &lt;a href=&quot;https://pico-8.fandom.com/wiki/Splore&quot; title=&quot;PICO-8 splore Function&quot;&gt;“splore”&lt;/a&gt; at the PICO-8 console and check out some of the games people have written. Don’t forget to check back here for more articles, I plan to get a couple more written.&lt;/p&gt;

&lt;hr /&gt;

</content>
 </entry>
 
 <entry>
   <title>Assembling a PC in an old Macintosh SE Case</title>
   <link href="http:///posts/pc-in-macse-build/"/>
   <updated>2022-12-19T00:00:00-05:00</updated>
   <id>http:///posts/pc-in-macse-build</id>
   <content type="html">&lt;p&gt;Back before the pandemic I did all of my work from a laptop. I’d tow it back and forth between my home and work but, honestly, I mostly used it at work. The idea was that if there was a critical issue I’d have it handy and if I traveled to visit a customer it would be close at hand.&lt;/p&gt;

&lt;p&gt;Things have certainly changed! Now I mostly work from my home office, my laptop spends most of it’s time plugged into a docking station powering a larger display, proper keyboard, etc. Many of the compromises the laptop represents no longer make as much sense. Why should I favor a small, light form factor when all it does is sit on my desk? Plus the harder it works, the warmer it gets and the more work it’s tiny internal fans spin, limiting it’s life span.&lt;/p&gt;

&lt;p&gt;I decided to free myself from the shackles of the laptop, to build myself a real workstation that can easily handle my workload. From time to time I do travel, it would be nice to have something at least somewhat portable. If the day comes where I need to return to the office, it would be nice to be able take my PC with me. Maybe I’d take it home on weekends.&lt;/p&gt;

&lt;p&gt;With these constraints in mind I started looking around at cases, looking for one that said “could maybe be moved” and “not entirely lacking in soul.” It turns out that there’s no shortage of featureless gray or black boxes but I wasn’t really excited about any of them. Then I thought back to the old &lt;a href=&quot;https://en.wikipedia.org/wiki/Macintosh_Classic_II&quot; title=&quot;Macintosh Classic II&quot;&gt;Mac Classic II&lt;/a&gt; that I had in college, it was heavy but it had a handy duffel bag-ish thing I could lug it around in. Why not use that?&lt;/p&gt;

&lt;p&gt;Decision made, I bought an old Macintosh SE case from a seller on eBay that was already bereft of guts and CRT. I measured the interior and was relieved to find that it would easily fit a &lt;a href=&quot;https://en.wikipedia.org/wiki/Mini_ATX&quot; title=&quot;Mini ATX&quot;&gt;mini-ATX&lt;/a&gt; board. All I had to do was 3D print some scaffolding to hold the pieces together and I’d be all set!&lt;/p&gt;

&lt;p&gt;More easily said than done, but after about a year I finally have it finished up and working. Behold, my small but mighty &lt;a href=&quot;https://www.amd.com/en/products/apu/amd-ryzen-5-5600g&quot; title=&quot;AMD Ryzen 5 5600G&quot;&gt;six core AMD Ryzen 5&lt;/a&gt; powered workstation!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-19-mac-se-neofetch.jpg&quot; alt=&quot;Front, Neofetch&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I filled the top floppy drive bay with a USB hub. I’ve moved the power switch to the front, where the brightness knob once was is a nice, chrome push-button. It also has it’s own speaker, just like the old Mac SE.&lt;/p&gt;

&lt;p&gt;An virtual machine running the MacOS of the era (although in color) is only a key-press away. 😯&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-19-mac-se-macos-startup.jpg&quot; alt=&quot;Front, MacOS&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The rear didn’t come out as nicely as I wanted but it could have been worse. 😛 I didn’t realize until far too late in the project that the logic board I purchased didn’t support USB-C video out, I had to run a cable to power the front LCD display. I also cut a little too low into the case for the ports and that looks pretty hacky. I may go back and print up a cover of some kind.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-19-mac-se-rear.jpg&quot; alt=&quot;Rear&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The scaffolding for the interior isn’t too complicated, I had to print the lowest layer in two parts in order to get the depth that I needed. The lowest layer holds the logic board and its components, the next layer supports the power supply. It was tricky getting a nice, snug fit on the front display that looked natural; I was saved by &lt;a href=&quot;https://www.thingiverse.com/thing:3551263&quot; title=&quot;Display Bezel Adapter for Mac Classic&quot;&gt;an adapter for the front of the case&lt;/a&gt; that worked out great.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-19-mac-se-interior.jpg&quot; alt=&quot;Interior&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After some searching around on the internet, I finally found a period travel bag that was in decent shape. After a little cleaning and repair, it’s good to go.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-19-mac-bag.jpg&quot; alt=&quot;Travel Bag&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s been a month or so and this little guy is working out great. While its little LCD panel is on the small side, it’s not entirely useless. I’ve been parking my various chat windows (Teams, Slack, etc.) over there, keeping them out of my main work area. The &lt;a href=&quot;https://noctua.at/en/nh-l9a-am4&quot; title=&quot;Noctua CPU Fan&quot;&gt;low-profile Noctua CPU fan&lt;/a&gt; I picked out is silent most of the time and nearly silent when the machine is working hard, I haven’t seen any throttling.&lt;/p&gt;

&lt;p&gt;So far it’s been a significant improvement over my laptop, I have no complaints. I’m also a little surprised at my schmalzy nostalgia for this case, I really enjoy seeing it perched on the edge of my desk.&lt;/p&gt;

&lt;hr /&gt;
</content>
 </entry>
 
 <entry>
   <title>Getting Started with Pixel Vision 8</title>
   <link href="http:///posts/pixelvision8-intro/"/>
   <updated>2022-11-07T00:00:00-05:00</updated>
   <id>http:///posts/pixelvision8-intro</id>
   <content type="html">&lt;p&gt;I’ve been interested in fantasy console for a long time now, but my interest has been limited to reading a blog article here and there and thinking something like “Man, that looks like fun!” After literal years of doing other things, I’ve finally decided to put my own 8-bit arcade game together. After taking a look at the field, I decided to go with the &lt;a href=&quot;https://pixelvision8.github.io/Website/&quot; title=&quot;Pixel Vision 8&quot;&gt;Pixel Vision 8&lt;/a&gt;!  My plan is to try an get some of the people are work interested so it’s important that the console be free. 😉&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-11-07-pv8.svg&quot; alt=&quot;PV8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s a pretty nice product and reasonably polished. It has handy tools for writing code, drawing sprites and tile maps, and editing music. It’s also pretty modular, each specialized function is managed by a “chip” (color, tile, sprite, etc.) that can be customized or even replaced with your own code. Plus, it looks pretty dang awesome on my snazzy retro looking PC.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-11-07-retro-pc.jpg&quot; alt=&quot;Snazzy Retro PC&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This post will cover getting the Pixel Vision 8 application installed, drawing your first sprite and moving that sprite around on screen. If people are interested and if I have time, I may write another post that covers getting an actual game coded up.&lt;/p&gt;

&lt;h2 id=&quot;install-the-pixel-vision-8-application&quot;&gt;Install the Pixel Vision 8 Application&lt;/h2&gt;

&lt;p&gt;Head over to the &lt;a href=&quot;https://pixelvision8.github.io/Website/&quot; title=&quot;Pixel Vision 8&quot;&gt;Pixel Vision 8&lt;/a&gt; home page and click on the link for the “Latest Build”. This will take you to the page with the latest release on the project’s GitHub page. Scroll down to the “Assets” section and then click on the archive for your operating system. For instance, if you were on MacOs and the latest release was 1.0.18, you’d click on the link that reads “PixelVision8-v1.0.18-macOS.zip”.&lt;/p&gt;

&lt;p&gt;Once you have the release downloaded, go ahead and unzip it. This is kind of on you, as every platform is a bit different. Good luck!&lt;/p&gt;

&lt;p&gt;With the distribution unpacked, open up the directory and take a look at the contents. There’s a lot of stuff in there, you want to double-click the executable named “Pixel Vision 8”. The machine will boot up, load it’s operating system and present you with a welcome screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-07-pv8-welcome.png&quot; alt=&quot;PV8 Welcome&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Pretty neat, eh? It kind of reminds me of the &lt;a href=&quot;https://www.gregdonner.org/workbench/wb_11.html&quot; title=&quot;Amiga Workbench&quot;&gt;Amiga Workbench&lt;/a&gt;. Go ahead and click the “Yes” button to create a new project. You will be dropped at the “Workspace Explorer” where you can browse all of the files in your starter template project.&lt;/p&gt;

&lt;h2 id=&quot;draw-a-sprite&quot;&gt;Draw a Sprite&lt;/h2&gt;

&lt;p&gt;First things first: we need a sprite to display on screen. For those of you not in the know, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Sprite_(computer_graphics)&quot; title=&quot;Sprite&quot;&gt;sprite&lt;/a&gt; is a bitmap graphic that can easily be moved around on screen or stacked on top of or below other bitmaps. We will use this sprite to represent the player.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/PixelVision8/PixelVision8/wiki/sprites&quot; title=&quot;PV8 Sprites&quot;&gt;Sprites in Power Vision 8&lt;/a&gt; are all 8x8 pixels in size, when your game starts up the file “sprites.png” is loaded and chopped up in to 8x8 images. Each of these images is then loaded into the memory of the sprite chip until it runs out of space. Each sprite is given an index number at load time (starting from 0) and you can use that index to fetch a particular sprite.&lt;/p&gt;

&lt;p&gt;Double-click the folder labeled “Sprites” to open it up, then choose “New Sprite” from the “Workspace Explorer” menu (the menu is under an icon that looks like three colored slashes, “///”). The “New Sprite” dialog will appear, asking you to name the file; change the name to “sprites” (plural).&lt;/p&gt;

&lt;p&gt;At this point we run into a minor bug in the Pixel Vision 8 Workspace Explorer: we need the “sprites” file in the root of our project, next to our “code” file. Unfortunately we can &lt;em&gt;only&lt;/em&gt; create a new sprite file when we’re in the “Sprites” directory. Lucky for us this is easy to fix, simply drag the “sprites” file to the folder named “..”, the one with the green up-arrow icon. You will be asked to confirm the move, go ahead and say “yes”. You may now double-click the “..” folder to move up a level and you will see the files in your project.&lt;/p&gt;

&lt;p&gt;Now that you have a file of sprite data, double-click the “sprites” file to open it. The sprite editor will open and you will be asked if you want to use the Pixel Vision 8 color palette, go ahead and say yes. You should now see one lone sprite in the upper-left hand corner of your canvas.&lt;/p&gt;

&lt;p&gt;We want edit that little sprite, but it’s so small! In the lower right-hand corner of the editor is a large yellow button; click that button to zoom in and make the sprite as large as you can (if you click while holding down the shift key then everything will get smaller 😉). Now that it’s nice and big, edit that sprite until you think it looks like your player; you can select the draw color by clicking on it, the pencil and line tools may be used to draw your spite. The magenta background will be ignored, at game time it will be replaced with whatever is behind your sprite.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-07-pv8-sprite.png&quot; alt=&quot;A Sprite&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When you like the look of your sprite, choose “Save” from under the “///” menu to save your sprite and then choose “Quit” to close the sprite editor.&lt;/p&gt;

&lt;h2 id=&quot;displaying-your-sprite&quot;&gt;Displaying Your Sprite&lt;/h2&gt;

&lt;p&gt;The next step is to write enough code to get your sprite to display when your game is run. You should be back at the “Workspace Explorer”, looking at the contents of the “sprites” directory. Double-click the “code” file to edit your game’s code in the editor.&lt;/p&gt;

&lt;h3 id=&quot;the-game-loop&quot;&gt;The Game Loop&lt;/h3&gt;

&lt;p&gt;The Pixel Vision 8 lets you choose between using &lt;a href=&quot;https://www.lua.org/pil/contents.html&quot; title=&quot;Lua&quot;&gt;Lua&lt;/a&gt; or &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/&quot; title=&quot;C#&quot;&gt;C#&lt;/a&gt;, for this project we’ll be using &lt;a href=&quot;https://www.lua.org/pil/contents.html&quot; title=&quot;Lua&quot;&gt;Lua&lt;/a&gt;. It’s pretty concise and, in my opinion, easier to pick up from scratch than C#. You can read up on the language later, this tutorial will give you enough knowledge just-in-time style to get by.&lt;/p&gt;

&lt;p&gt;You will see a bunch of code in the file already, this is the sample code that comes with each new workspace. In my opinion it’s not all that helpful; press “Control”+”A” to select all of the text and then hit the backspace or delete key to remove all of it. Next, type in the text below to code up the skeleton of your game.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeDelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This won’t actually do anything but it does provide a short game that presents an entirely black screen! Choose “Save” from under the “///” menu and then choose “Run Game” to try it out. Your game should load and you should see a screen of black. When you are done exploring this barren landscape, press the “Escape” key to quit your game and return to the editor.&lt;/p&gt;

&lt;p&gt;These three functions are common to every game, they are called by the Pixel Vision 8 to perform the following…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Setup your game&lt;/li&gt;
  &lt;li&gt;Update the current state of your game (i.e. move the player)&lt;/li&gt;
  &lt;li&gt;Draw the current state of the game on screen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While the setup function (“Init”) is only called once the other two are called over and over. The “Update” function will be called, it can do things like see if any buttons are pressed and move the player to a new location. After that, the “Draw” function will be called to update what we see on the display. This is called &lt;a href=&quot;https://gameprogrammingpatterns.com/game-loop.html&quot; title=&quot;Game Loop&quot;&gt;“the game loop”&lt;/a&gt;, it’s a common pattern in a lot of different kinds of video games.&lt;/p&gt;

&lt;h3 id=&quot;draw-the-player&quot;&gt;Draw the Player&lt;/h3&gt;

&lt;p&gt;While we could jam all of our code into those three functions, this style is frowned upon by sane people everywhere. Instead we’ll add some new functions to the top of our file and then call those functions where appropriate. First up is keep track of the state of our player, add the following code to the top of your file.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InitActors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sprite&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code creates a new variable named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;player&lt;/code&gt; that keeps track of all the data about our player: their coordinates on screen (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posX&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posY&lt;/code&gt;) and the index of the sprite we’re using to represent them (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sprite&lt;/code&gt;). The data we’ve assigned to the player is called &lt;a href=&quot;https://www.lua.org/pil/2.5.html&quot; title=&quot;Lua, Tables&quot;&gt;a “table”&lt;/a&gt;, it stores any number of keys and values and makes it easy to get at their values and update them.&lt;/p&gt;

&lt;p&gt;Now we need to call our function to initialize our player when our game initializes. Edit your “Init” function to match the listing below.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;InitActors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next up we want to draw the player. This will be pretty simple; we need to position the sprite in the player’s current location and then draw the sprite. Type in the code below right underneath your “InitActors” function.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DrawPlayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DrawSprite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sprite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/PixelVision8/PixelVision8/wiki/api-draw-sprite&quot; title=&quot;DrawSprite&quot;&gt;DrawSprite&lt;/a&gt; function is provided by the Pixel Vision 8 (via the sprite chip), it handles all of the nitty-gritty of drawing the sprite to the display. It takes three arguments, conveniently all of the data it needs is available from our player data. We provide the sprite, the coordinate on the “x” axis and then the coordinate of the player along the “y” axis. With that information in hand, our sprite will be drawn.&lt;/p&gt;

&lt;p&gt;The last step is to update our “Draw” function to actually draw the player’s sprite. Edit your “Draw” function to look like the code listed below.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DrawPlayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Your entire file of code should look something like this…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-07-pv8-code.png&quot; alt=&quot;Code Listing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and save the code file and run your game. You should see your sprite on the screen. 😎&lt;/p&gt;

&lt;h3 id=&quot;move-the-player&quot;&gt;Move the Player&lt;/h3&gt;

&lt;p&gt;The last step is to let the player move around by pressing the arrow keys on their keyboard. There’s only a little bit of logic to handle, we’ll add it to a new function named “UpdatePlayer”.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdatePlayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Pixel Vision 8 &lt;a href=&quot;https://github.com/PixelVision8/PixelVision8/wiki/api-button&quot; title=&quot;Button&quot;&gt;Button&lt;/a&gt; function is doing most of the work here. This function accepts a button type (up, down, left, right, etc.), a state for the button, and a controller index. The state of the button is either “Down” (pressed) or “Up” (not pressed) and there are only two controllers (0 for player 1, 1 for player 2). When you call the “Button” function it takes this information and compares it to the actual state of the controller, if the controller is in that state then the function returns “true”.&lt;/p&gt;

&lt;p&gt;When this function is called we check each button and, if it’s pressed, we update the current location of the player. Simple, right?&lt;/p&gt;

&lt;p&gt;To make this work, add a call to this function to our “Update” function.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeDelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;UpdatePlayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Try it out! You’ll notice it doesn’t work &lt;em&gt;exactly&lt;/em&gt; as expected. The problem is that we aren’t clearing the display before we draw the sprite in the next location. The &lt;a href=&quot;https://github.com/PixelVision8/PixelVision8/wiki/api-clear&quot; title=&quot;Clear&quot;&gt;Clear&lt;/a&gt; function will clear the screen, if we add that right before we draw the items on screen, we’ll be all set.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DrawPlayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Woot! If you are interested in going the extra mile, how would we change the “UpdatePlayer” code to let the player appear on one side of the screen when they reach the other, like in &lt;a href=&quot;https://freepacman.org/&quot; title=&quot;Pac Man&quot;&gt;Pac Man&lt;/a&gt;? What would we do if we wanted the boundaries of the display to act as a wall? What might we change to make the player move faster or slower?&lt;/p&gt;

&lt;h2 id=&quot;build-your-game&quot;&gt;Build Your Game&lt;/h2&gt;

&lt;p&gt;The last thing we’ll cover is building a “disk” that contains your game, this provides you one with one file that you can share with friends. You simply drag the disk file onto the Pixel Vision 8 window (or executable) to run the game.&lt;/p&gt;

&lt;p&gt;Go ahead and quit the code editor, bringing you back to the Workspace Explorer and viewing the contents of your game’s directory. First we want to change the name of your game and it’s description; double click on the “Info” file or edit your game’s information.&lt;/p&gt;

&lt;p&gt;With this tool you may edit the name and description of your game, along with a couple other items. Go ahead and change the name and description, then choose “Save” from the “///” menu.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-07-pv8-info.png&quot; alt=&quot;Update Game Info&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, click the “Build” button to the right of your game’s name. You will be asked if you really want to build the game’s disk, go ahead and click the “Build” button. Your game will be built and then you will be asked if you want to visit the new build directory, go ahead and click the “No” button and close the editor.&lt;/p&gt;

&lt;p&gt;At this point your game is built! You can switch to your operating system’s file browser, there should be a “PixelVision8” directory present. When you open up that directory, you should see another named “Workspace”, this contains all of the files you were looking at with the Workspace Explorer. Open the “MyFirstGame” directory to see your game’s files, then navigate through the “Builds” and “Build” directories. You should now see the disk file for your game.&lt;/p&gt;

&lt;p&gt;Drag the disk to the Pixel Vision 8 window. The disk with your game should be visible on the desktop of the Workspace Explorer and the files that make up your game should be visible. Now open the disk by double-clicking it and then pick “Run” from the “///” window: you will be able to run your game!&lt;/p&gt;

&lt;p&gt;You can email this file to friend or post it somewhere on the internet. Anyone with the Pixel Vision 8 application installed can run your game or play around with it, perhaps even making their own changes.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It’s quite an achievement to get this much done so quickly, the tools provided by the Pixel Vision 8 make it easy to get straight to game building. Looking at your code, there’s not so much that it’s hard to read, hopefully it’s also pretty clear how things fit together.&lt;/p&gt;

&lt;p&gt;Good luck on your next video game! If time allows, I’ll post another article that gets a little closer to a playable game.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2022-12-07-pv8-drawn-sprite.png&quot; alt=&quot;Your Sprite&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

</content>
 </entry>
 
 <entry>
   <title>Really Using Visual Studio Code Development Containers</title>
   <link href="http:///posts/vscode-devcontainers/"/>
   <updated>2021-09-25T00:00:00-04:00</updated>
   <id>http:///posts/vscode-devcontainers</id>
   <content type="html">&lt;p&gt;Here’s a hypothetical: you have to on-board another developer and, on top weathering their entirely reasonable questions about why this or that tool isn’t being used, you have to help them get their development environment setup. In addition to explaining that, yes, this other tool would be great and improve the project but time is kind of short right now and there are features that need to be developed you suddenly notice that they are not using Linux. All of your suggestions work, but not quite, and it is truly frustrating.&lt;/p&gt;

&lt;h2 id=&quot;development-containers&quot;&gt;Development Containers&lt;/h2&gt;

&lt;p&gt;At this point, like all of us, you wonder “is there a better way?” There’s always a better way and the idea of using Docker to support your development environment is nothing new. The interesting bit is that &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; is bringing some tooling to the table that makes this much easier.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/remote/create-dev-container#_set-up-a-folder-to-run-in-a-container&quot;&gt;A spec for Development containers or “devcontainers”&lt;/a&gt; to specify how to build your development container.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/learn/develop-cloud/containers&quot;&gt;An extension called “Remote Containers”&lt;/a&gt; to make it easier to develop in these containers locally.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/codespaces/getting-started/quickstart&quot;&gt;GitHub Codespace&lt;/a&gt; will run your development container in the cloud and connect remotely from Code or through their website.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we go further, let me clarify: a development container isn’t just &lt;em&gt;one container.&lt;/em&gt; You can also provide a &lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;Docker compose file&lt;/a&gt; where you can describe all of the other containers your need to work on your project (a database server, web server, etc.)&lt;/p&gt;

&lt;p&gt;Put this all together and every new developer on your project is literally one click away from being productive.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fastest: They can click the link on GitHub and use the web-based version of Code and get to work within minutes.&lt;/li&gt;
  &lt;li&gt;Fast: They can click &lt;em&gt;one more button&lt;/em&gt; to spin up the required containers on GitHub and then connect from their own, locally installed and customized copy of Code.&lt;/li&gt;
  &lt;li&gt;Somewhat Quickly: They can clone the project and open it in their local copy of Code, letting Code provision the containers on their local machine&lt;/li&gt;
  &lt;li&gt;Not quick at all: For the truly picky developers, they can do whatever they like on their own time, we’ve already provided three working options!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion, they real pain-point we’re avoiding here is the morass of confusion and mediocre performance that is &lt;a href=&quot;https://www.docker.com/products/docker-desktop&quot;&gt;Docker Desktop&lt;/a&gt;. I could detail my complaints about the product but the fact is that if you’re reading this article, we’re all on the same page already.&lt;/p&gt;

&lt;h2 id=&quot;getting-it-setup&quot;&gt;Getting It Setup&lt;/h2&gt;

&lt;p&gt;If you’re on the fence about Visual Studio Code, then just put your concerns aside and trust me; it’s worth it. It’s free, it’s popular, it’s easy to install and there are extensions available for nearly every feature it lacks. It may not be every developer’s preference but it’s easily good enough to get the job done.&lt;/p&gt;

&lt;p&gt;Next install (if you haven’t already)…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/downloads&quot;&gt;Git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/install/ubuntu/&quot;&gt;Docker&lt;/a&gt; (if you are on Linux) or &lt;a href=&quot;https://www.docker.com/products/docker-desktop&quot;&gt;Docker Desktop&lt;/a&gt; (if you’re on MacOS or Windows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After that, the first step is to get the development container setup and working. You can start from scratch by creating a directory for your sample project (maybe called “new-project”, avoid spaces in the directory name) and then a directory inside of that called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.devcontainer&lt;/code&gt; with a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; inside of that. The simplest file will contain only a pointer to a Docker image. Using Code, create and save the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; file.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mcr.microsoft.com/vscode/devcontainers/php:8-bullseye&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That will spin up a development container based on Microsoft’s PHP image, which in turn is built on Debian Bullseye. To try it out, choose “Command Palette…” from under the “View” menu and start typing “Developer: Reload Window” to choose that command.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wbgnv354viricefxh3rt.png&quot; alt=&quot;Reload the sample project in Code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Code will close and re-open the sample project and then it will prompt you to install the recommended extensions. Click on the “Install” button.”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0jsttxnlb873btbr5ret.png&quot; alt=&quot;Install recommended extensions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now that the extension is installed, Code will suggest that you re-open the project one more time but, this time, open it in a container.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3fw2fnmzabtup19c8m9.png&quot; alt=&quot;Reopen the project in a Docker container&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This time Code will open up the project and then spin up a new Docker container using the image we specified in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.devcontainer.json&lt;/code&gt; file. It will then install it’s own set of tools inside that container to make it easier to edit files, run code, etc.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gbiaxig5kqvh8h6n45wg.png&quot; alt=&quot;Code builds and starts development container&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When it’s all done you’ll be back at the a regular Code window, with your project files on the left-hand side. But if you select “New Terminal…” from under the “Terminal” window, you’ll see that the terminal has opened a session &lt;em&gt;inside&lt;/em&gt; of the container!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m51vgysr3u366tte3awy.png&quot; alt=&quot;Code&apos;s Terminal opens session in the container&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;customize-the-development-container&quot;&gt;Customize the Development Container&lt;/h2&gt;

&lt;p&gt;This is the simplest scenario where we just plop a development container definition into our project and Code spins it up. But what if we needed to add some of our own tools? Perhaps our team relies heavily on &lt;a href=&quot;https://flywaydb.org/&quot;&gt;Flyway&lt;/a&gt; for database migrations and &lt;a href=&quot;https://httpie.io/&quot;&gt;HTTPie&lt;/a&gt; for testing our RESTful endpoints. This can easily be done by writing our own Docker image definition. First remove the “image” attribute from your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; file and replace it with a “dockerFile” attribute that points to our soon-to-be-created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dockerFile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Dockerfile&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; next to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; file and add the following recipe to it.&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/vscode/devcontainers/php:8-bullseye&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# update apt&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt update

&lt;span class=&quot;c&quot;&gt;# install httpie&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; httpie

&lt;span class=&quot;c&quot;&gt;# install flyway&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /usr/local&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;wget &lt;span class=&quot;nt&quot;&gt;-qO-&lt;/span&gt; https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/8.0.0-beta1/flyway-commandline-8.0.0-beta1-linux-x64.tar.gz &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;| &lt;span class=&quot;nb&quot;&gt;tar &lt;/span&gt;xvz &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;/flyway-8.0.0-beta1/flyway /usr/local/bin
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of using the PHP image directly we use it as a starting point for our own container image. We run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt update&lt;/code&gt; to pull down the latest list of packages for the distribution and then we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; to install HTTPie. With that done, we follow the instructions from the Flyway website to get that tool installed as well.&lt;/p&gt;

&lt;p&gt;Save the file and then reload the window (by choosing the option from the command palette). Code will close and then open the project and prompt you to open the project yet again in the container, go ahead and do so. While it’s doing that it will notice that the development container definition has changed and ask you if you’d like to rebuild it, do that as well.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/msoct80kdwyc5j6np92p.png&quot; alt=&quot;Code rebuilds the development container&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Code will delete the Docker image, rebuild the image using our Docker file and then drop us at the project window. You can open up a new terminal panel to verify that our will truly has been done and that our tools are now available.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mjmdjpjqy3dmq6bbjeoc.png&quot; alt=&quot;Verify our tools are installed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At this point you should start to get excited, thoughts of time saved and frustrations avoided racing through your mind. But wait, there’s more!&lt;/p&gt;

&lt;h2 id=&quot;add-more-containers-to-the-development-environment&quot;&gt;Add More Containers to the Development Environment&lt;/h2&gt;

&lt;p&gt;If we have anything at all in common, your next though is something along the lines of “how do I get more containers in there, for things like a database or a web server?” In the same way we added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; we can also add a Docker Compose definition. When Code starts up the project it will bring up all of the containers in the compose definition and then connect to the development container. Let’s add a database server to our devcontainer.&lt;/p&gt;

&lt;p&gt;Open up your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.js&lt;/code&gt; file, remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dockerFile&lt;/code&gt; attribute and replace it with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dockerComposeFile&lt;/code&gt; entry.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dockerComposeFile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;docker-compose.yml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;service&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;development&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;workspaceFolder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/workspace&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We point our devcontainer definition at our Docker Compose file, with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service&lt;/code&gt; attribute we let Code know which container we’d like it to connect to when that stack for the project is brought up. Lastly, we add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;workspaceFolder&lt;/code&gt; attribute and provide the complete path where we want our project files mounted.&lt;/p&gt;

&lt;p&gt;We also need to create that compose file. Create a new file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; right next to our devcontainer definition and add the following.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
      &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Dockerfile&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..:/workspace&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;stdin_open&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the stanza above, we describe the container that we’ll be using for our development environment. We point it at our existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; and we map in the volume that will hold our project files (the directory above the one holding our devcontainer definition, mounted at “/workspace”). The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context&lt;/code&gt; attribute tells compose to look for files in the current directory, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdin_open&lt;/code&gt; indicates that we’ll be connecting with an interactive session.&lt;/p&gt;

&lt;p&gt;That takes care of our development container, now we’ll add a database server. Since we’re using PHP for this project it’s clear that the database needs to be MySQL. Before we add the database service to our compose file, let’s setup the credentials we’d like to use. Docker Compose will read a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; when it starts assembling the stack and it will make the variables in that file available to our script. Create a new file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; next to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file and add the text below.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;DATABASE_HOST=mysql_db
DATABASE_USER=example_account
DATABASE_PASSWORD=applesauce
DATABASE_DB=example
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The database credentials will now be in environment variables that we can use in our compose file. Add the following service you your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file to get our database server setup.&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;mysql_db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql_db&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql/mysql-server:latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MYSQL_ROOT_PASSWORD=${DATABASE_PASSWORD}&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MYSQL_DATABASE=${DATABASE_DB}&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MYSQL_USER=${DATABASE_USER}&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MYSQL_PASSWORD=${DATABASE_PASSWORD}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Before we spin this up, it would be nice to have an convenient way to interact with our database server from inside Code…&lt;/p&gt;

&lt;h2 id=&quot;add-visual-studio-code-extensions&quot;&gt;Add Visual Studio Code Extensions&lt;/h2&gt;

&lt;p&gt;In fact, it would also handy to have Code extensions that we’ve agreed to use in our project installed along with everything else. Let’s add the PHP extension pack and then a MySQL client. Open up the your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devcontainer.json&lt;/code&gt; file and add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extensons&lt;/code&gt; attribute listed below.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dockerComposeFile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;docker-compose.yml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;service&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;development&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;workspaceFolder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/workspace&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;extensions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;felixfbecker.php-pack&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cweijan.vscode-mysql-client2&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open the command palette and ask Code to reload the folder. Code will prompt you to rebuild the devcontainer as the configuration files have changed; agree and let it bring up the new environment. When it’s finished you will be back at your Code window, the new extensions will be listed in the “Extensions” panel under “Dev Container - Installed”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/twpxn2feoehbx2j0oyk0.png&quot; alt=&quot;Dev Container Extensions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can click on the icon for our database client and type in the connection information (remember the “hostname” attribute in our compose file?) to connect to our new MySQL database.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fjafgm307gc4zhwyw64y.png&quot; alt=&quot;Connect to Database Server&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now we’re starting to get somewhere! We have a container we can use for development, with all of our tools and Code extensions installed and &lt;em&gt;another&lt;/em&gt; container running our database server. From here we could add a third container to run our web server and serve our application. We could even add migrations to get our application in a usable state, ready for developers to start working on new features.&lt;/p&gt;

&lt;p&gt;This article is already getting pretty lengthy, we won’t detail getting migrations or a web server working here. You can take a stab at it on your own or you &lt;a href=&quot;https://github.com/cmiles74/devcontainer-example&quot;&gt;can checkout the full example project on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/cmiles74/devcontainer-example&quot;&gt;Complete example project with web server and migrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;github-codespace&quot;&gt;GitHub Codespace&lt;/h2&gt;

&lt;p&gt;The last thing I wanted to mention is that we have everything we need to host our development environment on &lt;a href=&quot;https://docs.github.com/en/codespaces/getting-started/quickstart&quot;&gt;GitHub Codespace&lt;/a&gt;. If we publish this on GitHub, our devcontainer definition will get picked up and (if it’s enabled for your account) the “Codespace” tab with a “New codespace” button will appear for anyone who stumbles upon the project. Anyone who would like to contribute can simply click the button and start work in seconds. Codespace will use the their GitHub credentials to handle pushing, pulling and committing code.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;I encourage you to checkout the &lt;a href=&quot;https://github.com/cmiles74/devcontainer-example&quot;&gt;example project on GitHub&lt;/a&gt;, you should try it out in Codespace as well as launching the stack in Codespace and then connecting with your own, local version of Visual Studio Code. In my opinion this latter option is pretty compelling: you can customize Code the way you like it (including extensions that &lt;em&gt;you&lt;/em&gt; prefer) and then connect to a set of running containers hosted at GitHub.&lt;/p&gt;

&lt;p&gt;This is &lt;em&gt;especially compelling&lt;/em&gt; if you are on a platform that &lt;em&gt;only&lt;/em&gt; provides Docker through Docker Desktop (MacOS or Windows). While Docker Desktop is fine for small projects, anything of significant size will bring Docker Desktop to it’s knees. The real pain point here is sharing files from the host machine to the virtual machine that’s running Linux (and Docker) and then sharing those files once more with the actual containers running in Docker.&lt;/p&gt;

&lt;p&gt;I hope this article has given you an idea of how Code development containers work and how they might fit into your own workflow. In my opinion, it’s an exciting feature and really gets us to a place where using Docker to containerize our development environment starts to become accessible to everyone, regardless of their level of experience with Docker and the related tooling. While Codespace might feel a bit gimmicky, it does allow anyone who may want to contribute to your project a quick and easy way to get started and to submit that pull request.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Getting Started with Clojure, Even on Windows</title>
   <link href="http:///posts/getting-started-clojure-windows/"/>
   <updated>2021-04-29T00:00:00-04:00</updated>
   <id>http:///posts/getting-started-clojure-windows</id>
   <content type="html">&lt;p&gt;When you find yourself looking to flex your language learning skills, functional languages often end up on your short list. After reading this past year’s &lt;a href=&quot;https://www.surveymonkey.com/results/SM-S2L8NR6K9/&quot;&gt;“State of the Clojure Community” survey results&lt;/a&gt;, I couldn’t help but notice how few developers who run Windows are choosing to learn Clojure. For sure, it could be the &lt;a href=&quot;https://en.wikipedia.org/wiki/F_Sharp_(programming_language)&quot;&gt;shining allure of F#&lt;/a&gt;. But maybe it’s also that many tutorials focus on Linux or MacOS and often skip over the installation of the Java development kit. In any case, this article is going to cover getting up-and-running with &lt;a href=&quot;https://en.wikipedia.org/wiki/Clojure&quot;&gt;Clojure&lt;/a&gt;, specifically when using Windows and we won’t be using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux&quot;&gt;Windows subsystem for Linux (WSL)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btqx1hxs06rpv8xn1orp.png&quot; alt=&quot;What is your primary development OS? Answer: Not Windows&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In my opinion, Clojure has a lot to offer anyone working on software development regardless of the operating system we have on our primary machine. :wink:&lt;/p&gt;

&lt;p&gt;Aside from being a functional language, Clojure has some other interesting features: there is a very real stress on immutability and that makes some other things, like concurrent or parallel code, easier to reason about. The language continues to evolve and improve and the community is reasonably friendly and accessible.&lt;/p&gt;

&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;/h2&gt;

&lt;p&gt;This article assumes that you have little or no experience with Clojure and that you haven’t really done much in the way of setup ahead of time. To that end we’ve chosen some tools for you.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visual Studio Code for editing source code&lt;/li&gt;
  &lt;li&gt;Calva will be the VS Code plugin we use to make working with Clojure straightforward&lt;/li&gt;
  &lt;li&gt;Clojure runs on the Java virtual machine, we’ll get our JVM from AdoptOpenJDK&lt;/li&gt;
  &lt;li&gt;There are a couple different tools for managing Clojure projects, we’ll use Leiningen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these are quick and painless to install, even under Windows.&lt;/p&gt;

&lt;h2 id=&quot;install-your-tools&quot;&gt;Install Your Tools&lt;/h2&gt;

&lt;p&gt;We’re going to start at the bottom of our list and work our way up, the first thing we will install is…&lt;/p&gt;

&lt;h3 id=&quot;java&quot;&gt;Java&lt;/h3&gt;

&lt;p&gt;Open up your preferred web browser and navigate to the &lt;a href=&quot;https://adoptopenjdk.net/&quot;&gt;AdoptOpenJDK&lt;/a&gt; website. Whatever they reccomend is fine, leave the radio buttons as-is and click on the “Latest Release” button to download the Java Development Kit installer. Double-click the installer to run, accept the license agreement, and install the software.&lt;/p&gt;

&lt;h3 id=&quot;leiningen&quot;&gt;Leiningen&lt;/h3&gt;

&lt;p&gt;Clojure projects are typically managed by a tool, we’ll be using &lt;a href=&quot;https://leiningen.org/&quot;&gt;Leiningen&lt;/a&gt;. It will be responsible for downloading and managing libraries that we use in our project and building our code during development and deployment.&lt;/p&gt;

&lt;h4 id=&quot;where-do-you-keep-your-scripts&quot;&gt;Where Do You Keep Your Scripts?&lt;/h4&gt;

&lt;p&gt;Leiningen is distributed as a batch file that you place in your path. This is pretty common and if you have a location for storing files like this, you can skip this part. If you don’t have a location, read on!&lt;/p&gt;

&lt;p&gt;I recommend that you store your loose executables and scripts in a directory named “bin” in your home directory. We will now create that folder and then add it to your path, making these scripts available to you from wherever you happen to be on your machine.&lt;/p&gt;

&lt;p&gt;Open up a File Explorer and then select the directory with your name from the right-hand side to view the contents of your “home” directory (“C:\Users\YOUR_NAME”). Create a new directory for your loose scripts and call it “bin” (C:\Users\YOUR_NAME\bin”), you can double-click it to view it’s contents and then click on the path at the top of the window and copy it to the clipboard.&lt;/p&gt;

&lt;p&gt;Next, right click on the icon for “This PC” and choose the “Properties” option; the details for your computer will appear. Click on the “Advanced System Settings” item in the right-hand menu, then click on the “Environment Variables” button in the lower right-hand corner. The “Environment Variables” window will appear with the entries specific to your account at the top. Highlight the entry with the value “Path” in the “Variable” column and then press the “Edit…” button. You will be present with a list of all the directories added to your path, click the “New” button to add a new entry and then paste in (or type) the path to your new “bin” directory (“C:\Users\YOUR_NAME\bin”). Press the “OK” button to make the change permanent and then again to dismiss the “Environment Variables” window and then again to close the “System Properties” window.&lt;/p&gt;

&lt;h4 id=&quot;install-leiningen&quot;&gt;Install Leiningen&lt;/h4&gt;

&lt;p&gt;Installing Leiningen is straightforward: we download the script and then place it in the same directory where we keep all of our loose tools and scripts (i.e. “C:\Users\YOUR_NAME\bin” or any location on your executable path). Open your web browser and &lt;a href=&quot;https://leiningen.org/&quot;&gt;navigate to the Leiningen website&lt;/a&gt; and scroll down to the “Install” section, you will see the instructions and links to the two scripts (one for Linux and one for Windows). Right click on the script for Windows (“lein.bat”) and choose the “Save Link As…” item to download a copy to your machine. The last step is to place the file in your “bin” directory (“C:\Users\YOUR_NAME\bin”).&lt;/p&gt;

&lt;p&gt;Now the Leiningen is available, we can tell it to download its own dependencies so that it’s ready to run. Open up a Powershell window and then run the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;PS &amp;gt; lein self-install
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The self-install should go pretty quickly, typically the only output you’ll see is…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;Downloading Leiningen now...
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just to test it out, you can ask Leiningen what version it is.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;PS&amp;gt; lein -version
Leiningen 2.9.6 on Java 11.0.11 OpenJDK 64-Bit Client VM
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;visual-studio-code&quot;&gt;Visual Studio Code&lt;/h3&gt;

&lt;p&gt;You probably have Visual Studio Code installed and running already, but for the sake of completion we’ll provide a brief overview of that here. If you’re all set with Code, you can definitely skip this step.&lt;/p&gt;

&lt;p&gt;When you visit &lt;a href=&quot;https://code.visualstudio.com/download&quot;&gt;the Visual Studio Code download page&lt;/a&gt;, you can click on the big blue “Windows” download box. That will get you the “User Installer” for your machine. This version will install everything in your current account profile and, in general, is much easier to keep up-to-date.&lt;/p&gt;

&lt;p&gt;Once downloaded, you can double-click to start the installation. After you accept the license and choose the install location, you will be presented with a couple other options. All things being equal, I recommend that you check the boxes to add an “Open with Code” action to Windows Explorer files and directories.&lt;/p&gt;

&lt;h3 id=&quot;install-the-calva-clojure-plugin&quot;&gt;Install the Calva Clojure Plugin&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=betterthantomorrow.calva&quot;&gt;Calva&lt;/a&gt; is a plugin for Visual Studio Code that works with Leiningen (and other tools) to provide an integrated development environment where you can work on your Clojure code and get real work done. Since we already have our Java development environment and Leiningen installed, this will be very easy!&lt;/p&gt;

&lt;p&gt;Open up Visual Studio Code and then click on the “View” menu and choose the “Extensions” item. The “Extensions” panel will load in the left hand side of the editor window and a list of popular extensions will be displayed. Click in the search box at the top of the panel and type in “Clojure”. You will see that there are a variety of plugins for Code that support Clojure, “Calva” should be listed towards the top. Go ahead and click on “Calva” to select it, information about the plugin should appear in the main body of the editor window. Click on the “Install” button at the bottom of the Calva header panel to install it.&lt;/p&gt;

&lt;h2 id=&quot;create-your-project&quot;&gt;Create Your Project&lt;/h2&gt;

&lt;p&gt;Now that we have all of the tools installed, we are ready to create our project! Close the editor panel for Calva and then click on the “View” menu and choose the “Explorer” option. Next, click on the “File” menu and choose “Open Folder…” A new open dialog will appear, navigate to where you would like to store your code and then click the “New Folder” button towards the top of the window to create a new folder, call it something like “tutorial”. Next, click on the new folder to select it and click on the “Select Folder” button at the bottom of the window.&lt;/p&gt;

&lt;h3 id=&quot;create-the-project-file&quot;&gt;Create the Project File&lt;/h3&gt;

&lt;p&gt;Every project needs a project file where we can store information about our project, like it’s name, version and any libraries that it depends on. Click on the “File” menu and choose “New File” to create a new file. Before we start adding content to the file, choose “Save As…” from under the “File” menu and name your file “project.clj”, you want to save it right into the root of your project directory.&lt;/p&gt;

&lt;p&gt;We’ll fill this file in with our project definition. You can model on the very simple one listed below (or copy it directly).&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defproject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tutorial&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:dependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;org.clojure/clojure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.10.3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:main&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tutorial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we set the name of our project, it’s version and a description. Next we add the version of Clojure we’re working with as a dependency (1.10.3 is the current version at the time of this writing). On the last line we point out which file should be the entry point to the project. You can name it anything, in this case we called it “tutorial” meaning that Leiningen should look for a file called “tutorial.clj” in our source code directory.&lt;/p&gt;

&lt;h3 id=&quot;add-a-source-file&quot;&gt;Add a Source File&lt;/h3&gt;

&lt;p&gt;Now we can add a file of source code. Leiningen will look for a source code in the “src” directory; in the “Explorer” panel on the left-hand side of your Code window, you will see some icons appear at the top when the mouse enters the panel or it gains focus. Click on the icon that looks like a folder to create a new directory and call it “src”. Click on that new directory and then click on the icon that looks like a file to create a new file inside that “src” directory and call it “tutorial.clj”. Add the text below to your “tutorial.clj” file and then save it.&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tutorial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;-main&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The “-main” function represents the entry point to our application, if we were to compile our project and run it on it’s own, the “-main” function is called with any parameters we pass in on the command line.&lt;/p&gt;

&lt;h3 id=&quot;start-an-interactive-session-repl&quot;&gt;Start An Interactive Session (REPL)&lt;/h3&gt;

&lt;p&gt;Select the “Command Palette” item from under the “View” menu, the command window will appear at the center top of your Code window, it will have a text area at the top where you can type in text and a list of matching commands in the body of the window. Type in “jack” and you will see the that the list narrows by a lot, you will see the “Calva: Start a Project REPL and Connect (aka Jack-in)” item in that list, to the right of it will be the command shortcut to invoke the task (Control-Alt-C and then Control-Alt-J). Note the shortcut as it’s easier to type then navigating the command palette and then select the “Calva: Start a Project REPL…” item.&lt;/p&gt;

&lt;p&gt;Calva will now ask you what kind of REPL session you’d like to start. Go ahead and pick “Leiningen” (the first item) to start a new REPL with the Leingingen tool. The panel at the bottom of your window will show you details as Calva sets up and connects to your REPL and then a new editor pane will appear with the contents of your session (typically called “output.calva-repl”).&lt;/p&gt;

&lt;p&gt;Go ahead and click into the REPL pane to make it active. You’ll see some introductory text from Calva and then the prompt (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clj:tutorial:&amp;gt;&lt;/code&gt;). Type the following after the prompt and press return.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;(-main)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will call the main function and you should see output similar to what is pictured below.&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;; Jack-in done.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clj-tutorial-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hello!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clj-tutorial-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Congratulations! You have installed all of the tools and started a new Clojure project. :trophy:&lt;/p&gt;

&lt;h4 id=&quot;working-with-calva&quot;&gt;Working with Calva&lt;/h4&gt;

&lt;p&gt;There’s a good amount of documentation on the &lt;a href=&quot;https://calva.io/&quot;&gt;Calva website&lt;/a&gt;, you should definitely check it out. There is a &lt;a href=&quot;https://calva.io/try-first/&quot;&gt;“try first” tutorial&lt;/a&gt; that goes over compiling your whole file or just the form you are working on. From there you can explore the rest of the documentation to get productive in no time.&lt;/p&gt;

&lt;h4 id=&quot;build-a-self-contained-file&quot;&gt;Build a Self Contained File&lt;/h4&gt;

&lt;p&gt;Now that we have some code that knows how to say “hi”, the last thing we’ll do is build one Java archive (JAR) file that contains our application. We can then distribute that archive to anyone else who would like to run our code.&lt;/p&gt;

&lt;p&gt;First we need to compile a class with our entry point so that a regular Java runtime knows how to start it up. Open up the “src\tutorial.clj” file and add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(:gen-class)&lt;/code&gt; to the namespace declaration at the top, like so:&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tutorial&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:gen-class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;-main&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next we’ll ask Leiningen to compile one archive for our project. If we had included any libraries in our project, Leiningen would add those to the archive as well. Click on the “View” menu and choose the “Terminal” item, a new terminal pane will open at the bottom of your code window. Type in the following and press return to build a self contained JAR file for your project.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;C:\&amp;gt; lein uberjar
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Leiningen will display some output and build your archive. You may run it like so…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;C:\&amp;gt; java -jar target\tutorial-0.1-standalone.jar
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should see the output with the text “Hello!” At this point you should know enough to be dangerous. Good luck! :smirk:&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Provision Pragmatically and Programmatically with CloudFormation, 3</title>
   <link href="http:///posts/cloudformation-tutorial-3/"/>
   <updated>2019-04-22T00:00:00-04:00</updated>
   <id>http:///posts/cloudformation-tutorial-3</id>
   <content type="html">&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/cmiles74/cloudformation-tutorial/master/template-diagram-3.png&quot; alt=&quot;CloudFormation Diagram&quot; title=&quot;Diagram of our CloudFormation Tutorial Stack&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is the third and final part in a three part series where we build up a CloudFormation tempate for what I like to think of as a pretty typical environment: one virtual private cloud broken into two subnets and including two instances. So far we have covered…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Part 1:  we setup of the VPC and subnets&lt;/li&gt;
  &lt;li&gt;Part 2:  we setup some buckets, groups and privileges, and setup our instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this final part we put our backup jobs in place and wrap-up the series. We have covered a lot of CloudFormation syntax and have realy exercised the tool, after this last part we’ll have everything you need to support your next project. If you want to browse through the complete template, &lt;a href=&quot;https://github.com/cmiles74/cloudformation-tutorial&quot;&gt;it is available on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;setting-up-lambda-for-snapshotting-and-retention&quot;&gt;Setting Up Lambda for Snapshotting (and Retention)&lt;/h2&gt;

&lt;p&gt;What, when and how to backup your instances and data is a complicated subject and covers a lot of ground. For this project we’re only going to deal with the bare minimum: performing nightly snapshots of your images and retaining those snapshots according to a schedule. This will get a reliable image of your server’s storage at backup time but it may not guarantee that your database files are in a consistent state. We don’t walk through the process here, but my advice is to setup a job on your database server that writes a backup of the database to disk &lt;em&gt;before&lt;/em&gt; the snapshot job runs. If you ever restore a snapshot you will have a consistent backup of the database right there along with it.&lt;/p&gt;

&lt;p&gt;We will use the &lt;a href=&quot;https://aws.amazon.com/lambda/&quot;&gt;Amazon Lambda&lt;/a&gt; service to perform the snapshot and cull old snapshot images. Since the Lambda script run independent of our instances we can be sure it will always be run and we can monitor the success or failure of the backup job on it’s own: we don’t need to provide administrative access to our instances in order to perform or monitor the backup process. It’s also really nice that we can get the backup job writtent and start it running all from within our CloudFormation script.&lt;/p&gt;

&lt;h3 id=&quot;daily-snapshot-job&quot;&gt;Daily Snapshot Job&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is to setup a new role for our backup job, this role will have the ability to create snapshots, list the existing snapshots, tag and otherwise modify snapshots and write logs to CloudFormation (so we can monitor those backup jobs).&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialLambdaBackupRole&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Statement&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Allow&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Principal&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;lambda.amazonaws.com&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sts:AssumeRole&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Policies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PolicyName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialLambdaBackupRolePolicy&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PolicyDocument&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;Statement&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
             &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Allow&lt;/span&gt;
               &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ec2:CreateTags&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ec2:CreateSnapshot&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ec2:DeleteSnapshot&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ec2:Describe*&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ec2:ModifySnapshotAttribute&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ec2:ResetSnapshotAttribute&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xray:PutTraceSegments&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xray:PutTelemetryRecords&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xray:GetSamplingRules&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xray:GetSamplingTargets&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xray:GetSamplingStatisticSummaries&lt;/span&gt;
                 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;logs:*&lt;/span&gt;
               &lt;span class=&quot;na&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ve seen all of this stuff before: we create a new role and we associate the AWS service (in this case Lambda) and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AssumeRole&lt;/code&gt; action, letting whoever we assign the role the ability to “assume” it. We then create a new policy for the role that allows whoever assumes this role access to manage snapshots for our account as well as the ability to write to the CloudFormation logs and the &lt;a href=&quot;https://aws.amazon.com/xray/&quot;&gt;Amazon X-Ray (tracing) service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next we create our Lambda process, I’m going to leave out the code for now and we’ll go over that next. There are better ways to get your backup script into your Lambda but they all involve adding a lot of complexity to this article. When you have the time and energy you can look into this on your own (or maybe I’ll write another article). In any case, having the script inline works for now.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialCreateBackupLambda&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FunctionName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ebs-snapshots-create&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Create EBS Snapshots&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.handler&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialLambdaBackupRole.Arn&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Variables&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;REGIONS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Region&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;python3.6&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;90&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TracingConfig&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Active&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;code ellided for clarity&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AWS::Lambda::Function&lt;/code&gt; resource to provision our new Lambda function ad we assign it a name, description and tell it how to invoke itself. We’re going to write the backup job in &lt;a href=&quot;https://www.python.org/&quot;&gt;Python&lt;/a&gt; because it keeps the code simple and nearly everyone knows Python. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;-&lt;/code&gt; And next, the Python code itself.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;env_regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;REGIONS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;total_created&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegionNames&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_created&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# loop through all of our regions
&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Regions&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;regionName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;RegionName&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Checking for volumes in region %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;RegionName&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# query for volumes with matching tags that are in use
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe_volumes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Values&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;in-use&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;No matching EBS volumes with tag &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; and tag &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; in region %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projectTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_created&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Volumes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;volume_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VolumeId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;volume_description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Created by ebs-snapshots-create&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# check the volume for matching tags
&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;volume_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;volume_description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# daily
&lt;/span&gt;      &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Creating snapshot for EBS volume %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VolumeId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;VolumeId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VolumeId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_description&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ec2_resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;RegionName&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;snapshot_resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SnapshotId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;snapshot_resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Interval&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;total_created&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# quarterly
&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Creating quarterly snapshot for EBS volume %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VolumeId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;VolumeId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VolumeId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_description&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ec2_resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;RegionName&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;snapshot_resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SnapshotId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;snapshot_resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Interval&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Quarterly&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;total_created&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# annual
&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Creating annual snapshot for EBS volume %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VolumeId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;VolumeId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VolumeId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_description&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ec2_resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;RegionName&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;snapshot_resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SnapshotId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;snapshot_resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;volume_description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Interval&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Annual&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;total_created&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_created&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You only have so many characters available for an inline Lambda script so I’ve kept the comments to a minimum. The script is straightforward and the code (for the sake of clarity) doesn’t do anything tricky; it should be easy to follow. Basically what it does is check all of the regions we provided in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REGIONS&lt;/code&gt; environment variable and then it checks that region for instances. When it finds one it gets a handle on all of it’s volumes and then snapshots each one.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First the daily snapshot is created, the script always takes this snapshot&lt;/li&gt;
  &lt;li&gt;If this is the first day of the first month of the quarter then a quarterly snapshot is taken as well&lt;/li&gt;
  &lt;li&gt;If it’s the last day of the year then we take the annual snapshot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each snapshot created our script sets the snapshot name to match the volume name and the description of “Created by ebs-snapshot-create” (the name of our Lambda function). It also sets the “Client” and “Project” tags on each snapshot to match the environment variable values.&lt;/p&gt;

&lt;p&gt;The script keeps track of how many snapshot it’s created and returns that number when the function ends. Along the way we also &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print&lt;/code&gt; data to standard out: the standard out and the return value will all be gathered and logged when the script runs.&lt;/p&gt;

&lt;h3 id=&quot;daily-snapshot-retention-job&quot;&gt;Daily Snapshot Retention Job&lt;/h3&gt;

&lt;p&gt;We don’t want to accrue snapshots indefininitely, don’t forget, we get charged for their storage. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;-)&lt;/code&gt; This next job will enforce our retention policy.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialDeleteBackupLambda&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FunctionName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ebs-snapshots-delete&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Delete Old EBS Snapshots&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.handler&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialLambdaBackupRole.Arn&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Variables&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;REGIONS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Region&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;python3.6&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;90&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TracingConfig&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Active&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;code elided for clarity&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s almost exactly like the last job, aside from it’s name and description. The real difference is in the script for the Lambda function. The script calculates the cutoff date for our daily snapshots: seven days old. As the script checks through the snapshots for the volumes it will check the tags that we placed on the snapshots in the backup job, specificaly the “Interval” tag. Daily snapshots older than our cutoff date will be removed leaving only the seven momst recent daily snapshots.&lt;/p&gt;

&lt;p&gt;We do the same for the quartlerly snapshots; we calculate the cutoff data and then check through all of the snapshots tagged with an “Interval” value of “Quarterly”. All of the snapshots past the cutoff are deleted leaving us with the four most recent quartlery snapshots.&lt;/p&gt;

&lt;p&gt;The annual snapshots we leave as is, we will accrue those at the rate of once a year forever. You never know when someone will want to dig up old, historic data.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateutil&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativedelta&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;date_format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%Y-%m-%dT%H:%M:%S.%fZ&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;daily_cutoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;utcnow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;timedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;days&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;env_regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;REGIONS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;total_deleted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegionNames&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_created&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Regions&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;regionName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;RegionName&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Checking for snapshots in region %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# query for Daily snapshots with matching tags
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe_snapshots&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tag:Interval&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Values&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;No matching Daily snapshots with tag &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; and tag &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; in region %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projectTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_deleted&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Snapshots&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;snapshot_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StartTime&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;snapshot_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SnapshotId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;daily_cutoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;snapshot_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;snapshot_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Deleting Daily EBS snapshot ID = %s, Name = %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snapshot_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete_snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SnapshotId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;total_deleted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# query for quarterly snapshots with matching tags
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;quarterly_cutoff_raw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;utcnow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;relativedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;months&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# keep five quarters
&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;quarterly_cutoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quarterly_cutoff_raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe_snapshots&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tag:Interval&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Values&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Quarterly&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;No matching Quarterly snapshots with tag &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; and tag &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; in region %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projectTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_deleted&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Snapshots&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;snapshot_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StartTime&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;snapshot_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SnapshotId&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quarterly_cutoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;snapshot_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;snapshot_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

          &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Deleting Quarterly EBS snapshot ID = %s, Name = %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snapshot_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;ec2_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete_snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SnapshotId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snapshot_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;total_deleted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_deleted&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With these two functions added to your CloudFormation template you are nearly ready to go. All we need to do is call these jobs every night.&lt;/p&gt;

&lt;h3 id=&quot;rules-to-invoke-our-jobs&quot;&gt;Rules to Invoke our Jobs&lt;/h3&gt;

&lt;p&gt;We want to run our jobs every night, first the backup job and then the retention job that culls out old snapshots. Lucky for us CloudWatch will let you create a &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/Create-CloudWatch-Events-Scheduled-Rule.html&quot;&gt;“rule” that is triggered on a schedule&lt;/a&gt;, we can create our own rule that will start our backup jobs. The &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-target.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AWS::Events::Rule&lt;/code&gt;&lt;/a&gt; can be used to do exactly this.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPerformBackupRule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Events::Rule&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Triggers our backup lambda script&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ScheduleExpression&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cron(30 4 * * ? *)&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ENABLED&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Targets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Arn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialCreateBackupLambda.Arn&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialBackupRule&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;TutorialDeleteBackupRule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Events::Rule&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Triggers our backup delete lambda script&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ScheduleExpression&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cron(45 4 * * ? *)&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ENABLED&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Targets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Arn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialDeleteBackupLambda.Arn&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialBackupRule&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These two jobs use the familiar &lt;a href=&quot;https://crontab.guru/&quot;&gt;cron style syntax&lt;/a&gt;, we backup at 4:00AM and then delete old snapshots at 4:45AM every day. Each rule has a pointer to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ARN&lt;/code&gt; of the Lambda function it will invokce.&lt;/p&gt;

&lt;p&gt;With our jobs all setup, we need to provide our rules with permissions to invoke our Lambda functions with the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-permission.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AWS::Lambda::Permission&lt;/code&gt;&lt;/a&gt; resource.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPermissionCreate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Lambda::Permission&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FunctionName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialCreateBackupLambda.Arn&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;lambda:InvokeFunction&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Principal&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;events.amazonaws.com&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SourceArn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPerformBackupRule.Arn&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;TutorialPermissionDelete&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Lambda::Permission&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FunctionName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialDeleteBackupLambda.Arn&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;lambda:InvokeFunction&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Principal&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;events.amazonaws.com&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SourceArn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialDeleteBackupRule.Arn&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvokeFunction&lt;/code&gt; permission to our two CloudFront triggers so that they are allowed to invoke our Lambda functions. With that our backups are all taken care of.&lt;/p&gt;

&lt;h2 id=&quot;template-wrap-up-output&quot;&gt;Template Wrap-Up: Output&lt;/h2&gt;

&lt;p&gt;The last bit of any CloudFront template is provided output data, this data will be provided as a JSON object. You could provide thiss data to another process that double-checks that the resources have been created or performs additional tasks. Here’s our output section…&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;Outputs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;WebServer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;A reference to the web server&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialWebServer&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;DatabaseServer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;A reference to the database server&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialDatabaseServer&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;BackupS3Bucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;S3 Bucket used to backup data&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;https://${TutorialBackupS3Bucket.DomainName}\&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we return references to our two server instances and our S3 bucket. There are certainly more items we could list in this section, perhaps our reference to our VPC. For now we’ll leave it short and to the point.&lt;/p&gt;

&lt;p&gt;Whew! That is the last piece in the series! You can model on the template we’ve put together here and start provisioning resources on CloudFormation with a slightly more rapidity and confidence. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;-D&lt;/code&gt;&lt;/p&gt;

&lt;hr /&gt;

</content>
 </entry>
 
 <entry>
   <title>Provision Pragmatically and Programmatically with CloudFormation, 2</title>
   <link href="http:///posts/cloudformation-tutorial-2/"/>
   <updated>2019-02-05T00:00:00-05:00</updated>
   <id>http:///posts/cloudformation-tutorial-2</id>
   <content type="html">&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/cmiles74/cloudformation-tutorial/master/template-diagram-2.png&quot; alt=&quot;CloudFormation Diagram&quot; title=&quot;Diagram of our CloudFormation Tutorial Stack&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is the second part in a three part series where we build up a CloudFormation template for what I think of as a pretty typical environment: one virtual private cloud broken into two subnets and including two instances. If you haven’t read the first part in the series, I encourage you to check it out now!&lt;/p&gt;

&lt;h2 id=&quot;setup-an-admininstrators-group&quot;&gt;Setup an “Admininstrators” Group&lt;/h2&gt;

&lt;p&gt;For this project we’re creating a general “administrators” group that has access to the backup bucket with our database backups. I generally don’t allow anyone direct access to the EC2 console unless they really need it, so I don’t address that in this template (in addition, not every EC2 web console action can be managed this way). My thinking here is that we typically give “administrators” or “developers” accounts on the individual instances, they don’t really need the EC2 web console in order to get their work done.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialAdminGroup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::IAM::Group&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Policies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PolicyName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialAdminPolicy&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;PolicyDocument&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Statement&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Allow&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:ListBucket&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:ListBucketByTags&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:ListBucketMultipartUploads&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:ListBucketVersions&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${TutorialBackupS3Bucket.Arn}&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Allow&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:PutObject&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:DeleteObject&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:GetObject&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${TutorialBackupS3Bucket.Arn}/*&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-group.html&quot;&gt;IAM Group resource&lt;/a&gt; to create our new group. The only property that we provide is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Policies&lt;/code&gt; property that contains a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PolicyName&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PolicyDocument&lt;/code&gt; pairs, in this case we define only one that we have named “TutorialAdminPolicy”.&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PolicyDocument&lt;/code&gt;contains a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Statement&lt;/code&gt; that holds a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Effect&lt;/code&gt; instances; each of those in turn contains an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Action&lt;/code&gt; property with a list of permissions. We use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resource&lt;/code&gt; property to tie in a reference to our bucket, in this case the backup bucket’s ARN. If we take a look at the first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Effect&lt;/code&gt;, you’ll see that we’ve assigned four “ListBucket…” permissions for our backup bucket. The second effect applied three more actions to every file in the backup bucket, that’s indicated by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/*&lt;/code&gt; at the end of the bucket’s ARN.&lt;/p&gt;

&lt;p&gt;Any account that we add to this group will be able to download or delete any of the files in the bucket (as well as upload). In this case when we say “files” we really mean database dumps for this project. They will also be able to pull up the S3 console for the bucket but they will need a link that is &lt;em&gt;directly to the bucket&lt;/em&gt;. They will not be able to log into S3 and browse the list of all buckets.&lt;/p&gt;

&lt;p&gt;One last note: since we are creating an IAM role with our template, we need to let CloudFormation know that this is okay. From here on out we need to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--capabilities&lt;/code&gt; flag to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws&lt;/code&gt; commands.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;aws cloudformation create-stack &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--stack-name&lt;/span&gt; tutorial &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--template-body&lt;/span&gt; file://template.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--parameters&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ParameterKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;KeyPairName,ParameterValue&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cloudfront-tutorial &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Project,Value&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cf-tutorial &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--capabilities&lt;/span&gt; CAPABILITY_IAM
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you don’t include the tag, CloudFormation will exit with an error. The same flag can be used with “update” commands.&lt;/p&gt;

&lt;h3 id=&quot;setup-a-role-for-our-instances&quot;&gt;Setup a Role for Our Instances&lt;/h3&gt;

&lt;p&gt;Next we need to setup the role that our instances can assume to get access to resources (for now, just the backup bucket).&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialInstanceRole&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;arn:aws-us-gov:iam::aws:policy/CloudWatchAgentServerPolicy&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;arn:aws-us-gov:iam::aws:policy/service-role/AmazonEC2RoleforSSM&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;arn:aws-us-gov:iam::aws:policy/AmazonSSMReadOnlyAccess&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Statement&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Allow&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Principal&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ec2.amazonaws.com&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sts:AssumeRole&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html&quot;&gt;Role resource&lt;/a&gt; to create our new instance role and we assign three canned policies…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the first lets the instance run the &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html&quot;&gt;CloudWatch agent&lt;/a&gt; and submit events to the &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html&quot;&gt;CloudWatch service&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;the next lets the instance interact with &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/what-is-systems-manager.html&quot;&gt;Systems Manager&lt;/a&gt;, letting us manage the instances as a group&lt;/li&gt;
  &lt;li&gt;The last provides read-only access to the Systems Manager parameter store.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Amazon’s CloudWatch service will aggregate data submitted by your instances and let you setup reports, dashboards and alerts based on this data (i.e. when CPU usage gets too high or available disk space is too low). Systems Manager provides some tools for managing your instances, like installing patches or a software package. It lets you perform these actions on several instances as a group which can be handy. I’m not going to go over these services in-depth here but I encourage you to spend some time checking them out if you haven’t done so already.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialInstanceRolePolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::IAM::Policy&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;PolicyName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialInstanceRolePolicy&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Roles&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Ref&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialInstanceRole&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;PolicyDocument&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Statement&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Allow&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:ListBucket&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:ListBucketByTags&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:ListBucketMultipartUploads&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:ListBucketVersions&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${TutorialBackupS3Bucket.Arn}&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Allow&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:PutObject&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:DeleteObject&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3:GetObject&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${TutorialBackupS3Bucket.Arn}/*&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We are creating a new &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-policy.html&quot;&gt;Policy resource&lt;/a&gt; that we will assign to our instances. We link the role we just created and then add a new policy that provides access to the backup bucket. As you can see, it is exactly the same as the role that we created for our administrators group.&lt;/p&gt;

&lt;p&gt;The last piece of this puzzle is an “instance profile” that we can assign to our instances.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialInstanceProfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::IAM::InstanceProfile&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Roles&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialInstanceRole&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With all of the other work done, there’s not much to see here. We create a new &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html&quot;&gt;InstanceProfile resource&lt;/a&gt; and link it to our role. When we provision instances we can assign them this profile, they will then have the ability to write to our backup bucket, submit performance and event data to CloudWatch and accept remote commands from System Manager.&lt;/p&gt;

&lt;h3 id=&quot;provision-cloudwatch-log-groups&quot;&gt;Provision CloudWatch Log Groups&lt;/h3&gt;

&lt;p&gt;The very last thing we need before provisioning are a couple CloudWatch &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html&quot;&gt;log groups&lt;/a&gt;. We can instruct our instances to ship some of their log files out to these groups and then view the logs through the CloudWatch console.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialBootLog&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Logs::LogGroup&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;TutorialKernelLog&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Logs::LogGroup&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html&quot;&gt;LogGroup&lt;/a&gt; resource to create two new log groups. These groups will be names after the name we provide in the template with some random characters tacked on at the end. We’re creating the groups now because we’re going to reference them directly when we provision the instances.&lt;/p&gt;

&lt;h3 id=&quot;provision-the-database-server&quot;&gt;Provision the Database Server&lt;/h3&gt;

&lt;p&gt;I know what you are thinking: all that work and we’re only now provisioning our instances! It’s true, there was a lot of preparation and things to think about but we are well on our way to having a template that will provision our servers in a repeatable and reasonably safe manner. Just think of all the templates you will be writing!&lt;/p&gt;

&lt;p&gt;We’re going to provision the database server first. This one is a little bit involved so I’m going to break it up into pieces. First we provision a new instance with the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html&quot;&gt;Instance resource&lt;/a&gt;…&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialDatabaseServer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::Instance&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AWS::CloudFormation::Init&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;/etc/profile.d/cloudformation-init.sh&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;export BACKUP_S3_BUCKET=&quot;${TutorialBackupS3Bucket}&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that in the code stanza above I stop before the properties, we’ll go over the properties next.&lt;/p&gt;

&lt;p&gt;The first thing we do is setup the “metadata” for the CloudFormation initialization script (we will call that at the very end) with the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html&quot;&gt;CouldFormation Init type&lt;/a&gt;. This is were we tell the initialization script what we would like it to do when it runs, in this case we add a new environment variable to a new file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/profile.d&lt;/code&gt; called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloudformation-init.sh&lt;/code&gt;. Whenever someone logs into the machine this script (along with everything else in the directory) will be evaluated, the end result will be that the name of our backup bucket will be available through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BACKUP_S3_BUCKET&lt;/code&gt; environment variable. Keep in mind that this metadata doesn’t do anything on it’s own: the initialization script will use it when it runs and we’ll do that at the end of our instance definition.&lt;/p&gt;

&lt;p&gt;We also need to provide the configuration file for the CloudWatch agent. This agent will collect performance data and log files and ship them back to the CloudWatch service.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;            &lt;span class=&quot;na&quot;&gt;/etc/amazon/amazon-cloudwatch-agent.json&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;{&lt;/span&gt;
                  &lt;span class=&quot;s&quot;&gt;&quot;metrics&quot;: {&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;append_dimensions&quot;: {&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;AutoScalingGroupName&quot;: &quot;${!aws:AutoScalingGroupName}&quot;,&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;ImageId&quot;: &quot;${!aws:ImageId}&quot;,&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;InstanceId&quot;: &quot;${!aws:InstanceId}&quot;,&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;InstanceType&quot;: &quot;${!aws:InstanceType}&quot;&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;metrics_collected&quot;: {&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;cpu&quot;: {&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;measurement&quot;: [&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;cpu_usage_idle&quot;,&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;cpu_usage_iowait&quot;,&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;cpu_usage_user&quot;,&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;cpu_usage_system&quot;&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;],&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;metrics_collection_interval&quot;: 30,&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;totalcpu&quot;: false&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;disk&quot;: {&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;measurement&quot;: [&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;used_percent&quot;,&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;inodes_free&quot;&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;],&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;metrics_collection_interval&quot;: 30,&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;resources&quot;: [&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;]&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;diskio&quot;: {&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;measurement&quot;: [&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;io_time&quot;&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;],&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;metrics_collection_interval&quot;: 30,&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;resources&quot;: [&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;]&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;mem&quot;: {&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;measurement&quot;: [&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;mem_used_percent&quot;&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;],&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;metrics_collection_interval&quot;: 30&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;statsd&quot;: {&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;metrics_aggregation_interval&quot;: 30,&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;metrics_collection_interval&quot;: 10,&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;service_address&quot;: &quot;:8125&quot;&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;swap&quot;: {&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;measurement&quot;: [&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;&quot;swap_used_percent&quot;&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;],&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;metrics_collection_interval&quot;: 30&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
                  &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
                  &lt;span class=&quot;s&quot;&gt;&quot;logs&quot;: {&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;logs_collected&quot;: {&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;files&quot;: {&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;collect_list&quot;: [&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;s&quot;&gt;&quot;log_group_name&quot;: &quot;${TutorialBootLog}&quot;,&lt;/span&gt;
                            &lt;span class=&quot;s&quot;&gt;&quot;file_path&quot;: &quot;/var/log/boot.log&quot;&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;},&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;s&quot;&gt;&quot;log_group_name&quot;: &quot;${TutorialKernelLog}&quot;,&lt;/span&gt;
                            &lt;span class=&quot;s&quot;&gt;&quot;file_path&quot;: &quot;/var/log/messages&quot;&lt;/span&gt;
                          &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;]&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
                  &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This configuration file tells the CloudWatch agent to ship back data on CPU, disk, memory and swap file usage back to CloudWatch as well as two log files: the boot and kernel logs.&lt;/p&gt;

&lt;p&gt;Next we’ll set the properties for our instance…&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ImageId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ImageId&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;InstanceType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;InstanceType&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;KeyName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;KeyPairName&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;NetworkInterfaces&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SubnetId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPrivateSubnet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DeviceIndex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;GroupSet&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPrivateSecurityGroup&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;BlockDeviceMappings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DeviceName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/dev/${RootDevice}&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Ebs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;VolumeSize&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;VolumeSize&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;VolumeType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gp2&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;IamInstanceProfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialInstanceProfile&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tutorial&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Server&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Way back in part 1 we create these parameters and provided default values for them, now they are finally coming into play. If you look back at the top of the template, you’ll see that we set parameters for the image, instance type, key pair name and root volume. While our template requires that a key pair name be provided the other values are populated with default values. My expectation is that the default values will normally be used, most of the reason they are at the top in the parameter section is to make them easy to get at.&lt;/p&gt;

&lt;p&gt;The first thing we do is choose the image we’d like for this instance, in the code above we have a reference to the parameter, the default value is for the &lt;a href=&quot;https://aws.amazon.com/amazon-linux-2/&quot;&gt;Amazon Linux 2&lt;/a&gt; image. There are other Linux images out there, I picked this one mostly because it already has the AWS tools and CloudFormation stuff installed, making things that much easier. Also kind of interesting, if you develop locally you can work with an &lt;a href=&quot;https://hub.docker.com/_/amazonlinux/&quot;&gt;Amazon Linux 2 Docker Container&lt;/a&gt; on your workstation.&lt;/p&gt;

&lt;p&gt;Next we use our instance type parameter, the default values is for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t2.micro&lt;/code&gt; because this is a tutorial and I don’t want to cost you money; this type is eligible for use under &lt;a href=&quot;https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/free-tier-limits.html&quot;&gt;Amazon’s free tier&lt;/a&gt;. If you haven’t used up your 750 hours of free tier usage for the month you shouldn’t be charged for provisioning these instances. Setting cost aside, a micro instance is likely enough to host a low traffic website, like your personal blog.&lt;/p&gt;

&lt;p&gt;We set the name of the key pair we want to use to provision our instance, note that the value we reference in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KeyName&lt;/code&gt; property is the one parameter we set for this script back in part 1. You will need to have this key pair handy in order to SSH to the instance.&lt;/p&gt;

&lt;p&gt;We’d like the database server to be on our private subnet and we take care of that when we specify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NetworkInterfaces&lt;/code&gt; property. Here we pass in a reference to our private subnet, we then set the security group to our “private” security group with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupSet&lt;/code&gt; property of the network interface.&lt;/p&gt;

&lt;p&gt;Every instance needs disk space and we can customize the &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html&quot;&gt;Elastic Block Store&lt;/a&gt; (EBS) volumes for our instance with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BlockDeviceMappings&lt;/code&gt; property. We map in one volume of 250GB (referencing our parameter) to the root device by using the substitution function with our parameter to set the value to  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/xvda&lt;/code&gt;, we chose the “general purpose” (gp2) volume type.&lt;/p&gt;

&lt;p&gt;The default volume type is “gp2” and it’s a reasonable choice, more information about the various types may be found in the &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html&quot;&gt;EBS volume type documentation&lt;/a&gt;. Also note that the root device that the instance boots from may vary from one Linux distribution to the other. For instance, under Ubuntu the root volume needs to be mapped to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda1&lt;/code&gt;; if the instance can’t find the volume you will see it start up in the EC2 console but it will stop in just a couple of minutes.&lt;/p&gt;

&lt;p&gt;Instead of managing credentials for our EC2 instances we thoughtfully created a profile! We set the profile for our instance with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IamInstanceProfile&lt;/code&gt; property and pass in a reference to that profile.&lt;/p&gt;

&lt;p&gt;Lastly we set tags on our instance, in this case we set the “Name” tag.&lt;/p&gt;

&lt;p&gt;With that out of the way the only thing left to do is to invoke the CloudFormation initialization script when the instance starts up. To get this done we write a little script that calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cfn-init&lt;/code&gt; at start time.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;      &lt;span class=&quot;na&quot;&gt;UserData&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;Fn::Base64&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
          &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;#!/bin/bash -xe&lt;/span&gt;

            &lt;span class=&quot;s&quot;&gt;# cloudformation initialize&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;/opt/aws/bin/cfn-init -v -s ${AWS::StackName} --region ${AWS::Region} -r TutorialDatabaseServer&lt;/span&gt;

            &lt;span class=&quot;s&quot;&gt;# download and install cloudwatch agent&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;yum -y install amazon-cloudwatch-agent.rpm&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;mv /etc/amazon/amazon-cloudwatch-agent.json /etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.json&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;sudo systemctl enable amazon-cloudwatch-agent&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;sudo systemctl start amazon-cloudwatch-agent&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Documentation for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cfn-init&lt;/code&gt; is &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-init.html&quot;&gt;available on the CloudFormation site&lt;/a&gt;. You can see in the example above that we call the script with the name of our stack, the stack’s deployment region and the name of our server. When the script runs it will inspect the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Metadata&lt;/code&gt; property that we set at the the beginning of our instance stanza and will carry out those tasks. For this instance, the initialization script will add that new file to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/profile.d&lt;/code&gt; with our custom environment variables.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cfn-init&lt;/code&gt; script handles getting our configuration files written to disk, the other thing we need to do is get the CloudWatch agent installed and running. The next bit of script downloads the current version, installs it, moves our configuration file into place and then enables and starts the service so that the agent is started at boot time.&lt;/p&gt;

&lt;p&gt;And that’s it… For our database server. We need to do pretty much the same thing for our web server.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialWebServer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::Instance&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AWS::CloudFormation::Init&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;/etc/profile.d/cloudformation-init.sh&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;export BACKUP_S3_BUCKET=&quot;${TutorialBackupS3Bucket}&quot;&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;export DATABASE_SERVER=&quot;${TutorialDatabaseServer.PrivateIp}&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Mostly everything is exactly the same but there are a couple small differences. When we setup the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Metadata&lt;/code&gt; for the initialization script we added another environment variable that contains the private IP address of the database server, in this way we can deploy this template more than once and the web server in each stack will know where to find it’s matching database server.&lt;/p&gt;

&lt;p&gt;I don’t include it in this article, but we provide the exact same configuration file for the CloudWatch agent that we used for the web server. If you are writing the template along with this article, take a minute to copy and paste that stanza into the file now.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ImageId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ImageId&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;InstanceType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;InstanceType&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;KeyName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;KeyPairName&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;NetworkInterfaces&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;SubnetId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPublicSubnet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DeviceIndex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;GroupSet&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPublicSecurityGroup&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;BlockDeviceMappings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DeviceName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/dev/${RootDevice}&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Ebs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;VolumeSize&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;VolumeSize&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;VolumeType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gp2&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;IamInstanceProfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialInstanceProfile&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tutorial&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Web&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Server&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;UserData&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;Fn::Base64&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
          &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;#!/bin/bash -xe&lt;/span&gt;

            &lt;span class=&quot;s&quot;&gt;# cloudformation initialize&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;/opt/aws/bin/cfn-init -v -s ${AWS::StackName} --region ${AWS::Region} -r TutorialDatabaseServer&lt;/span&gt;

            &lt;span class=&quot;s&quot;&gt;# download and install cloudwatch agent&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;yum -y install amazon-cloudwatch-agent.rpm&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;mv /etc/amazon/amazon-cloudwatch-agent.json /etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.json&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;sudo systemctl enable amazon-cloudwatch-agent&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;sudo systemctl start amazon-cloudwatch-agent&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In terms of the properties, the only difference is that we have placed our web server in the public subnet so that it can communicate with the public internet (as web servers so often need to do).&lt;/p&gt;

&lt;p&gt;At this point we have covered the first four goals that we laid out for ourselves in part 1. With the template as it stands right now, we can…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Provision resources for a project&lt;/li&gt;
  &lt;li&gt;Retire resources for a project&lt;/li&gt;
  &lt;li&gt;Isolate resources for a project&lt;/li&gt;
  &lt;li&gt;Report on resources by project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think this is a pretty significant milestone! From here we can tweak the template to match a particular project and with just one command setup the environment. If we have a client that wants a “test” and a “production” environment, all we need to do is provision the template twice.&lt;/p&gt;

&lt;h2 id=&quot;reporting-on-resources&quot;&gt;Reporting on Resources&lt;/h2&gt;

&lt;p&gt;This is a little outside the realm of CloudFormation, but I thought we’d take a short break from the template and take a look at some ways we can use the tags we have so diligently been placing on our resources. Take a look at the &lt;a href=&quot;https://console.aws.amazon.com/billing/home&quot;&gt;billing console&lt;/a&gt; for your account. You will be dropped at the dashboard and some summary data for your account will be visible.&lt;/p&gt;

&lt;p&gt;If you haven’t done so already, click on the “Cost Explorer” link from the left-hand navigation bar and enable it for your account. It’s a handy tool that lets you do some simple querying and reporting on your resources and their costs.&lt;/p&gt;

&lt;p&gt;Next click on the “Cost Allocation Tags” link, also on the left-hand navigation bar. At the top you can see that there are some cost allocation tags that AWS can generate on it’s own, go ahead and click the “Activate” button to turn those on. Lower down on the page will be a list of all of the tags that we have defined (as well as any tags you had already setup). You can choose which tags you want to make available in the Cost Explorer but my advice is just to click the top-most checkbox and make them all active. You never know when a tag will come in handy! Click the “Activate” button and confirm that, yes, you want the tags to be “active”.&lt;/p&gt;

&lt;p&gt;Now click back to the “Cost Explorer” link and click on “Launch Cost Explorer”. If you just activated the cost explorer now, you will probably have to wait until tomorrow; keep it in mind and try to come back to this section.&lt;/p&gt;

&lt;p&gt;If the Cost Explorer has data on hand then you will be presented with another dashboard attempting to summarize your costs on amazon. Click on the magnifying glass icon on the left-hand navigation bar and choose “Cost and Usage”, the report builder will appear with your last six months of spending. Click on the “Last 6 Months” pull-down and choose “1M” from the “Historical” section at the bottom, this will show your last month of spending.&lt;/p&gt;

&lt;p&gt;On the right-hand side is a list of filters and this is where the tags will come in handy. Click on the “Tag” link in the filter list, a list of your tags will appear; click on “Project” and a list of all your values for the “Project” tag will be listed. In this tutorial we’ve been putting “cf-tutorial” in the “Project” tags, check the box for “cf-tutorial” and then press the “Apply filters” button to update the report.&lt;/p&gt;

&lt;p&gt;What you are looking at now is likely a very dull report, because we’ve been using low-cost free-tier instances. But, still, what we have is a report on just the resources that belong to this project. If you were to place a “Client” tag in your templates you could report on the entire cost of a client (maybe you could use that to figure out how to bill) and you can break down a client’s costs by particular projects. It is a valuable tool and definitely worth putting some time in exploring your options.&lt;/p&gt;

&lt;h2 id=&quot;set-alarms-on-instance-usage&quot;&gt;Set Alarms on Instance Usage&lt;/h2&gt;

&lt;p&gt;I think of this as related to our reporting goals: we’d like to set some alarms that monitor the data we’re sending to CloudWatch to let us know if our servers start to go off the rails. We’ll set alarms for when CPU usage gets high or if we start to run out of disk space but I bet you can think of lots of other things you’d like to monitor.&lt;/p&gt;

&lt;p&gt;We’re going to use the &lt;a href=&quot;https://aws.amazon.com/sns/&quot;&gt;Simple Notification Service&lt;/a&gt; to alert us when an alarm is hit. The way this will work is that when CloudWatch sees an alarm trigger, it will send a message to the SNS “topic”. It will be our responsibility to log into the SNS console and add ourselves to the notification list (by email or text message), we aren’t going to populate the list of subscribers.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialAlarmTopic&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::SNS::Topic&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TopicName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialAlarms&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html&quot;&gt;Topic&lt;/a&gt; resource to create our new alarm target topic. With the SNS topic created we can now create our first alarm.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialDatabaseCPUAlarm&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AlarmDescription&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Database Server CPU Usage High&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AlarmActions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialAlarmTopic&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MetricName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;CPUUtilization&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS/EC2&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Statistic&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Average&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Period&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;EvaluationPeriods&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Threshold&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;89&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ComparisonOperator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GreaterThanThreshold&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Dimensions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;InstanceId&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialDatabaseServer&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html&quot;&gt;Alarm&lt;/a&gt; resource we create an alarm for CPU usage on the database server, if the server is using more than 89% of the CPU for more than 3 minutes then the alarm will trigger and send a message to our SNS topic.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialDatabaseDiskAlarm&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AlarmDescription&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Database Server Disk Usage High&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AlarmActions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialAlarmTopic&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MetricName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;disk_used_percent&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;CWAgent&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Statistic&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Average&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Period&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;EvaluationPeriods&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Threshold&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;89&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ComparisonOperator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GreaterThanThreshold&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Dimensions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;InstanceId&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialDatabaseServer&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ImageId&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ImageId&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;InstanceType&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;InstanceType&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;path&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;device&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${RootDevice}1&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;fstype&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;RootFsType&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This next alarm is for the amount of disk used by the database server, here we set an alarm to trigger if more than 89% of the disk is in use. This one will also wait for the disk to be in this state for 3 minutes before triggering the alarm.&lt;/p&gt;

&lt;p&gt;Provisioning this alarm is a bit more work, you can see that we had to add several &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dimensions&lt;/code&gt; to get the alarm setup. This is because when the CloudWatch agent reports this data it references all of these dimensions and we need to match them all in order to have a working alarm. If we had left one of these out then the alarm wouldn’t match any data and would never trigger.&lt;/p&gt;

&lt;p&gt;The only bit left is the backup jobs but we’ll leave that for part 3. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;-)&lt;/code&gt; Congratulations on working your way through all of this material! While it can be pretty dry reading you can really get a lot of mileage building templates for projects that you know you will be deploying over and over.&lt;/p&gt;

&lt;hr /&gt;

</content>
 </entry>
 
 <entry>
   <title>Provision Pragmatically and Programmatically with CloudFormation</title>
   <link href="http:///posts/cloudformation-tutorial-1/"/>
   <updated>2019-01-25T00:00:00-05:00</updated>
   <id>http:///posts/cloudformation-tutorial-1</id>
   <content type="html">&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/cmiles74/cloudformation-tutorial/master/template-diagram.png&quot; alt=&quot;CloudFormation Diagram&quot; title=&quot;Diagram of our CloudFormation Tutorial Stack&quot; /&gt;&lt;/p&gt;

&lt;p&gt;How many times have you gazed upon the list of services that Amazon is providing through their Amazon Web Services (AWS) family of products and felt an unpleasant mixture of anxiety and confusion? If you’re anything like me, it would be literally &lt;em&gt;every time&lt;/em&gt; you visited their website. While I was one of the first in my circle of software developer friends to get on board the Elastic Compute Cloud (EC2) train, I haven’t been great at keeping up with new developments. This year, I decided, would be the year that I get back on track and sort out this alphabet soup of cloud virtualization technology.&lt;/p&gt;

&lt;p&gt;After clicking around at random and reading an unwholesome amount of bland introductory pages, I finally started to get an idea of how I should try to put all of these pieces together. The first thing I figured out:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It is way, way too hard to get everything up and running by clicking a bunch of buttons.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What are your goals? For me, it’s to be able to deploy a web-based application for one of my clients. As a corollary, I want to be able to entirely scrap a web-based application for one of my clients (maybe it’s been replaced, maybe they’ve hired another consultant). In effect, that’s two goals. Plus I would like to keep all of my clients separate, I don’t want changes to a firewall rule to effect all of my clients, just the one client who’s willing to take the risk. If you think about it, keeping things isolated will also make it easier to figure out who we can charge for what services at the end of the month; we should also tag these resources in a way that makes sense. That’s four goals and that’s only what we’ve thought of so far. And we should snapshot on a daily basis and maybe not keep all of the snapshots. Oh, backing up the database is important, too!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Provision resources for a project&lt;/li&gt;
  &lt;li&gt;Retire resources for a project&lt;/li&gt;
  &lt;li&gt;Isolate resources for a project&lt;/li&gt;
  &lt;li&gt;Report on resources by project&lt;/li&gt;
  &lt;li&gt;Snapshot instance volumes and delete old backups on a schedule&lt;/li&gt;
  &lt;li&gt;Backup the databases on a schedule and delete old backups on a schedule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at these goals, the number of services, the way many of them build on each other… It all becomes clear that the answer is automation. Of course, Amazon provides an automation tool: &lt;a href=&quot;https://aws.amazon.com/cloudformation/&quot;&gt;CloudFormation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is a first in a series of articles that will cover all of these goals. We’ll start out with an empty file and we’ll gradually build that file up, adding more and more resources to that file as we go along. I am planning three articles all together, this on covers a brief introduction to the tools and gets as far as setting up a virtual private cloud for the project, splitting it into two networks and putting security groups in place. So exciting!&lt;/p&gt;

&lt;p&gt;I encourage you to follow along as we build up the template but you can always checkout the finished product &lt;a href=&quot;https://github.com/cmiles74/cloudformation-tutorial&quot;&gt;over at GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;install-the-aws-command-line-tools&quot;&gt;Install the AWS Command Line Tools&lt;/h2&gt;

&lt;p&gt;Amazon provides a goofy drag-and-drop interface for CloudFormation but that runs kind of counter to the whole point of this article. You can also write your CloudFormation template and then upload it via their web interface, I think that’s also a little clunky. My recommendation is that you install the &lt;a href=&quot;https://aws.amazon.com/cli/&quot;&gt;AWS Command Line Tools&lt;/a&gt; and use those tools to deploy. Amazon also provides packages for Windows, but if you’re working from within the Windows environment you might be more interested in the &lt;a href=&quot;https://aws.amazon.com/powershell/&quot;&gt;AWS Tools for PowerShell&lt;/a&gt; which do much the same thing but, you know, with &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/scripting/overview?view=powershell-6&quot;&gt;PowerShell&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is kind of a power tip, but if you’re managing more than one Amazon account or dealing with more than one region, you can &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html&quot;&gt;configure profiles&lt;/a&gt; to make that process easier.&lt;/p&gt;

&lt;p&gt;With one of these tools installed and properly configured, you can invoke CloudFormation directly from your console window! &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:-)&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;start-our-new-cloudformation-template&quot;&gt;Start Our New CloudFormation Template&lt;/h2&gt;

&lt;p&gt;In the world of CloudFormation, the document you are writing is called a “template”. I believe it’s referred to as a template because one template can be used to create many different sets of resources. For instance in this tutorial we’re going to be writing a template that will provision a web server and a database server; we can use the template to provision as many of these web-and-database-server pairs as we might like, all independent of one another.&lt;/p&gt;

&lt;p&gt;You can write your template in &lt;a href=&quot;https://yaml.org/&quot;&gt;YAML&lt;/a&gt; or &lt;a href=&quot;https://www.json.org/&quot;&gt;JSON&lt;/a&gt;, if you’re writing it by hand (as we are right now) then I &lt;em&gt;strongly&lt;/em&gt; recommend that you write it in YAML. I know, I know, YAML isn’t everyone’s favorite but getting all of the ending braces and brackets lined up in a JSON document is it’s own kind of mental torture. You deserve better, trust me: don’t do that to yourself.&lt;/p&gt;

&lt;p&gt;Anyway, we’re going to start off with a description and a parameter. A parameter, as you may have suspected, is a value that we can supply when we are actually using our template to provision something. Open up a new file in your favorite editor, type in the text below and save it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Provisions a simple cluster with a web and a database server&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;Parameters&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;KeyPairName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Name of keypair used when provisioning instances&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Throughout this article we’ll continue to build up this template but if you need to jump ahead or need to refer to the entire thing, you can &lt;a href=&quot;https://github.com/cmiles74/cloudformation-tutorial/blob/master/template.yaml&quot;&gt;browse through it in the GitHub project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some decisions are best postponed until the absolute last minute and parameters are here when you have decisions to postpone. When we decide to provision our resources, we can provide the name of the EC2 key pair to use at that time.&lt;/p&gt;

&lt;p&gt;I’m not going to go into a big discussion about how to manage your EC2 key pairs. Maybe you have one per person who has access to provision instances. Maybe you have one per client. However you manage it, you can pass it in when you provision resources.&lt;/p&gt;

&lt;p&gt;Next we’re going to add some more parameters but we’ll be providing default values for all of them. The thinking here is two-fold: first, we want the flexibility to be able to change some of the features at provisioning time. Second, we can reference these parameters throughout the template and this keeps us from sprinkling these identical values throughout the template.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;InstanceType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Instance type to provision&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;t2.micro&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ImageId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Image to use when provisioning instances&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ami-035be7bafff33b6b6&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Amazon Linux 2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;RootDevice&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Name of the root device on provisioned instances&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xvda&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# varies based on Linux type (Ubunut /dev/sda1)&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;RootFsType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;File system type for root device&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xfs&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can see that these all relate to the instances that we will be provisioning. We call out the instance type, the image we’ll use to create the instances and the name of the root volume and it’s file type. Note that the root volume and file type aren’t something we can really choose, they are dictated by the image (in this case, Amazon Linux 2).&lt;/p&gt;

&lt;h2 id=&quot;provision-a-new-virtual-private-cloud&quot;&gt;Provision a New Virtual Private Cloud&lt;/h2&gt;

&lt;p&gt;We need at least one resource in order to provision our template. For this project we’re going to create our own &lt;a href=&quot;https://aws.amazon.com/vpc/faqs/&quot;&gt;Virtual Private Cloud&lt;/a&gt; (VPC) to hold our resources. This will keep things isolated from everything else you might have deployed to AWS.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;TutorialVPC&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;                   &lt;span class=&quot;c1&quot;&gt;# name of the resource&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::VPC&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# type of resource&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# properties of the resource&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;CidrBlock&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;10.6.0.0/24&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;EnableDnsSupport&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;EnableDnsHostnames&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tutorial&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VPC&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resources&lt;/code&gt; section of the template will contain a stanza for each of the resources we provision, this will be the bulk of the template. Each stanza in this section will start with the name of the resource (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TutorialVPC&lt;/code&gt;), we can use this name when we need to reference the resource while creating other resources (for instance, when we add subnets to this VPC). After the name we need to declare the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Type&lt;/code&gt; of resource: every CloudFormation resource has a type, they are all clearly documented in the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/Welcome.html&quot;&gt;CloudFormation API Documentation&lt;/a&gt;. In the example above we are creating a new &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html&quot;&gt;VPC&lt;/a&gt; instance. Next we set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Properties&lt;/code&gt; for our resource, the values provided here will be particular to each resource. Most resources accept a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tags&lt;/code&gt; property but not all, if you set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tags&lt;/code&gt; on a resource that doesn’t support that property CloudFormation will throw an error.&lt;/p&gt;

&lt;p&gt;Every VPC needs a block of IP addresses, we set this with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CidrBlock&lt;/code&gt; property and provide &lt;a href=&quot;http://jodies.de/ipcalc?host=10.6.0.0&amp;amp;mask1=24&amp;amp;mask2=&quot;&gt;a non-routable network with 254 available addresses&lt;/a&gt;. I chose a network that won’t conflict with the address range I use at home or at work, this is important if you ever want to use &lt;a href=&quot;https://aws.amazon.com/premiumsupport/knowledge-center/create-connection-vpc/&quot;&gt;a Virtual Private Network (VPN) to connect your Amazon VPC to your own network&lt;/a&gt;, perhaps to make access to these resources easier.&lt;/p&gt;

&lt;p&gt;We’d like Amazon to continue to assign names to our instances, so we set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnableDnsSupport&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnableDnsHostnames&lt;/code&gt; properties to true. Lastly we set some tags on our resources to remind ourselves why we provisioned them in the first place.&lt;/p&gt;

&lt;h2 id=&quot;provision-resources-with-our-template&quot;&gt;Provision Resources With Our Template&lt;/h2&gt;

&lt;p&gt;With our template set, we’re ready to use it to provision some resources! When we provide the template to CloudFormation, we also have to provide our parameter values and we have the opportunity to tag &lt;em&gt;all&lt;/em&gt; of the provisioned resources (maybe by client or project or both).&lt;/p&gt;

&lt;p&gt;Go ahead and run the command below to begin provisioning. When CloudFormation sets up all of the resources it links them together into a “stack”. Once complete, you can deal with the stack as one unit. The stack name provides a human-friendly handle for all of the related resources.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;aws cloudformation create-stack &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--stack-name&lt;/span&gt; tutorial &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--template-body&lt;/span&gt; file://template.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--parameters&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ParameterKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;KeyPairName,ParameterValue&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cloudfront-tutorial &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Project,Value&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cf-tutorial
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remember to provide your own key pair name where we have used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloudfront-tutorial&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As soon as CloudFormation receives the template it will return a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StackId&lt;/code&gt; with the &lt;a href=&quot;https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html&quot;&gt;Amazon Resource Name&lt;/a&gt; (ARN) for the stack, you will have to wait a bit for the stack to finish being provisioned. You can check on your stack through the &lt;a href=&quot;https://console.aws.amazon.com/cloudformation&quot;&gt;CloudFormation web console&lt;/a&gt; and see how things are going. Some resources take longer than others, CloudFormation will ensure they are created in the right order and work out the dependencies. This template should move along pretty quickly as we aren’t provisioning much.&lt;/p&gt;

&lt;p&gt;I broke the command over several lines to try and make it easier to follow. In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create-stack&lt;/code&gt; command above we…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ask the AWS tools to call out to CloudFormation with the “create-stack” command&lt;/li&gt;
  &lt;li&gt;Set the name of the stack we are creating to “tutorial”&lt;/li&gt;
  &lt;li&gt;Provide our template to CloudFormation with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file://&lt;/code&gt; URL&lt;/li&gt;
  &lt;li&gt;We set the one template parameter, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KeyPairName&lt;/code&gt; (note the weird way we have to set parameters)&lt;/li&gt;
  &lt;li&gt;We provided a one tag key and value that CloudFormation will use to tag all of the provisioned resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may separate multiple parameters with a space, multiple tags are separated with a comma. It’s weird, I don’t know why they aren’t uniform.&lt;/p&gt;

&lt;p&gt;When provisioning is complete, you can head over to the &lt;a href=&quot;https://console.aws.amazon.com/vpc&quot;&gt;VPC web console&lt;/a&gt; to inspect your new virtual cloud. There’s not a lot to see, but if you choose “Yourq VPCs” from the left-hand navigation bar and then select your “Tutorial VPC” from the list, you can click the “Tags” tab and look at the tags associated with the resource. You’ll see the tag we specified in the template (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Name&lt;/code&gt;) and the tag we passed into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create-stack&lt;/code&gt; command (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Project&lt;/code&gt;). CloudFormation also set several tags of it’s own, linking the resource to the stack.&lt;/p&gt;

&lt;p&gt;As we work through the template we might update the stack or delete it entirely. The command to update the stack is almost exactly the same.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;aws cloudformation update-stack &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--stack-name&lt;/span&gt; tutorial &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--template-body&lt;/span&gt; file://template.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--parameters&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ParameterKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;KeyPairName,ParameterValue&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cloudfront-tutorial &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Project,Value&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cf-tutorial
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If CloudFormation runs into any problems while creating or updating your stack, it will roll back to the last valid state of the stack. If this happens during the creation of the stack you’ll end up with no resources at all, if it happens during an update then it will be as if your update attempt never occurred.&lt;/p&gt;

&lt;p&gt;Deleting the stack is quite a bit shorter.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;aws cloudformation delete-stack &lt;span class=&quot;nt&quot;&gt;--stack-name&lt;/span&gt; tutorial
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Go ahead and run that command now to delete the stack. The AWS tool command won’t return any results but if you switch to the CloudFormation console you might catch a glimpse of the stack before it’s entirely deleted. If you switch over to the VPC console you’ll see the new VPC is gone, it was deleted along with the stack.&lt;/p&gt;

&lt;p&gt;In my opinion, this is a really nice way to work. You can edit your template and work on getting all of the bits and pieces of your stack created and linked together in a way that makes sense (provisioned in the VPC, in the correct subnet, with the correct security group and policies, etc.) When you feel pretty good about your work you can go ahead and provision. If there’s a problem or something isn’t lining up the way it should you can simply delete the stack and continue to work on your template. For me, this is far more convenient then clicking through the various web consoles and trying to link everything up by filling out fields or applying actions to all of the various pieces.&lt;/p&gt;

&lt;h3 id=&quot;retaining-resources-through-stack-updates-and-even-deletion&quot;&gt;Retaining Resources through Stack Updates and Even Deletion&lt;/h3&gt;

&lt;p&gt;As much as CloudFormation can, it will try to preserve resources between updates. On the other hand there are some changes that can’t be made after provisioning: in those cases CloudFormation will delete the resource and create a replacement. For many things this doesn’t really matter, but for things like instances or Elastic IP addresses you may really &lt;em&gt;need&lt;/em&gt; to retain those resources. The &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/Welcome.html&quot;&gt;CloudFormation Documentation&lt;/a&gt; is pretty clear on what resource updates can be performed without “service interruption”, keep your eyes open for those warnings.&lt;/p&gt;

&lt;p&gt;You can also set a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeletionPolicy&lt;/code&gt; for the resources in your template. This lets you signal to CloudFormation that, for certain resources, you would like to keep the resource (even if a replacement is required for provisioning) or (if the resource supports it) perform a “snapshot” backup before deleting the resource (i.e., EC2 volumes or RDS instances). That said, even with the deletion policy set for your critical resources, you should think hard about updating them after provisioning the stack.&lt;/p&gt;

&lt;p&gt;My recommendation is that you leave the deletion policies out of your template while you are working on it. Wait until you feel pretty good about the template, maybe until the point where you’re ready to deploy your application or project. When you reach that point, go back and set the deletion policy to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Retain&lt;/code&gt; on those things that are truly critical (volumes with data on them, instances you’ve customized heavily, etc.) and then update your stack.&lt;/p&gt;

&lt;p&gt;Now that we have the basic ideas and the machinery down, we can move a little more quickly.&lt;/p&gt;

&lt;h2 id=&quot;provision-an-internet-gateway&quot;&gt;Provision an Internet Gateway&lt;/h2&gt;

&lt;p&gt;For this project We’re going to divide our virtual private cloud into two subnets: one will be able to communicate with the public internet and will house our web server. The other subnet will be able to talk to the first but will not face the internet, it will house our database server. In this way we can ensure that machines on the public internet cannot communicate directly with our database server.&lt;/p&gt;

&lt;p&gt;In order to get our external subnet facing the internet, we need to provision an &lt;a href=&quot;https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html&quot;&gt;Internet Gateway&lt;/a&gt; and then setup routes so that instances in our virtual cloud can send and receive traffic over the public internet.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialInternetGateway&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::InternetGateway&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Internet&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Gateway&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There isn’t a lot to configure for the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-internetgateway.html&quot;&gt;InternetGateway resource&lt;/a&gt;. We give it a name and add a tag. Next we need to attach it to our virtual cloud.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialVPCGatewayAttachment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::VPCGatewayAttachment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VpcId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPC&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;InternetGatewayId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialInternetGateway&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s not a lot to configuring the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc-gateway-attachment.html&quot;&gt;VPCGatewayAttachment&lt;/a&gt; either. The interesting bit is where we tell it which VPC to which we’d like it attached. We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!Ref&lt;/code&gt; function&lt;/a&gt; to indicate that we are providing a &lt;em&gt;reference&lt;/em&gt; to another resource in the template and then we provide the name of that resource.&lt;/p&gt;

&lt;h2 id=&quot;setup-our-subnets&quot;&gt;Setup Our Subnets&lt;/h2&gt;

&lt;p&gt;With that internet stuff out of the way, we can create our two subnets. First, our public subnet.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPublicSubnet&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::Subnet&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VpcId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPC&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;CidrBlock&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;10.6.0.0/25&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MapPublicIpOnLaunch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Public&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Subnet&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet.html&quot;&gt;Subnet resource&lt;/a&gt; to split off &lt;a href=&quot;http://jodies.de/ipcalc?host=10.6.0.0&amp;amp;mask1=25&amp;amp;mask2=&quot;&gt;a chunk of our VPC&lt;/a&gt; with half of our address space, we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VpcId&lt;/code&gt; property to link this subnet declaration to our VPC. We set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapPublicIpOnLaunch&lt;/code&gt; property to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; so that each instance gets a public IP when launched.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPrivateSubnet&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::Subnet&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VpcId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPC&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;CidrBlock&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;10.6.0.128/25&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MapPublicIpOnLaunch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Private&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Subnet&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the stanza above, we &lt;a href=&quot;http://jodies.de/ipcalc?host=10.6.0.128&amp;amp;mask1=25&amp;amp;mask2=&quot;&gt;take the rest of our address space&lt;/a&gt; and allocate that to our private subnet, again we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VpcId&lt;/code&gt; property to link this subnet declaration to our VPC. We don’t want instances in this network to have public IP addresses and we indicate that by setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapPublicIpOnLaunch&lt;/code&gt; property to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;setup-our-routing&quot;&gt;Setup Our Routing&lt;/h2&gt;

&lt;p&gt;We always have a default route that lets the addresses in our VPC communicate with each other but we don’t start out with any routes out to our internet gateway and the public internet. In order to setup our routes we first need to setup our routing tables.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPublicRouteTable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::RouteTable&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VpcId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPC&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Public&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Internet&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Table&quot;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;TutorialPrivateRouteTable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::RouteTable&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VpcId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPC&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Private&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Table&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s not much to the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route-table.html&quot;&gt;RouteTable resource&lt;/a&gt;, we indicate that we’re going to be managing routes for our VPC with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VpcId&lt;/code&gt; property and set a tag.&lt;/p&gt;

&lt;p&gt;Next we’re going setup the actual routes.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialInternetRoute&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;DependsOn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;VPCGatewayAttachment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;GatewayId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialInternetGateway&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RouteTableId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPublicRouteTable&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route.html&quot;&gt;Route resource&lt;/a&gt; adds a new route to a routing table. Here we add a route to our public table to route traffic for addresses on the public internet through our internet gateway. We let CloudFormation know that this resource shouldn’t be created unless the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VPCGatewayAttachment&lt;/code&gt; is already setup with the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DependsOn&lt;/code&gt; attribute&lt;/a&gt;. Like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Retain&lt;/code&gt; attribute, you can place this on any resource.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPublicSubnetPublicRoute&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RouteTableId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPublicRouteTable&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SubnetId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPublicSubnet&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With our route to the public internet setup, we can use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet-route-table-assoc.html&quot;&gt;SubnetRouteTableAssociation resource&lt;/a&gt; to link our routing table to our public subnet.&lt;/p&gt;

&lt;p&gt;Next we need to do something similar for our private subnet. The big difference here is that our private subnet doesn’t connect directly to the internet. Instead we will use a &lt;a href=&quot;https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html&quot;&gt;Network Address Translation (NAT) gateway&lt;/a&gt; to let instances on our private subnet connect to the internet without allowing machines on the public internet to initiate connections with instances in our private subnet.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialNatGatewaySubnetRouteTable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::RouteTable&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VpcId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPC&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;NAT&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Gateway&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Subnet&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Table&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We create a new routing table and associate it to our VPC.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialNatGatewayInternetRoute&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;DependsOn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPCGatewayAttachment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;GatewayId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialInternetGateway&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RouteTableId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialNatGatewaySubnetRouteTable&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then we create a route to the public internet through our internet gateway.&lt;/p&gt;

&lt;p&gt;The next step is to create our NAT gateway, but first we need to get a publicly accessible IP address that will be assigned to our NAT gateway. We can use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html&quot;&gt;EIP (Elastic IP) Resource&lt;/a&gt; to get a handle on a new Elastic IP address.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialNatGatewayElasticIP&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::EIP&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Domain&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vpc&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domain&lt;/code&gt; value to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vpc&lt;/code&gt;, indicating that we want to use this address from our VPC. Kind of funny, that’s the only valid value for that property.&lt;/p&gt;

&lt;p&gt;Now we can setup our NAT gateway…&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialNatGateway&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::NatGateway&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;DependsOn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;VPCGatewayAttachment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AllocationId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Sub&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${TutorialNatGatewayElasticIP.AllocationId}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SubnetId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPublicSubnet&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;NAT&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Gateway&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-natgateway.html&quot;&gt;NatGateway resource&lt;/a&gt; to create our new NAT gateway and indicate that we need our VPCGatewayAttachment resource provisioned before this is created. We set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SubnetId&lt;/code&gt; property to put it in our public subnet so that it can communicate with the internet.&lt;/p&gt;

&lt;p&gt;While it would make a lot of sense to provide the Elastic IP we provisioned directly to the gateway, we instead need to provide the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllocationId&lt;/code&gt; associated with that Elastic IP instead. Here we use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!Sub&lt;/code&gt;&lt;/a&gt; function to get the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllocationId&lt;/code&gt; of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NatGatewayElasticIP&lt;/code&gt; and provide that value to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllocationId&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Now we can setup our route for the private subnet to get to the internet via our NAT gateway.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPrivateSubnetNatRoute&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;DependsOn&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialNatGateway&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;NatGatewayId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialNatGateway&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RouteTableId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPrivateRouteTable&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We create our route, using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DependsOn&lt;/code&gt; attribute to indicate that we need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NatGateway&lt;/code&gt; provisioned before this route is created. Then we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NatGateway&lt;/code&gt; property to link in our gateway and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RouteTableId&lt;/code&gt; to tie the route to our routing table.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPublicSubnetRouteAssoc&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RouteTableId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPublicRouteTable&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SubnetId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPublicSubnet&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;TutorialPrivateSubnetRouteAssoc&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RouteTableId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPrivateRouteTable&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SubnetId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialPrivateSubnet&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The final step is to associate our routing table with our subnets! We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet-route-table-assoc.html&quot;&gt;SubnetRouteTableAssociation&lt;/a&gt; to attach our table of routes to the matching subnet.&lt;/p&gt;

&lt;h2 id=&quot;setup-our-security-groups&quot;&gt;Setup Our Security Groups&lt;/h2&gt;

&lt;p&gt;Before we can provision our instances we need to get our &lt;a href=&quot;https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html&quot;&gt;security groups&lt;/a&gt; set. We will have one for each subnet and these groups will specify what traffic we will allow in and out of each of our subnets. We will allow web traffic in and out of our public subnets, in our private subnet we’re only going to allow web traffic out and nothing inbound.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPublicSecurityGroup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::SecurityGroup&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Web&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Group&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VpcId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPC&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;GroupDescription&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Web Server Security Group&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SecurityGroupIngress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# Public Internet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tcp&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FromPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;# HTTP &lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ToPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# Public Internet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tcp&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FromPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;# HTTPS&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ToPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# Replace with your IP Range&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tcp&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FromPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;22&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;# SSH&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ToPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;22&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.6.0.0/24&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# Inside the VPC&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-1&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# All traffic&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group.html&quot;&gt;SecurityGroup resource&lt;/a&gt; to create our new group for the public subnet. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Description&lt;/code&gt; we provide is visible in the web console and then we move on to the properties. We use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VpcId&lt;/code&gt; property to indicate that this group is for our VPC and then provide another, very similar description, for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupDescription&lt;/code&gt; (this property is required).&lt;/p&gt;

&lt;p&gt;With that out of the way, we move on to the in-bound (ingress) rules. Each rule is provided by a &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group-rule.html&quot;&gt;Security Group Rule property&lt;/a&gt; that is made up of an IP address range, the protocol and then a range of ports. We have created rules for HTTP, HTTPS, and SSH and traffic inside of our VPC. If you were provisioning a Windows instance, don’t forget to add port 3389 for Remote Desktop access.&lt;/p&gt;

&lt;p&gt;In the example above we have opened up the SSH port to the entire internet: this is not a great idea. Since these ports are used almost exclusively for management of the instances, a better choice would be to open these ports up to the address range that is used by your office. If you don’t have a fixed range of addresses you can use then I encourage you to look into other solutions, perhaps using a VPN.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;      &lt;span class=&quot;na&quot;&gt;SecurityGroupEgress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# Public Internet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tcp&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FromPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;# HTTP&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ToPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# Public Internet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tcp&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FromPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;# HTTPS&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ToPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# Public Internet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tcp&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FromPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;587&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;# SSL SMTP&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ToPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;587&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.6.0.0/24&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# Inside the VPC&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-1&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# All traffic&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Public&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Subnet&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we setup the rules for outbound traffic: HTTP, HTTPS, and SMTP over SSL. We also have a rule that allows all traffic inside our VPC. If you are more security conscious, you can nail down specific types of traffic inside your VPC (i.e., just web, SSH and database traffic).&lt;/p&gt;

&lt;p&gt;The security group for our private subnet is much simpler.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialPrivateSecurityGroup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::EC2::SecurityGroup&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Private&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Group&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VpcId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!Ref&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TutorialVPC&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;GroupDescription&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Private Subnet Security Group&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SecurityGroupIngress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.6.0.0/24&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# Inside the VPC&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-1&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# All traffic&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SecurityGroupEgress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.6.0.0/24&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# Inside the VPC&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-1&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# All traffic&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# Public Internet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tcp&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FromPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;80&apos;&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# HTTP&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ToPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;80&apos;&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CidrIp&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# Public Internet&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IpProtocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tcp&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;FromPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;443&apos;&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# HTTPS&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ToPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;443&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Private&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Subnet&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We are only allowing incoming traffic from inside our VPC into the private subnet. Outbound, we allow all traffic destined for other addresses in our VPC and we’re allowing only HTTP and HTTPS to the public internet. Traffic from addresses in our private subnet is already being routed through our NAT gateway, we’re likely only going to use the internet to download software updates.&lt;/p&gt;

&lt;h2 id=&quot;provision-other-resources&quot;&gt;Provision Other Resources&lt;/h2&gt;

&lt;p&gt;There are a lot of other resource that we might provision, aside from instances. You might need queues, notification topics, etc. For this project we’re going to setup an S3 bucket to hold dump files from our database server.&lt;/p&gt;

&lt;p&gt;The nice thing about setting these up before the instances is that we can tell the instances about these resources and set policies so that our instances can access these resources without providing them with a specific set of credentials.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;na&quot;&gt;TutorialBackupS3Bucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::S3::Bucket&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AccessControl&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Private&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Name&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backup&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Bucket&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Simple enough! We use the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html&quot;&gt;Bucket resource&lt;/a&gt; to create our bucket and we set that bucket to be private with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccessControl&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;It was a long walk but we have accomplished a lot. Go ahead and provision your template and wait for CloudFormation to finish getting everything setup. From the CloudFormation web console, select your stack and click on the “Resources” tab to see all of the items that you have provisioned. Such progress!&lt;/p&gt;

&lt;h2 id=&quot;until-next-time&quot;&gt;Until Next Time&lt;/h2&gt;

&lt;p&gt;That is all we are going to cover in this article, we went over a lot of material. In the next piece in the series we’ll setup some groups and roles and actually provision some instances.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

&lt;hr /&gt;
</content>
 </entry>
 
 <entry>
   <title>Linux Is All About Choices</title>
   <link href="http:///posts/linux/"/>
   <updated>2014-07-10T00:00:00-04:00</updated>
   <id>http:///posts/linux</id>
   <content type="html">&lt;p&gt;
Downloading the latest Ubuntu release and installing it on your laptop
or in a virtual machine is easy, but maybe it’s &lt;i&gt;too&lt;/i&gt; easy. It’s too
easy to blindly accept the choices that the distribution has made for
us. Maybe it’s also too &lt;i&gt;hard&lt;/i&gt;, there’s little room left for us to
customize the environment or mold it to our own preferences and
habits.
&lt;/p&gt;

&lt;div id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-1&quot;&gt;It’s Too Easy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2&quot;&gt;Maybe We Don’t Need the Magic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3&quot;&gt;What Choices Didn’t We Make?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-1&quot;&gt;How Does This Thing Really Work?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-2&quot;&gt;All That Fancy GUI Stuff&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4&quot;&gt;Challenge Yourself&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5&quot;&gt;What Do I Use?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;It’s Too Easy&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;


&lt;p&gt;
I understand that there are people who come from another environment,
like Microsoft’s Windows or Apple’s OSX, who want something that
doesn’t challenge them. They want to send an e-mail or write a paper
or add some numbers and the rest is kind of like icing on the cake. I
can see how a distribution like Ubuntu would appeal to them and I
agree that it’s a good thing.
&lt;/p&gt;
&lt;p&gt;
But what about those of us who spend most of our workday (and some of
our non-workday) in front of a computer? For many of us an all-in-one
distribution is too easy, we forget that we can make the environment
our own.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-2&quot;&gt;Maybe We Don’t Need the Magic&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-2&quot;&gt;


&lt;p&gt;
Friends and family who use Apple products love them and enjoy talking
about them, once of the things I hear about most often is “the magic.”
How it knows how to do the right thing when they plug in their backup
drive or dock their phone. How a new phone contact magically turns
into a new e-mail contact. For sure this stuff is great, but what if
you want to do it a little different? That’s when the magic stops
being so magical and starts to become frustrating.
&lt;/p&gt;
&lt;p&gt;
Over on the Linux side we don’t need to be so magical. It’s really
nice when the computer does the thing you want all on it’s own, as if
it can read your mind. But unlike Apple there’s no need to hide the
mechanisms that make the magic happen. Most of the time these
functions can be easily customized if we happen to know what does what
when. Unfortunately many distributions are working hard to hide these
mechanisms and make them hard to manipulate. I think &lt;a href=&quot;https://www.gnome.org/&quot;&gt;Gnome Shell&lt;/a&gt; is a
good example of this; people new to Linux love it while those of us
who are more demanding are frustrated more and more with every
release.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-3&quot;&gt;What Choices Didn’t We Make?&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-3&quot;&gt;


&lt;p&gt;
Installing a distribution like Ubuntu is super easy, you boot from the
image and the graphical installer asks you where you live, what time
it is, and you pick a name and password. It’s painless, for sure, but
it’s also letting the (well intentioned) folks who assembled that
distribution make a lot of decisions on our behalf. Decisions that we
often didn’t even know where choices at all.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-1&quot;&gt;How Does This Thing Really Work?&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-1&quot;&gt;


&lt;p&gt;
I’ve been using Linux for a good long while now and there were some
frustrating things that I simply took for granted because I didn’t
understand how they really worked. For sure, most of that was laziness
but the distribution was at least partly to blame for hiding all of
the machinery. Hiding it, I suppose, so that I wouldn’t grow confused
by all of the choices.
&lt;/p&gt;
&lt;p&gt;
I have a docking station at work with two displays and another at home
with just the one display. For many moons I would grumble when docking
or un-docking and re-arranging the displays. It wasn’t until pretty
recently that I figured out I could write a simple &lt;a href=&quot;https://wiki.archlinux.org/index.php/Udev&quot;&gt;udev rule&lt;/a&gt; that
triggers every time the laptop plugs into or out of a dock. Combine
that with &lt;a href=&quot;https://github.com/phillipberndt/autorandr&quot;&gt;autorandr&lt;/a&gt; configurations for the displays and now I don’t
have to fuss with these settings at all. It just works and most
importantly: &lt;i&gt;not by magic!&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
This is an example from my own experience and I have more. The same
mechanism let’s you write a rule that, maybe, kicks off a backup when
you plug in a particular USB drive. Distribution packagers aren’t
trying to hide anything but it’s often the case that you just don’t
know what you don’t know.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-2&quot;&gt;All That Fancy GUI Stuff&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-2&quot;&gt;


&lt;p&gt;
Probably the choices we most miss out on are the ones that involve our
desktop environment. Many distributions have already chosen the entire
desktop stack for us and it’s often difficult to move away to
something else. There are several Ubuntu flavors that are bundled with
different desktop environments but they work much the same way. Part
of the problem is the way these components overlap and another is that
it’s a big change and no one wants to “mess it up.” But making it
difficult to switch from one desktop environment to another isn’t
helping anyone.
&lt;/p&gt;
&lt;p&gt;
I’m a strong believer in the &lt;a href=&quot;https://wiki.archlinux.org/index.php/Comparison_of_Tiling_Window_Managers&quot;&gt;tiling window manager&lt;/a&gt; and there are &lt;i&gt;a lot&lt;/i&gt; of them out there, many of them are easy to customize for your
own work environment. Ubunutu’s Unity environment is great (or
tolerable), to be sure, but it’s not so easy to change the way it
works. Instead of molding the environment to fit ourselves, more often
than not we adjust the way we work instead.
&lt;/p&gt;
&lt;p&gt;
I’ve been using the &lt;a href=&quot;http://i3wm.org/&quot;&gt;i3 window manager&lt;/a&gt; for over a year now and I really
like it. The things that maybe didn’t work exactly right for me I went
ahead and changed so that they did. I wanted to change the items in
the status bar and it was so straightforward that I ended up &lt;a href=&quot;https://github.com/cmiles74/i3status&quot;&gt;writing my own&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
And there are more options out there. &lt;a href=&quot;http://awesome.naquadah.org/&quot;&gt;Awesome&lt;/a&gt; let’s you customize the
window manager with &lt;a href=&quot;https://en.wikipedia.org/wiki/Lua_(programming_language)&quot;&gt;Lua&lt;/a&gt;, &lt;a href=&quot;http://xmonad.org/&quot;&gt;xmonad&lt;/a&gt; let’s you customize it’s behavior with
&lt;a href=&quot;http://haskell-lang.org/&quot;&gt;Haskell&lt;/a&gt;. And so on, the point being that not every tool is buying into
this idea that letting people customize their environment is somehow a
bad idea.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-4&quot;&gt;Challenge Yourself&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-4&quot;&gt;


&lt;p&gt;
I think we would all do better to challenge ourselves, to make things
a little harder. Instead of choosing a Linux distribution on the basis
of how easy it is to install, instead pick one that lets &lt;i&gt;you&lt;/i&gt; make
choices at every step. Choices like&amp;hellip;.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you want to encrypt any of your disk partitions?
&lt;/li&gt;
&lt;li&gt;Which bootloader would you like to use?
&lt;/li&gt;
&lt;li&gt;Which display driver do you &lt;i&gt;really&lt;/i&gt; want to use?
&lt;/li&gt;
&lt;li&gt;Which display manager would you like to use?
&lt;/li&gt;
&lt;li&gt;Would you like a desktop environment or just a window manager?
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
Yes, I’m recommending &lt;a href=&quot;https://wiki.archlinux.org/index.php/Beginners&apos;_guide#Post-installation&quot;&gt;Archlinux&lt;/a&gt;. In my humble opinion, you’ll learn a
much more about how everything works together just by spending a
little time on these questions. They are all worth asking and many of
them will impact you every time you sit down with your machine. Making
these kinds of choices might seem like minutia at first they build on
one another. At the end of the day, you might be surprised how good
you can have it.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-5&quot;&gt;What Do I Use?&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-5&quot;&gt;


&lt;p&gt;
On the off change that you’re curious, this is what I use daily on my
laptop. Sure, it’s a little ugly but it’s mine.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.archlinux.org/&quot;&gt;ArchLinux&lt;/a&gt; is my distribution
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.archlinux.org/index.php/GDM&quot;&gt;Gnome Display Manager&lt;/a&gt;, because pretty
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://i3wm.org/&quot;&gt;i3 Window Manager&lt;/a&gt;, I like keeping my hands on the keyboard
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://build.i3wm.org/docs/i3-dmenu-desktop.html&quot;&gt;i3 dmenu desktop&lt;/a&gt; for launching applications
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Compton&quot;&gt;Compton&lt;/a&gt; for window compositing
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.knopwob.org/dunst/&quot;&gt;Dunst&lt;/a&gt; for notifications
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cmiles74/i3status&quot;&gt;My Own Custom i3 Status Bar&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/phillipberndt/autorandr&quot;&gt;Autorandr&lt;/a&gt; to keep my displays sorted
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Guake/guake&quot;&gt;Guake&lt;/a&gt; and &lt;a href=&quot;https://github.com/thestinger/termite/&quot;&gt;Termite&lt;/a&gt; for the command line
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://fishshell.com/&quot;&gt;fish, the friendly interactive shell&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://portix.bitbucket.org/dwb/&quot;&gt;dwb&lt;/a&gt; is my primary web browser
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/emacs/&quot;&gt;Emacs&lt;/a&gt; and &lt;a href=&quot;http://www.jetbrains.com/idea/&quot;&gt;IntelliJ IDEA&lt;/a&gt; for editing code
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://notmuchmail.org/&quot;&gt;Notmuch&lt;/a&gt; for mail management with the &lt;a href=&quot;http://notmuchmail.org/emacstips/&quot;&gt;Emacs client&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nongnu.org/ranger/&quot;&gt;Ranger&lt;/a&gt; for managing my files
&lt;/li&gt;
&lt;/ul&gt;




&lt;a href=&quot;/images/desktop-screenshot.png&quot; rel=&quot;lightbox&quot;
  title=&quot;Miles’ Desktop&quot;&gt;
  &lt;img src=&quot;/images/desktop-screenshot.png&quot;
    style=&quot;width: 630px;&quot;/&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Real Life Web App Integration Testing (IT) with Spring</title>
   <link href="http:///posts/spring-integration-testion/"/>
   <updated>2013-03-30T00:00:00-04:00</updated>
   <id>http:///posts/spring-integration-testion</id>
   <content type="html">&lt;p&gt;
I know what you’re thinking: “what constitutes ‘real life’ integration
testing (IT)?” In my opinion, “real life” means “complicated web
application” or maybe “web app that is stuck to creaky database not
entirely under my control” or “web app that uses auditors to enforce
security” or, probably most inclusively “web app that leverages &lt;i&gt;a lot&lt;/i&gt; of Spring”.
&lt;/p&gt;
&lt;p&gt;
I&apos;m sure you know what I mean: the kind of application they pay you to
work on and, clearly, not something you designed from scratch to be
relatively clean and modular. Not your own pet project.
&lt;/p&gt;

&lt;div id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-1&quot;&gt;Doesn’t Spring Have Awesome IT Support Already?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2&quot;&gt;Our Test Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3&quot;&gt;The POM.XML File&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-1&quot;&gt;Dependencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-2&quot;&gt;The Jetty Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-3&quot;&gt;Tell Surefire to Skip Our IT Tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-4&quot;&gt;Tell Failsafe to Run Our IT Tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4&quot;&gt;The Test&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4-1&quot;&gt;Instantiate a New PhantomJS Before Each Test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4-2&quot;&gt;The Simple Case: Ensure the Page Renders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4-3&quot;&gt;The Tricky Case: Fill Out a Modal With a Form&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5&quot;&gt;The Idealized Case: You Just Saw It and Your App Isn’t It&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-1&quot;&gt;Access to the Spring Container&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-2&quot;&gt;Transactions That Persist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-3&quot;&gt;Where Are My Properties?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-4&quot;&gt;Two Incompatible Beans?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-6&quot;&gt;Your Mileage May Vary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Doesn’t Spring Have Awesome IT Support Already?&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;


&lt;p&gt;
Spring does have &lt;a href=&quot;http://static.springsource.org/spring/docs/3.0.0.M3/reference/html/ch10s03.html&quot;&gt;some good IT tools in place already&lt;/a&gt;, things like the
&lt;code&gt;TestContext&lt;/code&gt; framework and some special transaction management that
will roll back any database changes that your test may create. It
works pretty well but not for all cases; it really works best when
you’ve planned for it &lt;i&gt;ahead of time&lt;/i&gt; and often not so well if you’re
only thinking about it after a good size of the code base is in place.
&lt;/p&gt;


&lt;div class=&quot;pullquote&quot;/&gt;
&lt;p&gt;
I think there are some positive points to the Spring approach but I’d
argue that when your doing your integration testing, it’s kind of an
oxymoron to test your components in isolation.
&lt;/div&gt;

You’ll also run into problems if your doing in-browser testing and
your application is at all complicated. With your server running in
one context and your test in another, the test transaction manager is
going to frustrate you by keeping your changes invisible to your
server. If you want fixture data, you can use something like &lt;a href=&quot;http://www.dbunit.org/&quot;&gt;DbUnit&lt;/a&gt;,
but if your test is too complex it can be problematic to keep
everyone’s test data in sync. If your team is working with one central
test database this isn’t a viable solution at all.
&lt;/p&gt;
&lt;p&gt;
These are the issues I’m going to attempt to address. I think there
are some positive points to the Spring approach but I’d argue that
when your doing your integration testing, it’s kind of an oxymoron to
test your components in isolation.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-2&quot;&gt;Our Test Project&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-2&quot;&gt;


&lt;p&gt;
Our test project will be simple but we’ll pull in a good portion of
Spring’s features. We’ll use &lt;a href=&quot;http://www.springsource.org/spring-data&quot;&gt;Spring Data with JPA&lt;/a&gt; for persistence and
&lt;a href=&quot;http://wicket.apache.org/&quot;&gt;Apache Wicket&lt;/a&gt; to provide our web application. The application itself
is pretty simple, it provides a simple to-do list and it lets you add
&quot;contexts&quot; (the GTD-ism) as well as add and remove to-do items. You
can take a look at the test project &lt;a href=&quot;https://github.com/cmiles74/wicket-todos&quot;&gt;over on GitHub&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
We’ll use &lt;a href=&quot;http://docs.seleniumhq.org/&quot;&gt;Selenium&lt;/a&gt; with &lt;a href=&quot;https://github.com/detro/ghostdriver&quot;&gt;Ghostdriver&lt;/a&gt; and &lt;a href=&quot;http://phantomjs.org/&quot;&gt;PhantomJS&lt;/a&gt; for our integration
testing. Selenium will provide a nice Java API for managing the web
browser and PhantomJS gives us a fast, headless browser. In my
opinion, if you would also like to test real browsers you’re better
off making your continuous integration environment perform those
tests. You don’t want your developers bogged down by a lengthy and
time consuming integration test phase; PhantomJS lets them run the IT
tests as fast as possible.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-3&quot;&gt;The POM.XML File&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-3&quot;&gt;


&lt;p&gt;
As you might have guessed there’s a lot of good stuff setup in this
project’s &lt;a href=&quot;https://github.com/cmiles74/wicket-todos/blob/master/pom.xml&quot;&gt;POM file&lt;/a&gt;. We’ll go over the parts relevant to IT testing to
make sure you don’t miss anything.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-1&quot;&gt;Dependencies&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-1&quot;&gt;


&lt;p&gt;
We need to add JUnit, Selenium and Ghostdriver dependencies to our
project. This is pretty straightforward.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;&amp;lt;dependencies&amp;gt;
  ...
  &amp;lt;!-- Testing --&amp;gt;
  &amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;4.10&amp;lt;/version&amp;gt;
  &amp;lt;/dependency&amp;gt;
  &amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.seleniumhq.selenium&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;selenium-java&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.31.0&amp;lt;/version&amp;gt;
  &amp;lt;/dependency&amp;gt;
  &amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.github.detro.ghostdriver&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;phantomjsdriver&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0.3-dev&amp;lt;/version&amp;gt;
&amp;lt;/dependencies&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
We pull in JUnit, Selenium and Ghostdriver (the PhantomJS driver for
Selenium). You’ll need to have PhantomJS installed already, as long as
it’s in your current path you won’t need to do any configuration at all.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-1-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-3-1-1&quot;&gt;A Note About PhantomJS&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-3-1-1&quot;&gt;


&lt;p&gt;
As of this writing, there is a &lt;a href=&quot;https://github.com/detro/ghostdriver/issues/194&quot;&gt;known bug&lt;/a&gt; in the version of Ghostdriver
bundled with PhantomJS 1.9.0; it has some issues switching over to an
Iframe of content. Our test project uses Iframes for modal pop-up
windows so you’ll want to build your own PhantomJS with the newest
version of Ghostdriver, it’s all clearly documented on the &lt;a href=&quot;https://github.com/detro/ghostdriver&quot;&gt;Ghostdriver project page&lt;/a&gt;. If you’re on 64-bit Windows you can also use the &lt;a href=&quot;http://dl.dropbox.com/u/2132225/phantomjs-1.9.0-dev+ghostdriver-dev/phantomjs-amd64-windows.zip&quot;&gt;one that I compiled&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-2&quot;&gt;The Jetty Plugin&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-2&quot;&gt;


&lt;p&gt;
When we run our integration tests we need Jetty to startup and load in
our application. It needs to stay running while our tests run and it
needs to cleanly shutdown when our tests are over. The &lt;a href=&quot;http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin&quot;&gt;Jetty Maven Plugin&lt;/a&gt; makes it easy to do just this. First, we set some properties up
to let Jetty know what ports to use.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;&amp;lt;properties&amp;gt;
  &amp;lt;jetty.stopKey&amp;gt;stopJetty&amp;lt;/jetty.stopKey&amp;gt;
  &amp;lt;jetty.stopPort&amp;gt;8081&amp;lt;/jetty.stopPort&amp;gt;
  &amp;lt;jetty.port&amp;gt;8080&amp;lt;/jetty.port&amp;gt;
 &amp;lt;/properties&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
Here we tell Jetty what port to listen to for stop requests and which
port it should use to provide our application.
&lt;/p&gt;
&lt;p&gt;
Next we setup the plugin itself.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;&amp;lt;build&amp;gt;
  ...
  &amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;
     &amp;lt;artifactId&amp;gt;jetty-maven-plugin&amp;lt;/artifactId&amp;gt;
     &amp;lt;version&amp;gt;8.1.8.v20121106&amp;lt;/version&amp;gt;
     &amp;lt;configuration&amp;gt;
       &amp;lt;webApp&amp;gt;
         &amp;lt;contextPath&amp;gt;/&amp;lt;/contextPath&amp;gt;
         &amp;lt;descriptor&amp;gt;src/main/webapp/web.xml&amp;lt;/descriptor&amp;gt;
         &amp;lt;resourceBases&amp;gt;
           &amp;lt;dir&amp;gt;src/main/webapp&amp;lt;/dir&amp;gt;
           &amp;lt;dir&amp;gt;src/test/webapp&amp;lt;/dir&amp;gt;
         &amp;lt;/resourceBases&amp;gt;
      &amp;lt;/webApp&amp;gt;
    &amp;lt;/configuration&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
In the stanza above, we add the plugin to our project and tell it
where to find our web application&amp;hellip;
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;    &amp;lt;executions&amp;gt;
      &amp;lt;execution&amp;gt;
        &amp;lt;id&amp;gt;start-jetty&amp;lt;/id&amp;gt;
        &amp;lt;phase&amp;gt;pre-integration-test&amp;lt;/phase&amp;gt;
        &amp;lt;goals&amp;gt;
          &amp;lt;goal&amp;gt;run&amp;lt;/goal&amp;gt;
        &amp;lt;/goals&amp;gt;
        &amp;lt;configuration&amp;gt;
          &amp;lt;daemon&amp;gt;true&amp;lt;/daemon&amp;gt;
          &amp;lt;stopKey&amp;gt;${jetty.stopKey}&amp;lt;/stopKey&amp;gt;
          &amp;lt;stopPort&amp;gt;${jetty.stopPort}&amp;lt;/stopPort&amp;gt;
        &amp;lt;/configuration&amp;gt;
      &amp;lt;/execution&amp;gt;
      &amp;lt;execution&amp;gt;
        &amp;lt;id&amp;gt;stop-jetty&amp;lt;/id&amp;gt;
        &amp;lt;phase&amp;gt;post-integration-test&amp;lt;/phase&amp;gt;
        &amp;lt;goals&amp;gt;
          &amp;lt;goal&amp;gt;stop&amp;lt;/goal&amp;gt;
        &amp;lt;/goals&amp;gt;
        &amp;lt;configuration&amp;gt;
          &amp;lt;stopKey&amp;gt;${jetty.stopKey}&amp;lt;/stopKey&amp;gt;
          &amp;lt;stopPort&amp;gt;${jetty.stopPort}&amp;lt;/stopPort&amp;gt;
        &amp;lt;/configuration&amp;gt;
      &amp;lt;/execution&amp;gt;
    &amp;lt;/executions&amp;gt;
  &amp;lt;/plugin&amp;gt;
&amp;lt;/build&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
Then we set Jetty to run right before our integration tests and to
stop when the tests are complete. In this setup, Jetty will run in
“daemon” mode so that Maven can move on and run our tests.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-3&quot;&gt;Tell Surefire to Skip Our IT Tests&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-3&quot;&gt;


&lt;p&gt;
We don’t want the regular Surefire plugin to run our tests during the
regular “test” phase, we need to explicitly tell Surefire to skip
them. This also goes in the “build” section of our POM.xml file.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;&amp;lt;plugin&amp;gt;
  &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;maven-surefire-plugin&amp;lt;/artifactId&amp;gt;
  &amp;lt;configuration&amp;gt;
    &amp;lt;excludes&amp;gt;
      &amp;lt;exclude&amp;gt;**/integration/**/*.*&amp;lt;/exclude&amp;gt;
    &amp;lt;/excludes&amp;gt;
  &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;


&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-4&quot;&gt;Tell Failsafe to Run Our IT Tests&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-4&quot;&gt;


&lt;p&gt;
The Failsafe plugin will actually run our tests. This plugin is very
similar to Surefire, it justs runs the test at “integration test” time
(the “verify” phase”) instead of during the “test” phase.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;&amp;lt;plugin&amp;gt;
  &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;maven-failsafe-plugin&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;2.12.4&amp;lt;/version&amp;gt;
  &amp;lt;executions&amp;gt;
      &amp;lt;execution&amp;gt;
          &amp;lt;id&amp;gt;integration-tests&amp;lt;/id&amp;gt;
          &amp;lt;goals&amp;gt;
              &amp;lt;goal&amp;gt;integration-test&amp;lt;/goal&amp;gt;
          &amp;lt;/goals&amp;gt;
          &amp;lt;phase&amp;gt;integration-test&amp;lt;/phase&amp;gt;
          &amp;lt;configuration&amp;gt;
              &amp;lt;includes&amp;gt;
                  &amp;lt;include&amp;gt;**/integration/**/*.*&amp;lt;/include&amp;gt;
              &amp;lt;/includes&amp;gt;
          &amp;lt;/configuration&amp;gt;
      &amp;lt;/execution&amp;gt;
      &amp;lt;execution&amp;gt;
          &amp;lt;id&amp;gt;verify&amp;lt;/id&amp;gt;
          &amp;lt;goals&amp;gt;
              &amp;lt;goal&amp;gt;verify&amp;lt;/goal&amp;gt;
          &amp;lt;/goals&amp;gt;
      &amp;lt;/execution&amp;gt;
  &amp;lt;/executions&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
There’s not too much happening here, we setup a new “goal” called
“integration-test” and we set this goal to target the “verify”
phase. If any of our IT tests fail, Failsafe will ensure that the
entire build fails as well.
&lt;/p&gt;
&lt;p&gt;
Phew! That’s a lot of stuff.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-4&quot;&gt;The Test&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-4&quot;&gt;


&lt;p&gt;
With all of the infrastructure in place, we’re ready to write up our
first IT test. I’ll go through the important bits next, you can look
at the whole test &lt;a href=&quot;https://github.com/cmiles74/wicket-todos/blob/master/src/test/java/com/windsorsolutions/todos/integration/service/ToDoHomePageTest.java&quot;&gt;over here&lt;/a&gt;.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-4-1&quot;&gt;Instantiate a New PhantomJS Before Each Test&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-4-1&quot;&gt;


&lt;p&gt;
Creating a new PhantomJS instance is pretty cheap so we’ll create a
new one before each test is run.
&lt;/p&gt;



&lt;pre class=&quot;src src-java&quot;&gt;&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;WebDriver&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;driver&lt;/span&gt; = &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;null&lt;/span&gt;;

&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Before&lt;/span&gt;
&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;void&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;setUp&lt;/span&gt;() {

  &lt;span style=&quot;color: #b58900;&quot;&gt;DesiredCapabilities&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;desiredCapabilities&lt;/span&gt; = &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;DesiredCapabilities&lt;/span&gt;();
  driver = &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;PhantomJSDriver&lt;/span&gt;(desiredCapabilities);
  driver.manage().window().setSize(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Dimension&lt;/span&gt;(800, 600));
  driver.manage().timeouts().implicitlyWait(30, &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;TimeUnit&lt;/span&gt;.SECONDS);
}
&lt;/pre&gt;


&lt;p&gt;
Note that we set the size of the browser window, if you don’t
explicitly set this it will be far too small.
&lt;/p&gt;
&lt;p&gt;
Also interesting is how Selenium finds elements on the page. Whenever
you tell it to find something on the page, it will scan through the
DOM of the page until it finds the element you want and it will then
return the found element or “null”. If you tell Selenium to
“implicitly wait”, it will scan the page &lt;i&gt;continuously&lt;/i&gt; for the
element until it either finds it or the wait time is exceeded. This is
a great fit for pages that have dynamic content, Selenium will scan
the page until the element appears.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-4-2&quot;&gt;The Simple Case: Ensure the Page Renders&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-4-2&quot;&gt;


&lt;p&gt;
Here’s a very simple test that loads in a page and checks for a couple
items of content.
&lt;/p&gt;



&lt;pre class=&quot;src src-java&quot;&gt;&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Test&lt;/span&gt;
&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;void&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;testHomePageRenders&lt;/span&gt;() {

  driver.get(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;http://localhost:8080/&quot;&lt;/span&gt;);

  Assert.assertNotSame(driver.findElements(By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;title&quot;&lt;/span&gt;)).size(), 0);
  Assert.assertNotSame(driver.findElements(By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;addTodoLink&quot;&lt;/span&gt;)).size(),
    0);
  Assert.assertNotSame(driver.findElements(By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;addContextLink&quot;&lt;/span&gt;))
    .size(), 0);
}
&lt;/pre&gt;


&lt;p&gt;
Pretty simple! Selenium loads the page and then searches for some
elements. We then check to see that the number of returned elements is
not zero.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-4-3&quot;&gt;The Tricky Case: Fill Out a Modal With a Form&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-4-3&quot;&gt;


&lt;p&gt;
Now the tricky one, we want Selenium to load the home page and click
on the button that creates a new “context”. When the modal pop-up
window appears we’d like the form filled out with some test data and
then have Selenium click on the form’s “save” button. Lastly we’d like
Selenium to make sure that the new context appears on the home
page. These are all AJAX requests, our to-do application is a single
page application.
&lt;/p&gt;



&lt;pre class=&quot;src src-java&quot;&gt;&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Test&lt;/span&gt;
&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;void&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;textAddContext&lt;/span&gt;() {

  driver.get(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;http://localhost:8080/&quot;&lt;/span&gt;);

  &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;get all of the current context identifiers&lt;/span&gt;
  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;final&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;List&lt;/span&gt;&amp;lt;String&amp;gt; contextIdList = &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;ArrayList&lt;/span&gt;&amp;lt;String&amp;gt;();
  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;for&lt;/span&gt;(&lt;span style=&quot;color: #b58900;&quot;&gt;WebElement&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;context&lt;/span&gt; : driver.findElements(
    By.cssSelector(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;.context&quot;&lt;/span&gt;))) {
      contextIdList.add(context.getAttribute(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;id&quot;&lt;/span&gt;));
  }

  &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;open the &quot;add context&quot; modal&lt;/span&gt;
  driver.findElement(By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;addContextLink&quot;&lt;/span&gt;)).click();
  Assert.assertNotSame(driver.findElements(
    By.className(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;wicket_modal&quot;&lt;/span&gt;)).size(), 0);

  &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;switch focus to the modal&lt;/span&gt;
  &lt;span style=&quot;color: #b58900;&quot;&gt;WebElement&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;webElementModal&lt;/span&gt; = driver.findElement(
    By.className(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;wicket_modal&quot;&lt;/span&gt;));
  logger.info(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Modal: &quot;&lt;/span&gt; + webElementModal + &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;, &quot;&lt;/span&gt; +
    webElementModal.getAttribute(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;id&quot;&lt;/span&gt;));
  driver.switchTo().frame(webElementModal);
  logger.info(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Focused on modal window&quot;&lt;/span&gt;);

  &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;verify form elements rendered, fill out the form&lt;/span&gt;
  &lt;span style=&quot;color: #b58900;&quot;&gt;String&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;contextName&lt;/span&gt; = &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Test Context (&quot;&lt;/span&gt; + (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Date&lt;/span&gt;().getTime()) + &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;)&quot;&lt;/span&gt;;
  Assert.assertNotSame(driver.findElements(By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;contextAddNameField&quot;&lt;/span&gt;))
    .size(), 0);
  driver.findElement(By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;contextAddNameField&quot;&lt;/span&gt;)).sendKeys(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Test Context&quot;&lt;/span&gt;);
  Assert.assertNotSame(driver.findElements(
    By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;contextAddDescriptionField&quot;&lt;/span&gt;)).size(), 0);
  driver.findElement(By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;contextAddDescriptionField&quot;&lt;/span&gt;))
    .sendKeys(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;For all the testing&quot;&lt;/span&gt;);
  Assert.assertNotSame(driver.findElements(
    By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;formCancelLink&quot;&lt;/span&gt;)).size(), 0);
  Assert.assertNotSame(driver.findElements(
    By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;formSubmitLink&quot;&lt;/span&gt;)).size(), 0);

  &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;submit the form&lt;/span&gt;
  driver.findElement(By.id(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;formSubmitLink&quot;&lt;/span&gt;)).click();

  &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;switch focus back to the main window&lt;/span&gt;
  driver.switchTo().defaultContent();

  &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;wait for the new context to appear&lt;/span&gt;
  &lt;span style=&quot;color: #b58900;&quot;&gt;WebElement&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;webElementContextNew&lt;/span&gt; =
          (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;WebDriverWait&lt;/span&gt;(driver, 30)).until(
             &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;ExpectedCondition&lt;/span&gt;&amp;lt;WebElement&amp;gt;() {

      &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Override&lt;/span&gt;
      &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;WebElement&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;apply&lt;/span&gt;(&lt;span style=&quot;color: #b58900;&quot;&gt;WebDriver&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;webDriver&lt;/span&gt;) {

          &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;holder for the new context element&lt;/span&gt;
          &lt;span style=&quot;color: #b58900;&quot;&gt;WebElement&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;webElementNew&lt;/span&gt; = &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;null&lt;/span&gt;;

          &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;loop through all of our context identifiers&lt;/span&gt;
          &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;for&lt;/span&gt;(&lt;span style=&quot;color: #b58900;&quot;&gt;WebElement&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;context&lt;/span&gt; : webDriver.findElements(
            By.cssSelector(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;.context&quot;&lt;/span&gt;))) {

              &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;if&lt;/span&gt;(&lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;!&lt;/span&gt;contextIdList.contains(context.getAttribute(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;id&quot;&lt;/span&gt;))) {
                  logger.info(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;New context found with ID &quot;&lt;/span&gt; +
                    context.getAttribute(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;id&quot;&lt;/span&gt;));
                  webElementNew = context;
              }
          }

          &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;return&lt;/span&gt;  webElementNew;
      }
  });
  Assert.assertNotNull(webElementContextNew);
}
&lt;/pre&gt;


&lt;p&gt;
You can see that there’s a lot more going on here. In order to make
this test more robust, we’re making a list of the current contexts
before we add our new one. After we submit our form, we know our new
context has appeared on the page when the list of contexts grows by
one element.
&lt;/p&gt;
&lt;p&gt;
The nice thing about Selenium is that the code is pretty easy to
follow, it’s just more Java code. In my opinion the trickiest part is
figuring out which parts of the page you need to wait for and then
writing the right code to test the change.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-5&quot;&gt;The Idealized Case: You Just Saw It and Your App Isn’t It&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-5&quot;&gt;


&lt;p&gt;
It’s important to note that the example above covers a pretty
idealized case, the same sort of application that most of the Spring
with IT documentation spends its time on. It’s all pretty
straightforward and it all works really great. Still, before we move
on lets recap the assumptions we’ve made so far:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We don’t need any fixture data and our tests will interact &lt;i&gt;only&lt;/i&gt;
  with the web application. There’s no access to our Spring service or
  data layers at all.

&lt;/li&gt;
&lt;li&gt;We’re using an ephemeral data store and we’re not worried about
  cluttering it up with stuff. In the above example we didn’t talk
  about it but it’d be reasonable to use an in-memory database.
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
Idealism, like it or not, is a exclusionary. By assuming a brand new
application and presuming minimum complexity we’ve unwittingly
excluded most real-life applications; adapting the code above to your
own existing project may not work out so well. Such is life.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-1&quot;&gt;Access to the Spring Container&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-1&quot;&gt;


&lt;p&gt;
It doesn’t need to be this way, let’s address the first item: we need
access to the Spring container in order to create and delete fixture
data for our tests.
&lt;/p&gt;
&lt;p&gt;
The first thing we need to do is to start using the
&lt;code&gt;SpringJunit4ClassRunner&lt;/code&gt;, this will instantiate a Spring container
and then run our IT suite in the context of this container. Simply
adding an annotation to the top of the test class and then providing a
&lt;code&gt;ContextConfiguration&lt;/code&gt; that points to our Spring configuration will be
enough to get this working.
&lt;/p&gt;



&lt;pre class=&quot;src src-java&quot;&gt;&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@RunWith&lt;/span&gt;(SpringJUnit4ClassRunner.&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;class&lt;/span&gt;)
&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@ContextConfiguration&lt;/span&gt;(locations = {&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;classpath:/applicationContext.xml&quot;&lt;/span&gt;})
&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;ToDoHomePageTest&lt;/span&gt; {

  &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;test code goes here...&lt;/span&gt;
}
&lt;/pre&gt;


&lt;p&gt;
If you try to do this with our test application, you’ll see that this
is a little more complicated than it looks. In our case we’re using an
embedded Derby instance in our web application and when we try to spin
up a second Spring configuration it fails because the files that Derby
is using are locked. This isn’t a failure on the part of Spring, it’s
just the way it is: you probably don&apos;t want to use an embedded
database when your doing this kind of testing.  You could fudge it by
having the test Spring instance connect to the running Derby instance
but we’re not going to go over that here, this article is already
&lt;i&gt;way&lt;/i&gt; too long as it is.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-2&quot;&gt;Transactions That Persist&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-2&quot;&gt;


&lt;p&gt;
Let’s assume that we’ve moved to a standalone database that already
contains our test data, the next problem we’ll encounter is that no
matter how we annotate our tests, the data never makes it to the
database. This is a feature of the Spring JUnit runner: the
transaction manager provided rolls back the transaction once the test
completes. This is a nice idea but in the context of IT tests, not
super useful. After all, we need our fixture data to make it into the
database so that our web application can see this data and our tests
can interact with it.
&lt;/p&gt;
&lt;p&gt;
The fix here is easy, just ask Spring for a real transaction manager
and use the &lt;code&gt;TransactionTemplate&lt;/code&gt; to get a real-life transaction for
your operation. Also, use the &lt;code&gt;TransactionConfiguration&lt;/code&gt; annotation to
set the “defaultRollback” property to false.
&lt;/p&gt;



&lt;pre class=&quot;src src-java&quot;&gt;&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@TransactionConfiguration&lt;/span&gt;(transactionManager = &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;txMgr&quot;&lt;/span&gt;,
  defaultRollback = &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;false&lt;/span&gt;)
&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;ToDoHomePageTest&lt;/span&gt; {

  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Autowired&lt;/span&gt;
  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;PlatformTransactionManager&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;platformTransactionManager&lt;/span&gt;;

  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Autowired&lt;/span&gt;
  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;ContextService&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;contextService&lt;/span&gt;;

  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;TransactionTemplate&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;transactionTemplate&lt;/span&gt;;
  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Context&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;context&lt;/span&gt; = &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;null&lt;/span&gt;;

  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Before&lt;/span&gt;
  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;void&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;setUp&lt;/span&gt;() {

    transactionTemplate = &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;TransactionTemplate&lt;/span&gt;(
      platformTransactionManager);

    context = transactionTemplate.execute(
      &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;TransactionCallback&lt;/span&gt;&amp;lt;Context&amp;gt;() {

      &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Override&lt;/span&gt;
      &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Context&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;doInTransaction&lt;/span&gt;(
        &lt;span style=&quot;color: #b58900;&quot;&gt;TransactionStatus&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;transactionStatus&lt;/span&gt;) {

        &lt;span style=&quot;color: #b58900;&quot;&gt;Context&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;context&lt;/span&gt; = &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Context&lt;/span&gt;();
        context.setName(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Test Context&quot;&lt;/span&gt;);
        context.setDescription(&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;For all the testing.&quot;&lt;/span&gt;);

        &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;return&lt;/span&gt; contextService.save(context);
      }
    }
  }

  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@Test&lt;/span&gt;
  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;void&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;testAddToDo&lt;/span&gt;() {

    &lt;span style=&quot;color: #586e75;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;test code here&lt;/span&gt;
  }

  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@After&lt;/span&gt;
  &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;void&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;tearDown&lt;/span&gt;() {

    transactionTemplate.execute(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;new&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;TransactionCallback&lt;/span&gt;() {
      &lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;return&lt;/span&gt; contextService.delete(context);
    });
  }
}
&lt;/pre&gt;


&lt;p&gt;
We’ve added an &lt;code&gt;AutoWired&lt;/code&gt; field for the transaction manager and the
context service. With these in hand we can go ahead and create our
fixture data, run our test and then delete the data when the test are
complete. As an added bonus, this will also let you test with
Hibernate&apos;s Auditors.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-3&quot;&gt;Where Are My Properties?&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-3&quot;&gt;


&lt;p&gt;
So far so good but if you’re loading data from &lt;code&gt;*.properties&lt;/code&gt; files
you’ll notice that those files aren’t picked up by the test
runner. Lucky for you there’s another annotation for that, it may be
placed above your class declaration.
&lt;/p&gt;



&lt;pre class=&quot;src src-java&quot;&gt;&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;@PropertySource&lt;/span&gt;({&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;classpath:spring.properties&quot;&lt;/span&gt;,
  &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;classpath:other.properties&quot;&lt;/span&gt;})
&lt;/pre&gt;


&lt;p&gt;
This will read in those two files of properties and make those
properties available to your test Spring configuration.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-4&quot;&gt;Two Incompatible Beans?&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-4&quot;&gt;


&lt;p&gt;
The last gotcha&apos; I&apos;ve seen appears when your test Spring configuration
is starting up, it complains that there is more than one bean
available but that they are incompatible and thus the field can&apos;t be
autowired. This one took me a while to track down, in my case there
were mock service beans being instantiated to support regular unit
tests and because Failsafe pulls all of the test classes into the
classpath, these were clashing when the IT tests ran. The solution is
simple: add an exclusion for these test classes in your Spring
configuration file.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;&amp;lt;context:component-scan base-package=&quot;com.windsorsolutions.todos&quot;&amp;gt;
  &amp;lt;context:exclude-filter type=&quot;regex&quot;
     expression=&quot;.*ContextUnitTestConfiguration&quot;/&amp;gt;
&amp;lt;/context:component-scan&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
The regular expression above excluded these mock beans and everything
started working.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-6&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-6&quot;&gt;Your Mileage May Vary&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-6&quot;&gt;


&lt;p&gt;
My hope is that this article covers most of the issues you may run
into when running IT tests against your Spring project. Still, Spring
provides a lot of stuff and there may be issues lurking out there. If
you run into anything really interesting, let me know and I&apos;ll add it
to this article!
&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Bishop&#58; Makes Your Web Service Shiny</title>
   <link href="http:///posts/bishop/"/>
   <updated>2012-05-07T00:00:00-04:00</updated>
   <id>http:///posts/bishop</id>
   <content type="html">&lt;p&gt;
A couple of months ago I was working with another developer, he was
building a web application around a web service that I had just
finished writing. We were going over the API documentation that I put
together and he straight-out asked me: “What happens if I send it
something that’s not JSON?” I answered honestly: “I don’t know, but I
bet it logs a really big stack trace!”
&lt;/p&gt;


&lt;div class=&quot;pullquote&quot;/&gt;
&lt;p&gt;
“What happens if I send it something that’s not JSON?”
&lt;/p&gt;
&lt;p&gt;
“I don’t know, but I bet it logs a really big stack trace!”
&lt;/div&gt;

This sort of thing (authoring services) is becoming a bigger and
bigger part of my work and it’s inexcusable that I don’t know more
about how HTTP actually works. In this case the application was
implemented in &lt;a href=&quot;http://www.clojure.org&quot;&gt;Clojure&lt;/a&gt; and used &lt;a href=&quot;https://github.com/mmcgrana/ring&quot;&gt;Ring&lt;/a&gt; to handle the web server part,
anything that validated the content being sent to the application I
had to write myself. And I did, kind of; if the JSON parser couldn’t
parse the data the client sent then it logged a stack trace.
&lt;/p&gt;
&lt;p&gt;
Really, I should check the incoming “Content-Type” header that the
client provided and if wasn’t JSON I shouldn’t even try to parse the
data. That’s not so hard, but did I want to be re-writing that code
for every project? And what about all of the other &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_header&quot;&gt;HTTP headers&lt;/a&gt;?  To
be honest, I didn’t even know what most of them were supposed to be
doing.
&lt;/p&gt;
&lt;p&gt;
Naturally this is a problem that’s been solved before. After &lt;a href=&quot;file:///2012/04/02/restish/&quot;&gt;doing some research&lt;/a&gt; I found &lt;a href=&quot;http://wiki.basho.com/Webmachine.html&quot;&gt;Webmachine&lt;/a&gt;, which goes a long way towards
handling all of the HTTP level stuff. Unfortunately for me, it’s
written in &lt;a href=&quot;http://www.erlang.org/&quot;&gt;Erlang&lt;/a&gt; and that means writing your service in Erlang; I
really need to work with Java and Clojure. Still, not one to give up
lightly, I began writing my own Webmachine-like library in Clojure and
thus &lt;a href=&quot;https://github.com/tnr-global/bishop&quot;&gt;Bishop&lt;/a&gt; was born.
&lt;/p&gt;

&lt;div id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-1&quot;&gt;More Than a Pipe Into Which You Shovel Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2&quot;&gt;What Does Bishop Do?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3&quot;&gt;How Do I Get Started with Bishop?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-1&quot;&gt;Add the Dependency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-2&quot;&gt;The Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-3&quot;&gt;The Service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-4&quot;&gt;Setting up Routes and Starting the Server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4&quot;&gt;Conclusion and Wrap-Up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;More Than a Pipe Into Which You Shovel Data&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;


&lt;p&gt;
After taking a closer look at HTTP and &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616.html&quot;&gt;it’s specification&lt;/a&gt;, it was
clear that it could do a lot more than I had thought. Looking back on
past projects, it’s painfully obvious that I’ve been taking what is
really an application protocol and ignoring all of the interesting
bits, instead using it as little more than a pipe to push documents
through. I’ve been using either the requested URL or parameters or
maybe even neither and simply examined the body content, thus
eliminating any of the real advantages of using HTTP in the first
place.
&lt;/p&gt;
&lt;p&gt;
And there are advantages. The protocol is already thinking about
caching your data where it makes the most sense. There’s already an
algorithm for taking the list of content types that the client wants
and the content types the server provides and picking the best
match. It can manage safe updating of resources as well as notifying
the client of conflicts. And so on, by ignoring what the HTTP protocol
has to offer I was making more work for myself.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-2&quot;&gt;What Does Bishop Do?&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-2&quot;&gt;




&lt;a href=&quot;https://raw.github.com/wiki/basho/webmachine/images/http-headers-status-v3.png&quot; rel=&quot;lightbox&quot;
  title=&quot;HTTP 1.1 Activity Diagram&quot;&gt;
  &lt;img
  src=&quot;https://raw.github.com/wiki/basho/webmachine/images/http-headers-status-v3.png&quot;
  class=&quot;float_right&quot; style=&quot;width: 630px;&quot;/&gt;
&lt;/a&gt;

&lt;p&gt;
Like Webmachine, the Bishop library handles all of the HTTP protocol
level stuff, you won’t have to spend a lot of time thinking about
it. It does this by letting you code up a set of callback functions
that the library will invoke as needed. For instance, when you’re
using Bishop you can provide a function that lists the content types
that your application knows about (i.e. “text/html”,
“application/json”, etc.) and another that lists the request methods
your application can handle (i.e. “GET”, “PUT”, etc.) Bishop will
invoke the functions at the right time in order to negotiate with the
client. When everything is all figured out (what content type to
provide, if the URL points to a valid resource, etc.), your handler
function is invoked and you can do whatever it is that your
application does.
&lt;/p&gt;
&lt;p&gt;
The idea is to provide a relatively small library that will make your
life easier and hopefully more pleasant by making it straightforward
to provide a consistent web service API that obeys HTTP semantics. It
will make the lives of those around you easier as well, clients can
expect your service to respond to the common HTTP request methods with
reasonable responses. Placing caches around your service will also be
much simpler and you’ll have some level of control over how your
service’s data is cached.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-3&quot;&gt;How Do I Get Started with Bishop?&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-3&quot;&gt;


&lt;p&gt;
The Bishop library was designed to be small and easy to use. The basic
idea is that you will code up a set of Bishop resources, each resource
will correspond to a particular URL path. These resources will provide
the necessary functions that Bishop needs in order to negotiate the
request with the client and decide which handler function to
invoke. Once that step is complete, your handler function will likely
invoke a function or set of functions that provide the core
functionality of your application; perhaps calling functions that add
a record to a database or fetch data from your application.
&lt;/p&gt;
&lt;p&gt;
We’ll wall through the &lt;a href=&quot;https://github.com/tnr-global/bishop-sample&quot;&gt;sample application&lt;/a&gt; that accompanies the library
and explain how the pieces fit together.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-1&quot;&gt;Add the Dependency&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-1&quot;&gt;


&lt;p&gt;
If you’re using &lt;a href=&quot;https://github.com/technomancy/leiningen&quot;&gt;Leiningen&lt;/a&gt; to manage your project, getting started is
as easy as adding a dependency to your “project.clj” file.
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;defproject&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;bishop-sample&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.0&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:description&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;A sample Bishop application&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:dependencies&lt;/span&gt; [[&lt;span style=&quot;color: #b58900;&quot;&gt;org.clojure&lt;/span&gt;/clojure &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.3.0&quot;&lt;/span&gt;]
                 [&lt;span style=&quot;color: #b58900;&quot;&gt;ring&lt;/span&gt;/ring-core &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.0.2&quot;&lt;/span&gt;]
                 [&lt;span style=&quot;color: #b58900;&quot;&gt;ring&lt;/span&gt;/ring-jetty-adapter &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.0.2&quot;&lt;/span&gt;]
                 [cheshire &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;4.0.0&quot;&lt;/span&gt;]
                 [hiccup &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;]
                 [&lt;span style=&quot;color: #b58900;&quot;&gt;tnrglobal&lt;/span&gt;/bishop &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.0.2&quot;&lt;/span&gt;]]
  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:dev-dependencies&lt;/span&gt; [[&lt;span style=&quot;color: #b58900;&quot;&gt;swank-clojure&lt;/span&gt;/swank-clojure &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.3.3&quot;&lt;/span&gt;]]
  &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:main&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;com.tnrglobal.bishopsample.core&lt;/span&gt;)
&lt;/pre&gt;


&lt;p&gt;
In the example above, we’re using Ring and Jetty to provide the web
service, &lt;a href=&quot;https://github.com/dakrone/cheshire&quot;&gt;Cheshire&lt;/a&gt; to handle JSON parsing and &lt;a href=&quot;https://github.com/weavejester/hiccup&quot;&gt;Hiccup&lt;/a&gt; to provide HTML
output. When you build your project, Leiningen will fetch all of these
dependencies and anything they might depend on.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-2&quot;&gt;The Application&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-2&quot;&gt;


&lt;p&gt;
Next we need the application that we’ll be wrapping with our service
interface. In this example we’ll be providing a simple to-do list with
functions for listing all of the to-dos as well as adding, removing
and updating items.
&lt;/p&gt;
&lt;p&gt;
Each to-do item will be represented by a hash-map containing a title,
description, the date it was created and the date of it’s last
modification. First we setup a reference to hold our “database” of
to-do items and we’ll start it off with one entry. I won’t go over
this stuff function-by-function, you can take a look at &lt;a href=&quot;https://github.com/tnr-global/bishop-sample/blob/master/src/com/tnrglobal/bishopsample/application.clj&quot;&gt;the implementation here&lt;/a&gt;.
&lt;/p&gt;

&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-3&quot;&gt;The Service&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-3&quot;&gt;


&lt;p&gt;
Each Bishop resource is defined with two hash-maps. The first provides
a map of content types provided by the resource and the functions that
accept a Ring request map and return data in that format. The second
hash-map contains the key for a callback function and the actual
function, these are invoked by Bishop when processing each
request. You can &lt;a href=&quot;https://github.com/tnr-global/bishop/blob/master/src/com/tnrglobal/bishop/resource.clj&quot;&gt;view the complete list of callback functions here.&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
We can now work on developing the Bishop resources that will provide
our web service. We’ll be using the “/todos” URL to provide both a
list of to-do items (with a “GET”) and an end-point for requests to
create new items (via “POST” or “PUT”). While this application deals
with JSON data we also want to present a simple HTML interface for
viewing the data. Listed below is our resource definition.
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;todo-group-resource&lt;/span&gt;
  (&lt;span style=&quot;color: #b58900;&quot;&gt;bishop&lt;/span&gt;/resource

   {&lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;JSON handler&lt;/span&gt;
    &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;application/json&quot;&lt;/span&gt;
    (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;fn&lt;/span&gt; [request]
      (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;cond&lt;/span&gt;

               &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;return a sequence of to-do items&lt;/span&gt;
               (= &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:get&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:request-method&lt;/span&gt; request))
               {&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:body&lt;/span&gt; (generate-string
                        (map (partial add-resource-links URI-BASE)
                             (&lt;span style=&quot;color: #b58900;&quot;&gt;app&lt;/span&gt;/todos)))}

               &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;create the new to-do item&lt;/span&gt;
               (= &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:put&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:request-method&lt;/span&gt; request))
               (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;let&lt;/span&gt; [todo-in (parse-string (slurp (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:body&lt;/span&gt; request)) &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;true&lt;/span&gt;)
                     todo (&lt;span style=&quot;color: #b58900;&quot;&gt;app&lt;/span&gt;/todo-add todo-in)]
                 {&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:headers&lt;/span&gt; {&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Location&quot;&lt;/span&gt; (str URI-BASE &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;/&quot;&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:id&lt;/span&gt; todo))}})))

    &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;HTML handler&lt;/span&gt;
    &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;text/html&quot;&lt;/span&gt;
    (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;fn&lt;/span&gt; [request]
      {&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:body&lt;/span&gt; (xhtml {&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:lange&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;en&quot;&lt;/span&gt; }
                    [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:body&lt;/span&gt;
                     [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:h1&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;To-Do List&quot;&lt;/span&gt;]
                     [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:ul&lt;/span&gt; (map #(todo-short-html
                                  (add-resource-links URI-BASE &lt;span style=&quot;color: #268bd2;&quot;&gt;%&lt;/span&gt;))
                               (&lt;span style=&quot;color: #b58900;&quot;&gt;app&lt;/span&gt;/todos))]])})}

   {&lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;the request methods supported by this resource&lt;/span&gt;
    &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:allowed-methods&lt;/span&gt; (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;fn&lt;/span&gt; [request] [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:get&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:head&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:post&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:put&lt;/span&gt;])

    &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;POSTs with new data will be handled like a PUT&lt;/span&gt;
    &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:post-is-create?&lt;/span&gt; (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;fn&lt;/span&gt; [request] &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;true&lt;/span&gt;)

    &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;we use the modification date on the most recently modified&lt;/span&gt;
    &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;to-do item as the modification date for the list of items&lt;/span&gt;
    &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:last-modified&lt;/span&gt; (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;fn&lt;/span&gt; [request]
                     (&lt;span style=&quot;color: #b58900;&quot;&gt;app&lt;/span&gt;/most-recent-todo-modified))}))
&lt;/pre&gt;


&lt;p&gt;
The first map defines the content types our resource accepts and the
functions that handle each type of request. Our first handler accepts
JSON data and can handle either a “GET” or a “PUT” request. You can
see that when the client fetches data we add resource links to each
item and then use the resulting hash-map to create our JSON
output. This output is then placed under the “:body” key of the
response object. It the client is providing new to-do item data with a
“PUT” request, we’ll parse that JSON data into a hash-map and add that
item to our database. We’ll then redirect the client to the URL for
the new to-do item.
&lt;/p&gt;
&lt;p&gt;
Our second entry handles HTML content. When the client makes a “GET”
request for HTML to our resource, we’ll provide an HTML listing of the
items (including a link to the resource for each item). We don’t have
to worry about parsing out the client’s headers and figuring out which
type of content the client wants. Bishop handles that all of that for
us.
&lt;/p&gt;
&lt;p&gt;
We define our callback functions for our resource in the second
hash-map, we provide the information that Bishop needs to handle
incoming requests. We specify the methods that our resource can handle
and instruct it to treat “POST” requests as if the client was doing a
“PUT” for a new item. We also tell Bishop how to correctly generate
the “Last-Modified” header.
&lt;/p&gt;
&lt;p&gt;
There’s much more that Bishop can do for you, take a look at &lt;a href=&quot;https://github.com/tnr-global/bishop-sample/blob/master/src/com/tnrglobal/bishopsample/service.clj#L87&quot;&gt;the rest of the code for this service&lt;/a&gt;. For instance, when you tell Bishop how
to generate an “ETag” for your resource you make it easy for clients
to fetch the entire resource only when it actually changes. If you run
this sample application and point Google Chrome at it, you’ll notice
that the first time you view a to-do item in the browser it fetches
the item from the server. Subsequent fetches are served from Chrome’s
local cache: Chrome uses the ETag header to do a conditional fetch and
as long as the resource isn’t updated, Bishop responds with a “304 Not
Modified.”
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-4&quot;&gt;Setting up Routes and Starting the Server&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-4&quot;&gt;


&lt;p&gt;
Lastly, we need to set our application up with Ring and a server, in
this example we use &lt;a href=&quot;http://jetty.codehaus.org/jetty/&quot;&gt;Jetty&lt;/a&gt;. First, our routes.
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;routes&lt;/span&gt;
  {[&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;todos&quot;&lt;/span&gt;] todo-group-resource
   [&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;todos&quot;&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:id&lt;/span&gt;] todo-item-resource})
&lt;/pre&gt;


&lt;p&gt;
We’re using Bishop’s router, we indicate that everything with a
“todos” URL should be directed at our group resource and anything in
the form of “todo/???” should hit our item resource. The router
provided is modeled after Webmachine, if you prefer another router you
could simply use that instead.
&lt;/p&gt;
&lt;p&gt;
Next we setup our Ring application. No magic here, we provide our
routes (which point to Bishop resources) to our Bishop handler. We can
then wrap that handler in any required middle-ware (none in this case)
and hand that off to Jetty.
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;we setup our ring application&lt;/span&gt;
(run-jetty (&lt;span style=&quot;color: #b58900;&quot;&gt;bishop&lt;/span&gt;/handler routes) {&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:port&lt;/span&gt; 3000})
&lt;/pre&gt;


&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-4&quot;&gt;Conclusion and Wrap-Up&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-4&quot;&gt;


&lt;p&gt;
The Bishop library provides the tools needed to easily create a web
service that understands the semantics of HTTP. While this is
something that you could always do on your own, the library makes it
easy by encapsulating all of the logic required to understand the
client’s request headers and then directing it to the correct handler
on the application side. Under the covers Bishop implements &lt;a href=&quot;https://en.wikipedia.org/wiki/Finite-state_machine&quot;&gt;finite state machine&lt;/a&gt; that models the HTTP protocol, ensuring that requests
are handled the same way every time.
&lt;/p&gt;
&lt;p&gt;
It is early days for this project, this is only the first release and
I have no doubt that there are bugs to be uncovered! If you happen to
spot any, please take the time to &lt;a href=&quot;https://github.com/tnr-global/bishop/issues&quot;&gt;log an issue&lt;/a&gt;. Help of any kind is
greatly appreciated and we’re looking forward to accepting pull
requests.
&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Why Is My Web Service API Crappy?</title>
   <link href="http:///posts/restish/"/>
   <updated>2012-04-02T00:00:00-04:00</updated>
   <id>http:///posts/restish</id>
   <content type="html">&lt;p&gt;
I knew that I hard written a crappy web service API but &lt;i&gt;why&lt;/i&gt; was it
so crappy?  Was it because, no matter what, it only returned JSON
data?  Was it because the arguments were passed via their positions in
the URL? Was it because the version of the API was part of the URL? Was
it all of the above and, if so, how was it &lt;i&gt;supposed&lt;/i&gt; to be done?
&lt;/p&gt;
&lt;p&gt;
I’ve been doing a lot of research and reading and I think I have a
better idea of how this all should work. For the sake of getting all
of my ideas down in one place, I’m going to crudely regurgitate my
current thinking in this article.
&lt;/p&gt;

&lt;div id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-1&quot;&gt;Why It’s Crappy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2&quot;&gt;Another Way&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2-1&quot;&gt;Give the Client What the Client Wants&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2-2&quot;&gt;Tell the Client Where To Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2-3&quot;&gt;Provide Helpful Hints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2-4&quot;&gt;URLs Can Represent State Changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2-5&quot;&gt;Predictable Caching&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3&quot;&gt;A Really Simple Web Service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4&quot;&gt;Caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5&quot;&gt;Modeling a Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-6&quot;&gt;You Don’t Need to Do it All On Your Own&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Why It’s Crappy&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;


&lt;p&gt;
Right off the bat, I knew my API was weak in that it completely
ignored the &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616.html&quot;&gt;HTTP protocol&lt;/a&gt;. If you’re writing a service that speaks
HTTP (and I was), you should probably know a little about how HTTP
works (and I didn’t). This is the sort of thing those crazy &lt;a href=&quot;http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm&quot;&gt;REST&lt;/a&gt; people
have been crowing about forever and now I can understand why: there’s
a lot more meat in there than I had thought. For sure, it won’t solve
all of your problems but it can solve some of them.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-2&quot;&gt;Another Way&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-2&quot;&gt;


&lt;p&gt;
If I made my API work a little bit more like how web servers already
work, I could make things easier on everyone. I’ve always worked with
a pretty lightweight framework to put these APIs together
(i.e. &lt;a href=&quot;http://www.cherrypy.org/&quot;&gt;CherryPy&lt;/a&gt; or &lt;a href=&quot;https://github.com/mmcgrana/ring&quot;&gt;Ring&lt;/a&gt;) and while this works okay, it left lot of the
work of handling the various HTTP headers up to the me. I mostly
skipped all of that stuff because I needed to get things done. If
you’ve done the same, trust me, there’s no judgment here. But there
are some good reasons to implement more of this stuff.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-2-1&quot;&gt;Give the Client What the Client Wants&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-2-1&quot;&gt;


&lt;p&gt;
For one thing, the client can tell you very specifically what kind of
content it wants. Maybe it wants XML, if you really only serve JSON
data you can go ahead and say so. Maybe the client does want JSON data
but only an older version of your API. That’s right, you don’t need to
put the version in the URL, finessing the requested content type can
serve the same purpose.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-2-2&quot;&gt;Tell the Client Where To Go&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-2-2&quot;&gt;


&lt;p&gt;
I usually end up publishing more and more URLs as more functionality is
needed. It all ends up looking very ad-hoc because it’s all very
ad-hoc. But what if we provided pointers to the client instructing it
where it should go to get the information that it wants or to change
the state of a resource? We’d end up with much looser URLs and we’d be
able to change them when we need to. While the client needs to do a
little more work to parse out these URLs, I think it’s definitely
worth it.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-2-3&quot;&gt;Provide Helpful Hints&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-2-3&quot;&gt;


&lt;p&gt;
The client can submit the &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2&quot;&gt;“OPTIONS”&lt;/a&gt; request to find out what actions
(i.e. “GET”, “POST”, “PUT”, etc.) are valid for the URL. Sure, you can
put that in the documentation but the client can also discover it on
their own. Much of the time the correct method will already be
assumed; i.e. you POST to create a new resource and PUT to update an
existing item.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-2-4&quot;&gt;URLs Can Represent State Changes&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-2-4&quot;&gt;


&lt;p&gt;
Once we get our API to the point where the client is following the
links that we provided with our responses, we’re in a place where the
state of a particular item changes as the URLs our API provides are
followed. If a particular action isn’t valid for a particular item we
simply do not provide a URL for that action.
&lt;/p&gt;
&lt;p&gt;
This isn’t earth-shattering news, it’s just not something I hadn’t
ever thought about before. But now that I’m thinking about it, it’s
clearly a much better way to manage these state transitions.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-2-5&quot;&gt;Predictable Caching&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-2-5&quot;&gt;


&lt;p&gt;
Caching the results of your API can be a real pain point, especially
if you’re not providing the information that the caching layer really
needs to be effective. For the longest time I didn’t have to worry
about putting a cache in front of my web service, they were all
internal. Once I did, it was made clear to me how difficult my service
was making it for everyone else.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-3&quot;&gt;A Really Simple Web Service&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-3&quot;&gt;


&lt;p&gt;
Okay, enough talk. What does an API that leverages these ideas actual
look like? We’ll use a simple example, a web service that exposes a
single to-do list. First, we want to get a list of all of the items on
our list, this represents the main entry point to our API. I’m not
going to list all the headers, just the relevant ones.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;GET /todos HTTP/1.1
Host: somesite.com
Accept: application/vnd.com.somesite.todos-v1+xml

Date: Mon, 02 Apr 2012 01:04:15 GMT
Status: 200 OK
Content-Type: application/vnd.com.somesite.todos-v1+xml;charset=utf-8
ETag: &quot;1f298572c3b90d4b4e9da5f25ea0ba9a&quot;
Vary: Accept

&amp;lt;list xmlns=&quot;http://somesite.com/todo/schema/list&quot;
  xmlns:atom=&quot;http://www.w3c.org/2005/atom&quot;&amp;gt;
  &amp;lt;todo&amp;gt;
    &amp;lt;description&amp;gt;Write up demonstration of to-do API&amp;lt;/description&amp;gt;
    &amp;lt;atom:link rel=&quot;resource&quot; href=&quot;http://somesite.com/todos/1023&quot;/&amp;gt;
  &amp;lt;/todo&amp;gt;
  &amp;lt;todo&amp;gt;
    &amp;lt;description&amp;gt;Add more to-do items&amp;lt;/description&amp;gt;
    &amp;lt;atom:link rel=&quot;resource&quot; href=&quot;http://somesite.com/todos/1312&quot;/&amp;gt;
  &amp;lt;todo&amp;gt;
&amp;lt;/list&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
In this example the client requests the current list of to-do items
from the server and specifies that it accepts responses that are
specifically version 1 of our XML API. Since the server knows what
version of the API the client is using, it’s easy to provide the
response in the format that the client really wants.
&lt;/p&gt;
&lt;p&gt;
The server provides an &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19&quot;&gt;“ETag”&lt;/a&gt; header with the response, in this case
this is a value that will change every time the list changes. We could
hash the list of to-do items or the unique id of each hash and it’s
version; whatever makes the most sense. The client can then issue a
&lt;i&gt;conditional&lt;/i&gt; request to see if the resource has changed instead of
simply re-fetching all the data.
&lt;/p&gt;
&lt;p&gt;
Lastly, the server returns a list of the current to-do items and a
pointer to where the client can find our schema for this list. We’re
also leveraging the &lt;a href=&quot;http://www.atomenabled.org/developers/syndication/atom-format-spec.php&quot;&gt;Atom Publishing Protocol&lt;/a&gt; to provide annotated
links since that’s easier than writing up our own specification. Each
to-do item has a link that indicates where that individual item lives,
if the client needs to update or delete an item it knows where to go.
&lt;/p&gt;
&lt;p&gt;
Next we create a new item by posting to the “todos” resource. Our API
didn’t provide a link for this since it’s the common REST idiom.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;POST /todos HTTP/1.1
Host: somesite.com
Content-Type: application/vnd.com.somesite.todos-v1+xml

&amp;lt;todo xmlns:atom=&quot;http://somesite.com/todo/schema/todo&quot;&amp;gt;
  &amp;lt;description&amp;gt;Buy more coffee!&amp;lt;/description&amp;gt;
&amp;lt;/todo&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
The server creates the new item and then provides an affirmative
response. It also sends a “Location” header, this indicates the URL
for the new to-do item. The client can use that URL to fetch, update
or delete the item. Lastly, the response body contains the complete
content for the new to-do entry.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;HTTP/1.1 201 Created
Content-Type: application/vnd.com.somesite.todos-v1+xml
Date: Mon, 02 Apr 2012 01:06:23 GMT
ETag: &quot;64d053d0bcf1b4de41f852509fd5ffc2&quot;
Location: http://somesite.com/todos/1314

&amp;lt;todo xmlns:atom=&quot;http://somesite.com/todo/schema/todo&quot;&amp;gt;
  &amp;lt;description&amp;gt;Buy more coffee!&amp;lt;/description&amp;gt;
&amp;lt;/todo&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
With the location of the new resource in hand, the client can update
this new to-do item at will.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;PUT /todos/1314 HTTP/1.1
Host: somesite.com
Content-Type: application/vnd.com.somesite.todos-v1+xml
If-Match: &quot;64d053d0bcf1b4de41f852509fd5ffc2&quot;

&amp;lt;todo xmlns:atom=&quot;http://somesite.com/todo/schema/todo&quot;&amp;gt;
  &amp;lt;description&amp;gt;Buy more coffee! And more creamer.&amp;lt;/description&amp;gt;
&amp;lt;/todo&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
The client provides the entirety of the updated to-do item to that
resource’s location (“PUT” always expects the entire resource). Note
that the client provides the ETag of the resource in the &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24&quot;&gt;“If-Match”&lt;/a&gt;
header; if the to-do item has been updated by another client the ETag
won’t match and the server will return a “412 Precondition Failed”
instead of enacting the change.
&lt;/p&gt;
&lt;p&gt;
The server then responds with a confirmation.
&lt;/p&gt;



&lt;pre class=&quot;example&quot;&gt;HTTP/1.1 200 OK
Content-Type: application/vnd.com.somesite.todos-v1+xml
Date: Mon, 02 Apr 2012 01:12:46 GMT
ETag: &quot;9cc7dc7e5f4a81e3d8daf752a622e84d&quot;
Location: http://somesite.com/todos/1314

&amp;lt;todo xmlns:atom=&quot;http://somesite.com/todo/schema/todo&quot;&amp;gt;
  &amp;lt;description&amp;gt;Buy more coffee! And more creamer.&amp;lt;/description&amp;gt;
&amp;lt;/todo&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
And there you have it. The simple API above gracefully handles changes
to the version of API if needed. Our URLs are provided with the
relevant resource, hopefully cutting down on the number of URLs that
end up hard-coded into the client. The client uses ETag data when
updating resources to avoid conflicts. Our examples revolve around XML
messages but there’s no reason this same service couldn’t also support
JSON data.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-4&quot;&gt;Caching&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-4&quot;&gt;


&lt;p&gt;
In the example above, we didn’t do anything all that interesting to
support caching of our API’s responses. Nonetheless, the list of to-do
items and those items themselves could be cached somewhere between the
client and the server; all “GET” requests are cache-able by default.
&lt;/p&gt;
&lt;p&gt;
By adding headers to our responses we can exert some real control over
how these responses are cached. We can specify a specific time, after
which intermediate caches will need to fetch a fresh copy. We can
specify what sort of cache is allowed to cache our data, indicating
that the client can cache it locally but intermediate caches
cannot. And, of course, we can tag certain responses as entirely
uncache-able.
&lt;/p&gt;
&lt;p&gt;
The client can save itself some transmission overhead by using
conditional requests. In the example above, the client provided an
“If-Match” header when updating a resource, ensuring that the resource
was in the expected state. The client could use the very same logic
when fetching the list of to-do items, it would only pull down the new
list if it had changed.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-5&quot;&gt;Modeling a Protocol&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-5&quot;&gt;


&lt;p&gt;
We only covered some simple create, update and delete functionality in
the example above. While this is a good fit for how most of us think
about REST services it isn’t the whole story. This same method of
including links with the resource can also be used to define a
protocol, instructing the client on how a resource can be used. As the
client invokes these URLs for a resource the state of the resource
changes, the server instructs the client as to which changes are
available by varying the supplied URLs.
&lt;/p&gt;
&lt;p&gt;
There’s a great presentation out on &lt;a href=&quot;http://www.slideshare.net/adorepump/hateoas-the-confusing-bit-from-rest&quot;&gt;slideshare by Dr. Jim Webber&lt;/a&gt; that
walks through how such a protocol might work. His example is
reasonably complicated, it works through ordering a coffee from coffee
shop. He’s also &lt;a href=&quot;http://www.amazon.com/Jim-Webber/e/B0042TL1LW/ref=ntt_athr_dp_pel_1&quot;&gt;written some interesting books&lt;/a&gt; on the subject.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-6&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-6&quot;&gt;You Don’t Need to Do it All On Your Own&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-6&quot;&gt;


&lt;p&gt;
There are some frameworks out there that make the construction of this
kind of API much easier, &lt;a href=&quot;http://wiki.basho.com/Webmachine.html&quot;&gt;Webmachine&lt;/a&gt; being the most popular
example. These frameworks handle the lion’s share of the header
parsing and exception handling for you, leaving you to implement only
the functionality that makes your API work. In fact, I’m currently
working on a library for &lt;a href=&quot;http://clojure.org/&quot;&gt;Clojure&lt;/a&gt; that does just this (naturally, it’s
&lt;a href=&quot;https://github.com/tnr-global/bishop&quot;&gt;a work in progress&lt;/a&gt;).
&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>All Your HBase Are Belong to Clojure</title>
   <link href="http:///posts/clojure-hbase/"/>
   <updated>2012-01-12T00:00:00-05:00</updated>
   <id>http:///posts/clojure-hbase</id>
   <content type="html">&lt;p&gt;
I’m sure you’ve heard a variation on this story before&amp;hellip;
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;So I have this web crawler and it generates these super-detailed log
files, which is great ‘cause then we know what it&apos;s doing but it’s
also kind of bad ‘cause when someone wants to know why the crawler did
this thing but not that thing I have, like, literally &lt;i&gt;gajigabytes&lt;/i&gt; of
log files and I’m using grep and awk and, well, it’s not working
out. Plus what we really want is a nice web application the client can
use.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
I’ve never really had a good solution for this. One time I crammed
this data into a big Lucene index and slapped a web interface on
it. One time I turned the data into JSON and pushed it into CouchDB
and slapped a web interface on that. Neither solution left me with a
great feeling although both worked okay at the time.
&lt;/p&gt;
&lt;p&gt;
This time I already had a Hadoop cluster up and running, I didn’t have
any experience with HBase but it looked interesting. After hunting
around the internet, thought this might be the solution I had been
seeking. Indeed, loading the data into HBase was fairly
straightforward and HBase has been very responsive. I mean, very
responsive now that I’ve structured my data in such a way that HBase
&lt;i&gt;can&lt;/i&gt; be responsive.
&lt;/p&gt;
&lt;p&gt;
And that’s the thing: if you are loading literally gajigabytes of data
into HBase you need to be pretty sure that it’s going to be able to
answer your questions in a reasonable amount of time. Simply cramming
it in there probably won’t work (indeed, that approach probably won’t
work great for anything). I loaded and re-loaded a test set of twenty
thousand rows until I had something that worked.
&lt;/p&gt;

&lt;div id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-1&quot;&gt;Setting Up Your Environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2&quot;&gt;What is HBase Again?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3&quot;&gt;Goals: Web Application for Log Viewing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4&quot;&gt;Laying Out My Log Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5&quot;&gt;Loading Data into HBase with Hadoop&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-1&quot;&gt;Setting Up Our Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-2&quot;&gt;Use, Require and Import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-3&quot;&gt;Define Our Hadoop Job&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-4&quot;&gt;Mapping Function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-5&quot;&gt;Reduce Function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5-6&quot;&gt;Load Data into HBase&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-6&quot;&gt;Package and Deploy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-7&quot;&gt;For Great Justice&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Setting Up Your Environment&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;


&lt;p&gt;
Both &lt;a href=&quot;http://hadoop.apache.org/common/docs/stable/#Getting+Started&quot;&gt;Hadoop&lt;/a&gt; and &lt;a href=&quot;http://hbase.apache.org/book/getting_started.html&quot;&gt;HBase&lt;/a&gt; have pretty decent documentation that goes over
installing and configuring these tools. I won’t re-hash that here,
especially given the variance in setup (single node for development
verses multi-node for deployment). In my experience, getting a
multi-node development setup with virtual machines (i.e. with &lt;a href=&quot;http://vagrantup.com/&quot;&gt;Vagrant&lt;/a&gt;)
was problematic; even with host names properly configured I had issues
with connections timing outa.
&lt;/p&gt;
&lt;p&gt;
I’m using Clojure 1.3, the libraries that I needed were available in
&lt;a href=&quot;http://clojars.org&quot;&gt;Clojars&lt;/a&gt; but are built for 1.2. I forked these libraries and moved them
to 1.3 to keep things neat for myself. If you’re using Clojure 1.3,
you can clone these repositories and build your own copies.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/alexott/clojure-hadoop&quot;&gt;Clojure Hadoop&lt;/a&gt; (or &lt;a href=&quot;https://github.com/cmiles74/clojure-hadoop&quot;&gt;my fork for Clojure 1.3&lt;/a&gt; compatibility)
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/davidsantiago/clojure-hbase&quot;&gt;Clojure HBase&lt;/a&gt; (or &lt;a href=&quot;https://github.com/cmiles74/clojure-hbase&quot;&gt;my fork for Clojure 1.3&lt;/a&gt; compatibility)
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
My fork of the HBase library requires a 1.3 compatible version of the
monolithic Clojure Contrib library. You can &lt;a href=&quot;http://nexus.nervestaple.com/service/local/repositories/thirdparty/content/org/clojure/clojure-contrib-compat-1.3/1.2/clojure-contrib-compat-1.3-1.2.jar&quot;&gt;download a copy if you need it&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-2&quot;&gt;What is HBase Again?&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-2&quot;&gt;


&lt;p&gt;
&lt;a href=&quot;http://hbase.apache.org/&quot;&gt;HBase&lt;/a&gt; is provides a &lt;a href=&quot;http://en.wikipedia.org/wiki/BigTable&quot;&gt;BigTable&lt;/a&gt;-like database on top of Hadoop. Uh-oh, I
said &lt;i&gt;database!&lt;/i&gt; Well, it’s not at all like a SQL database; it’s more
like a really big sorted map. In the simplest scenario, given a
particular key HBase can quickly give you all of the values associated
with that key. Because it’s sorted, getting all of the rows between
one key and another is pretty fast. You can also quickly get all of
the rows that begin with a particular key.
&lt;/p&gt;
&lt;p&gt;
While Hadoop will let you store huge amounts of data and run jobs to
analyze that data, you need to do something with the results of those
jobs. Often making those results available for people to look at is
good enough, in my case the client wants to be able to search for
specific information (for instance, the log records produced when a
particular host was crawled) and this isn’t something I could do
easily with Hadoop. My solution was to store the crawl logs on my
Hadoop cluster and then run a Hadoop job that would load this data
into HBase. HBase will let customer query for data on-demand.
&lt;/p&gt;
&lt;p&gt;
This is important to note: it’s a good solution for myself and my
peculiar scenario. There are other data stores that may be a better
fit for someone else. I already have a Hadoop cluster up, running and
in active use. If I was already using &lt;a href=&quot;http://wiki.basho.com/&quot;&gt;Riak&lt;/a&gt;, I most likely would have
concentrated my efforts on making that work instead.
&lt;/p&gt;
&lt;p&gt;
An HBase table is made up of rows, each row contains any number of
columns and each one of those columns belongs to a &lt;i&gt;column family&lt;/i&gt;. Each column family can then have a qualifier and a value. It
took a bit of thinking to get my head around what a column family was,
it’s purpose and how it should be used. Generally speaking, items in
the same column family are stored together physically and that makes
fetching all of the data for a particular row or group of rows column
family faster. There’s a good discussion about how HBase actually
stores this data &lt;a href=&quot;http://ofps.oreilly.com/titles/9781449396107/advanced.html&quot;&gt;in the O’Reilly HBase book&lt;/a&gt;. It’s definitely worth
reading.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-3&quot;&gt;Goals: Web Application for Log Viewing&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-3&quot;&gt;


&lt;p&gt;
Before we move further down this path and try to figure out what data
should go where, we’re going to take a brief detour and look at the
web application that will be presenting the log data. In my opinion,
this will help us get a handle on what data we need and, at least in
terms of idealized application usage, when we will need it.
&lt;/p&gt;
&lt;p&gt;
The data set I’m working with looks like this: a set of files, each
line of each file contains one web crawler transaction. This will have
the date and time of the transaction, the URL that the crawler tried
to crawl, the response from the host serving that resource and the
status the indexer assigned to the result. Here’s a sample row:
&lt;/p&gt;
&lt;table border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;
&lt;colgroup&gt;&lt;col class=&quot;right&quot; /&gt;&lt;col class=&quot;right&quot; /&gt;&lt;col class=&quot;left&quot; /&gt;&lt;col class=&quot;left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th scope=&quot;col&quot; class=&quot;right&quot;&gt;Timestamp&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;right&quot;&gt;Response&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;Status&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;URL&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;right&quot;&gt;2011-01-12-12:44:33&lt;/td&gt;&lt;td class=&quot;right&quot;&gt;200&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;NEW&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;&lt;a href=&quot;http://twitch.nervestaple.com&quot;&gt;http://twitch.nervestaple.com&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;right&quot;&gt;2011-01-12-12:45:12&lt;/td&gt;&lt;td class=&quot;right&quot;&gt;200&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;NEW&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;&lt;a href=&quot;http://twitch.nervestaple.com/index&quot;&gt;http://twitch.nervestaple.com/index&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;
The client wants the ability to see a summary page for each crawled
host, this page should display the number of URLs for the host, how
many returned content and how many had problems. They’d like to have a
report page for each crawled URL that provides some summary
information and a list of transactions for that URL. They want to be
able to bang in an URL or host name into a search box and get dropped
at the right page. They say that’s everything but I can’t help but
have the feeling that this really only represents everything they’ve
though of &lt;i&gt;so far&lt;/i&gt;.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-4&quot;&gt;Laying Out My Log Data&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-4&quot;&gt;


&lt;p&gt;
Given the way that column families work, it makes sense to store data
that we want to access all at once in the same column family. While
it’s hard to know what data people will want, we can make some
guesses. In my case, I know what my web application is going to look
like and I can use that information to inform my choices.
&lt;/p&gt;
&lt;p&gt;
First up, we want the ability to fetch all of the transactions for a
given URL. This means that we’ll want an HBase table where the key
will at least start with the URL. Each URL could be crawled multiple
times, so we’ll combine the URL with the date and time crawled. It’ll
look unpleasant but something like this:
&lt;/p&gt;



&lt;pre class=&quot;src src-text&quot;&gt;http://twitch.nervestaple.com/index2011-01-12-12:45:12
&lt;/pre&gt;


&lt;p&gt;
HBase will store our data sorted by key, this means that when we want
all of the transactions for a given URL it can jump to the first row
that starts with our URL and then return each row there-after in
sequence.
&lt;/p&gt;
&lt;p&gt;
For our log data, we won’t have particularly interesting column
families. I chose to name my columns families “request”,
“transaction”, “response” and “crawler”. To follow through with our
example, the data in HTable would look something like this:
&lt;/p&gt;
&lt;table border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;
&lt;colgroup&gt;&lt;col class=&quot;left&quot; /&gt;&lt;col class=&quot;left&quot; /&gt;&lt;col class=&quot;left&quot; /&gt;&lt;col class=&quot;left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;Row&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;Column Family&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;Qualifier&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;Value&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;left&quot;&gt;&lt;a href=&quot;http://twitch&quot;&gt;http://twitch&lt;/a&gt;&amp;hellip;&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;request&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;url&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;&lt;a href=&quot;http://twitch&quot;&gt;http://twitch&lt;/a&gt;&amp;hellip;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;left&quot;&gt;&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;request&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;host&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;twitch&amp;hellip;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;left&quot;&gt;&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;transaction&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;date-time&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;2011-01-12-12:45:12&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;left&quot;&gt;&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;response&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;code&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;200&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;left&quot;&gt;&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;crawler&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;status&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;New&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;
This layout makes it easy to pull out the rows for a particular
URL. When we want to provide summary information for a specific host,
this isn’t as helpful. While we could scan through all of the records
for the host in question, the client would have to crunch through the
returned data and calculate the summary data. This will be problematic
in practice, instead we’re going to leverage another HBase function:
counters.
&lt;/p&gt;
&lt;p&gt;
HBase will let us treat a column as if it were a counter, this lets us
atomically increment the counter in just one call to the server. As we
load data into HBase, we’ll increment these counters; this will make
it easy to provide the summary data the client demands. For instance,
we’ll have a table called “host-stats” with the column families
“transactions”. We can use a qualifier like “total” to represent all
of the transactions for that host. Some example rows might look like
this:
&lt;/p&gt;
&lt;table border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;
&lt;colgroup&gt;&lt;col class=&quot;left&quot; /&gt;&lt;col class=&quot;left&quot; /&gt;&lt;col class=&quot;left&quot; /&gt;&lt;col class=&quot;right&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;Row&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;Column Family&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;left&quot;&gt;Qualifier&lt;/th&gt;&lt;th scope=&quot;col&quot; class=&quot;right&quot;&gt;Value&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;left&quot;&gt;twitch.nervestaple.com&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;transactions&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;total&lt;/td&gt;&lt;td class=&quot;right&quot;&gt;104&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;left&quot;&gt;bakery.somewhere.com&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;transactions&lt;/td&gt;&lt;td class=&quot;left&quot;&gt;total&lt;/td&gt;&lt;td class=&quot;right&quot;&gt;83&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;
In practice it’d make sense to break this down further. We could have
a family called “transactions-yearly” and use the year portion of the
crawl date as our qualifier. We could have a family called
“transactions-monthly” and use a combination of the month and year
portions of the crawl date as the qualifier. You get the idea.
&lt;/p&gt;
&lt;p&gt;
HBase will let us have as many items of data for each column family as
we want, in our last example we’re using the qualifier to distinguish
between each year and month combination in the “transactions-monthly”
family.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-5&quot;&gt;Loading Data into HBase with Hadoop&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-5&quot;&gt;


&lt;p&gt;
There are a couple different ways to load data into HBase. Given the
amount of computation we have to do (incrementing the counters based
on host and URL) it makes sense to write a Hadoop map/reduce job to
load in this data.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-1&quot;&gt;Setting Up Our Project&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-1&quot;&gt;


&lt;p&gt;
We’ll be using both the Clojure Hadoop and the Clojure HBase
libraries. I’m going to assume that you’re using &lt;a href=&quot;https://github.com/technomancy/leiningen&quot;&gt;Leinigen&lt;/a&gt; to manage
your Clojure projects. If not, you’ll want to revisit that. Go ahead a
create a new project with&amp;hellip;
&lt;/p&gt;



&lt;pre class=&quot;src src-sh&quot;&gt;lein new crawl-log-loader
&lt;/pre&gt;


&lt;p&gt;
Next up you’ll want to add the dependencies to your “project.clj”
file. If you built the libraries from my repositories, you’ll want to
add the following:
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;defproject&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;crawl-log-loader&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.0-SNAPSHOT&quot;&lt;/span&gt;
    &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:description&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Load Crawler Log data&quot;&lt;/span&gt;
    &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:dependencies&lt;/span&gt; [[&lt;span style=&quot;color: #b58900;&quot;&gt;org.clojure&lt;/span&gt;/clojure &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.3.0&quot;&lt;/span&gt;]
                   [clojure-hbase &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;0.90.5-SNAPSHOT&quot;&lt;/span&gt;]
                   [clojure-hadoop &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.3.3-SNAPSHOT&quot;&lt;/span&gt;]
                   [&lt;span style=&quot;color: #b58900;&quot;&gt;org.clojure&lt;/span&gt;/tools.logging &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;0.2.3&quot;&lt;/span&gt;]
                   [&lt;span style=&quot;color: #b58900;&quot;&gt;org.clojure&lt;/span&gt;/tools.cli &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;0.2.1&quot;&lt;/span&gt;]]
    &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:dev-dependencies&lt;/span&gt; [[&lt;span style=&quot;color: #b58900;&quot;&gt;org.codehaus.jackson&lt;/span&gt;/jackson-mapper-asl &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.9.2&quot;&lt;/span&gt;]
                       [&lt;span style=&quot;color: #b58900;&quot;&gt;org.slf4j&lt;/span&gt;/jcl104-over-slf4j &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.4.3&quot;&lt;/span&gt;]
                       [&lt;span style=&quot;color: #b58900;&quot;&gt;org.slf4j&lt;/span&gt;/slf4j-log4j12 &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;1.4.3&quot;&lt;/span&gt;]]
    &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:main&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;crawl-log-loader.core&lt;/span&gt;)
&lt;/pre&gt;


&lt;p&gt;
We’re defining our dependencies on Clojure, the HBase library and the
Hadoop library. We need the Clojure “tools.logging” library so that we
can log message while our Hadoop job is running. I like to include
“tools.cli” so I can look up documentation for functions from the REPL
while I work. Lastly, the Hadoop libraries depend on Jackson and
SLF4J; they’ll be present on our Hadoop cluster but we’ll need them
around in order to build our application.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-2&quot;&gt;Use, Require and Import&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-2&quot;&gt;


&lt;p&gt;
With the project setup, it’s time to add some code. Open up the core
file (src/crawl_log_loader/core.clj), and add something like this&amp;hellip;
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;ns&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;crawl-log-loader.core&lt;/span&gt;
  (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:use&lt;/span&gt; [&lt;span style=&quot;color: #b58900;&quot;&gt;clojure.repl&lt;/span&gt;]
        [&lt;span style=&quot;color: #b58900;&quot;&gt;clojure.tools.logging&lt;/span&gt;]
        [&lt;span style=&quot;color: #b58900;&quot;&gt;clojure.tools.cli&lt;/span&gt;])
  (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:require&lt;/span&gt; [&lt;span style=&quot;color: #b58900;&quot;&gt;clojure.string&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:as&lt;/span&gt; string]
            [&lt;span style=&quot;color: #b58900;&quot;&gt;clojure-hadoop.gen&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:as&lt;/span&gt; gen]
            [&lt;span style=&quot;color: #b58900;&quot;&gt;clojure-hadoop.imports&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:as&lt;/span&gt; imp]
            [&lt;span style=&quot;color: #b58900;&quot;&gt;clojure-hbase.core&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:as&lt;/span&gt; hb])
  (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:import&lt;/span&gt; [&lt;span style=&quot;color: #b58900;&quot;&gt;org.apache.hadoop.util&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Tool&lt;/span&gt;]
           [&lt;span style=&quot;color: #b58900;&quot;&gt;org.apache.hadoop.hbase.client&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Increment&lt;/span&gt;]
           [&lt;span style=&quot;color: #b58900;&quot;&gt;org.apache.commons.logging&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;LogFactory&lt;/span&gt;]
           [&lt;span style=&quot;color: #b58900;&quot;&gt;org.apache.commons.logging&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Log&lt;/span&gt;]
           [&lt;span style=&quot;color: #b58900;&quot;&gt;java.net&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;URL&lt;/span&gt;]
           [&lt;span style=&quot;color: #b58900;&quot;&gt;org.apache.hadoop.hbase.util&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Bytes&lt;/span&gt;]))
&lt;/pre&gt;


&lt;p&gt;
I’ve added the Clojure REPL, logging and CLI tools to make it easier
to bootstrap your application and parse out command line
arguments. Since we’re parsing log files, we’ll need the Clojure
String library as well. After we pull in our libraries for Hadoop and
HBase, we import some of the Java classes we’ll need to make those
libraries work.
&lt;/p&gt;
&lt;p&gt;
I haven’t mentioned if before, but HBase stores everything as an array
of bytes. It has no notion of type, it just sees byte arrays. Our last
import provides a utility class that makes it easier for us to convert
objects like strings and numbers into byte arrays.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-3&quot;&gt;Define Our Hadoop Job&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-3&quot;&gt;


&lt;p&gt;
We’re going to do this a little backwards and add the function that
sets up the Hadoop job next. This function will reference our map and
reduce functions although we haven’t written those yet.
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;tool-run&lt;/span&gt;
  &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Provides the main function needed to bootstrap the Hadoop application.&quot;&lt;/span&gt;
  [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;color: #b58900;&quot;&gt;Tool&lt;/span&gt; this args-in]

  &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;define our command line flags and parse out the provided&lt;/span&gt;
  &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;arguments&lt;/span&gt;
  (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;let&lt;/span&gt; [[options args banner]
        (cli args-in
             [&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;-h&quot;&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;--help&quot;&lt;/span&gt;
              &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Show usage information&quot;&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:default&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;false&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:flag&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;true&lt;/span&gt;]
             [&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;-p&quot;&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;--path&quot;&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;HDFS path of data to consume&quot;&lt;/span&gt;]
             [&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;-o&quot;&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;--output&quot;&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;HDFS path for the output report&quot;&lt;/span&gt;])]

    &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;display the help message&lt;/span&gt;
    (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;if&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:help&lt;/span&gt; options)
      (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;do&lt;/span&gt; (println banner) 0)

      &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;setup and run our job&lt;/span&gt;
      (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;do&lt;/span&gt;
        (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;doto&lt;/span&gt; (&lt;span style=&quot;color: #b58900;&quot;&gt;Job.&lt;/span&gt;)
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.setJarByClass&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.getClass&lt;/span&gt; this))
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.setJobName&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;crawl-log-load&quot;&lt;/span&gt;)
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.setOutputKeyClass&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;Text&lt;/span&gt;)
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.setOutputValueClass&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;LongWritable&lt;/span&gt;)
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.setMapperClass&lt;/span&gt; (&lt;span style=&quot;color: #b58900;&quot;&gt;Class&lt;/span&gt;/&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;forName&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;crawl-log-loader.core_mapper&quot;&lt;/span&gt;))
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.setReducerClass&lt;/span&gt; (&lt;span style=&quot;color: #b58900;&quot;&gt;Class&lt;/span&gt;/&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;forName&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;crawl-log-loader.core_reducer&quot;&lt;/span&gt;))
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.setInputFormatClass&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;TextInputFormat&lt;/span&gt;)
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.setOutputFormatClass&lt;/span&gt; &lt;span style=&quot;color: #b58900;&quot;&gt;TextOutputFormat&lt;/span&gt;)
          (FileInputFormat/&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;setInputPaths&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:path&lt;/span&gt; options))
          (FileOutputFormat/&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;setOutputPath&lt;/span&gt; (&lt;span style=&quot;color: #b58900;&quot;&gt;Path.&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:output&lt;/span&gt; options)))
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.waitForCompletion&lt;/span&gt; &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;true&lt;/span&gt;))
          0))))
&lt;/pre&gt;


&lt;p&gt;
This article isn’t about parsing command line arguments, but the above
is a good habit to get into. We use the CLI library to both setup our
arguments and to parse those arguments out into a hash-map. More
information on how this library works &lt;a href=&quot;https://github.com/clojure/tools.cli&quot;&gt;can be found on the project’s page&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Hadoop wants our application to return a status code that indicates
healthy completion of the job or exiting under an error condition. We
return “0” to indicate that our job exited normally. In real life you
may want to do something more clever.
&lt;/p&gt;
&lt;p&gt;
If our app isn’t invoked with the “-h” or “&amp;ndash;help” flag, we setup the
Hadoop job. We create a new Job object and set a bunch of fields. Note
that we set the output key and value class. The main purpose of this
job is to load data into HBase but we’ll also output the number of
transactions per host. This could be used for any number of things,
perhaps we want to double-check the data stored in HBase.
&lt;/p&gt;
&lt;p&gt;
We set the mapper and reducer classes, we’ll write those up next. We
set the input and output formats; the &lt;a href=&quot;http://hadoop.apache.org/common/docs/current/api/org/apache/hadoop/mapreduce/lib/input/TextInputFormat.html&quot;&gt;TextInputFormat&lt;/a&gt; reads plain text
files line-by-line, a good fit our log input. The &lt;a href=&quot;http://hadoop.apache.org/common/docs/current/api/org/apache/hadoop/mapreduce/lib/output/TextOutputFormat.html&quot;&gt;TextOutputFormat&lt;/a&gt;
writes plain text files.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-4&quot;&gt;Mapping Function&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-4&quot;&gt;


&lt;p&gt;
We’ll now add our mapping function. Make sure that you add your own
mapping function &lt;i&gt;above&lt;/i&gt; the definition of your “tool-run” function.
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;mapper-map&lt;/span&gt;
  &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Provides the function for handling our map task. We parse the data,&lt;/span&gt;
&lt;span style=&quot;color: #2aa198;&quot;&gt;  apply it to HBase and then write out the host and 1. This output is&lt;/span&gt;
&lt;span style=&quot;color: #2aa198;&quot;&gt;  used to provide a summary report that details the number of URLs&lt;/span&gt;
&lt;span style=&quot;color: #2aa198;&quot;&gt;  logged per host.&quot;&lt;/span&gt;
  [this key value &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;color: #b58900;&quot;&gt;MapContext&lt;/span&gt; context]

  &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;parse the data&lt;/span&gt;
  (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;let&lt;/span&gt; [parsed-data (parse-data value)]

          &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;apply the data to HBase&lt;/span&gt;
          (process-log-data parsed-data)

          &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;write our counter for our reduce task&lt;/span&gt;
          (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.write&lt;/span&gt; context
                  (&lt;span style=&quot;color: #b58900;&quot;&gt;Text.&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:host&lt;/span&gt; parsed-data))
                  (&lt;span style=&quot;color: #b58900;&quot;&gt;LongWritable.&lt;/span&gt; 1))))
&lt;/pre&gt;


&lt;p&gt;
This isn’t so tricky! We read in a key and a value, they key will be
the line number of the file being processed and the value will be the
text of that line (a log entry). We don’t really care which line of
what file this entry came from so we ignore it. Then we parse the line
of log data into a hash-map, apply that data to HBase with our
“process-log-data” function (yet unwritten) and then write out data
for our final report.
&lt;/p&gt;
&lt;p&gt;
We’re writing out the host for the URL that was crawled in this log
entry and the number 1. During the reduce phase we’ll sum the values
for each host and output the total number of transactions. In fact,
let’s do that right now.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-5&quot;&gt;Reduce Function&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-5&quot;&gt;





&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;reducer-reduce&lt;/span&gt;
  &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Provides the function for our reduce task. We sum the values for&lt;/span&gt;
&lt;span style=&quot;color: #2aa198;&quot;&gt;  each host yeilding the number of URLs logged per host.&quot;&lt;/span&gt;
  [this key values &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;color: #b58900;&quot;&gt;ReduceContext&lt;/span&gt; context]

  &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;sum the values for each host&lt;/span&gt;
  (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;let&lt;/span&gt; [sum (reduce + (map (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;fn&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;color: #b58900;&quot;&gt;LongWritable&lt;/span&gt; value]
                             (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.get&lt;/span&gt; value))
                           values))]

    &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;write out the total&lt;/span&gt;
    (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.write&lt;/span&gt; context key (&lt;span style=&quot;color: #b58900;&quot;&gt;LongWritable.&lt;/span&gt; sum))))
&lt;/pre&gt;


&lt;p&gt;
Again, this function isn’t scary at all. We map over the incoming
values (Hadoop wraps the number in a LongWritable instance), pull out
the actual values and reduce those values into our final sum. We write
out the key, which is the name of the host and the sum, the total
number of transactions for this host.
&lt;/p&gt;
&lt;p&gt;
We’re nearly through, the last function is the bit that applies our
data to HBase.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5-6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-5-6&quot;&gt;Load Data into HBase&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-5-6&quot;&gt;


&lt;p&gt;
We need to parse out our line of log data into something easier to
work with, a hash-map. My files are separated with spaces making this
very easy.
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;parse-data&lt;/span&gt;
  &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Parses a String representing a row of data from an ESP crawler log&lt;/span&gt;
&lt;span style=&quot;color: #2aa198;&quot;&gt;  into a hash-map of data.&quot;&lt;/span&gt;
  [text]

  &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;parse out the row of data&lt;/span&gt;
  (&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;let&lt;/span&gt; [data (&lt;span style=&quot;color: #b58900;&quot;&gt;string&lt;/span&gt;/split (str text) #&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;\s+&quot;&lt;/span&gt;)]

    &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;return a map of data&lt;/span&gt;
    {&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:timestamp&lt;/span&gt; (nth data 0)
     &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:reponse&lt;/span&gt; (nth data 1)
     &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:crawler-response&lt;/span&gt; (nth data 2)
     &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:url&lt;/span&gt; (nth data 3)
     &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:host&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.getHost&lt;/span&gt; (&lt;span style=&quot;color: #b58900;&quot;&gt;URL.&lt;/span&gt; (nth data 3)))}))
&lt;/pre&gt;


&lt;p&gt;
This is a bit simplistic, but you get the idea. In practice you’d
probably want to be more careful and make sure the input data is
valid. You’d likely want to split the date and time out or even parse
it back into a real date instance.
&lt;/p&gt;
&lt;p&gt;
Lastly, we want to add this row to HBase and update some counters.
&lt;/p&gt;



&lt;pre class=&quot;src src-clojure&quot;&gt;(&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;process-log-data&lt;/span&gt;
  &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;Handles the processing of log data by applying the map of data to&lt;/span&gt;
&lt;span style=&quot;color: #2aa198;&quot;&gt;  the proper counters and adding the correct rows to our HBase&lt;/span&gt;
&lt;span style=&quot;color: #2aa198;&quot;&gt;  tables.&quot;&lt;/span&gt;
  [parsed-data]

  &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;add our row of data&lt;/span&gt;
  (&lt;span style=&quot;color: #b58900;&quot;&gt;hb&lt;/span&gt;/with-table [urls (&lt;span style=&quot;color: #b58900;&quot;&gt;hb&lt;/span&gt;/table &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;urls&quot;&lt;/span&gt;)]
    (&lt;span style=&quot;color: #b58900;&quot;&gt;hb&lt;/span&gt;/put urls
            (str (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:url&lt;/span&gt; parsed-data)
                 (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:timestamp&lt;/span&gt; parsed-data))
            &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:values&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:request&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:url&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:url&lt;/span&gt; parsed-data)]]
            &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:values&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:request&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:host&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:host&lt;/span&gt; parsed-data)]]
            &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:values&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:transaction&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:timestamp&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:timestamp&lt;/span&gt; parsed-data)]]
            &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:values&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:response&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:http&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:response&lt;/span&gt; parsed-data)]]
            &lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:values&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:response&lt;/span&gt; [&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:crawler&lt;/span&gt; (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:crawler-response&lt;/span&gt; parsed-data)]]))

  &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;update our host stats table&lt;/span&gt;
  (&lt;span style=&quot;color: #b58900;&quot;&gt;hb&lt;/span&gt;/with-table [host-stats (&lt;span style=&quot;color: #b58900;&quot;&gt;hb&lt;/span&gt;/table &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;host-stats&quot;&lt;/span&gt;)]
    (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.incrementColumnValue&lt;/span&gt; host-stats
                           (&lt;span style=&quot;color: #b58900;&quot;&gt;hb&lt;/span&gt;/to-bytes (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;:host&lt;/span&gt; parsed-data))
                           (&lt;span style=&quot;color: #b58900;&quot;&gt;hb&lt;/span&gt;/to-bytes &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;transactions&quot;&lt;/span&gt;)
                           (&lt;span style=&quot;color: #b58900;&quot;&gt;hb&lt;/span&gt;/to-bytes &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;total&quot;&lt;/span&gt;)
                           (&lt;span style=&quot;color: #268bd2; font-weight: bold;&quot;&gt;.longValue&lt;/span&gt; 1))))
&lt;/pre&gt;


&lt;p&gt;
We’re keeping this simple so that you get an idea of how this
works. We add the row of data to our “urls” HBase table, then we
increment to total number of transactions for this host in the
“host-stats” table. We don’t have to worry if there’s already a row in
the “host-stats” table for this host, if there isn’t then HBase will
create a new row with a value of zero and then increment it by our
supplied value, 1.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-6&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-6&quot;&gt;Package and Deploy&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-6&quot;&gt;


&lt;p&gt;
Deployment is straightforward, create an “uberjar” with Leiningen and
then copy that out to your Hadoop cluster. From there you can invoke
the JAR with an input and output path.
&lt;/p&gt;



&lt;pre class=&quot;src src-sh&quot;&gt;lein uberjar
...watch Leiningen work...

scp crawl-log-loader-1.0-SNAPSHOT-standalone.jar hadoop1.local:/hadoop

ssh hadoop1.local
...log into your Hadoop node
&lt;/pre&gt;


&lt;p&gt;
And then run your job. It turns out this is trickier than you’d
think. If you simply invoke the JAR with the “hadoop” command it will
run on the local job runner (it won’t run distributed across the
cluster) because it won’t be able to find your HBase install. To fix
this, create a new folder in your project directory called
“resources”. Files in this folder will be bundled up by Leiningen into
your final JAR and they’ll be present on the class-path when the
application is launched.
&lt;/p&gt;
&lt;p&gt;
Next, create a file called “hbase-site.xml” in this folder. It should
look something like this:
&lt;/p&gt;



&lt;pre class=&quot;src src-xml&quot;&gt;&amp;lt;?&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;xml&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;version&lt;/span&gt;=&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;&lt;/span&gt;?&amp;gt;
&amp;lt;?&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;xml-stylesheet&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;type=&quot;text/xsl&quot; href=&quot;configuration.xsl&quot;&lt;/span&gt;?&amp;gt;
&amp;lt;&lt;span style=&quot;color: #268bd2;&quot;&gt;configuration&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span style=&quot;color: #268bd2;&quot;&gt;property&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&quot;color: #268bd2;&quot;&gt;name&lt;/span&gt;&amp;gt;hbase.zookeeper.quorum&amp;lt;/&lt;span style=&quot;color: #268bd2;&quot;&gt;name&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style=&quot;color: #268bd2;&quot;&gt;value&lt;/span&gt;&amp;gt;hadoop1.local&amp;lt;/&lt;span style=&quot;color: #268bd2;&quot;&gt;value&lt;/span&gt;&amp;gt;
  &amp;lt;/&lt;span style=&quot;color: #268bd2;&quot;&gt;property&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span style=&quot;color: #268bd2;&quot;&gt;configuration&lt;/span&gt;&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
The Zookeeper Quorum value should list all of your Zookeeper nodes, if
you’re running in standalone development mode then there’s just the
one. If you are running in production this should be a comma separated
list of host names. Lastly, you can use a script similar to the
following to start your job.
&lt;/p&gt;



&lt;pre class=&quot;src src-sh&quot;&gt;&lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;export&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;HBASE_HOME&lt;/span&gt;=/hbase
&lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;export&lt;/span&gt; &lt;span style=&quot;color: #268bd2;&quot;&gt;HADOOP_HOME&lt;/span&gt;=/hadoop

&lt;span style=&quot;color: #268bd2;&quot;&gt;HADOOP_CLASSPATH&lt;/span&gt;=&lt;span style=&quot;color: #6c71c4; font-weight: bold;&quot;&gt;`${HBASE_HOME}/bin/hbase classpath`&lt;/span&gt; &lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;\&lt;/span&gt;
  ${&lt;span style=&quot;color: #268bd2;&quot;&gt;HADOOP_HOME&lt;/span&gt;}/bin/hadoop jar crawl-log-loader-1.0-SNAPSHOT....jar &lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;\&lt;/span&gt;
  -p hdfs://hadoop1.local:54310/data/in &lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;\&lt;/span&gt;
  -o hdfs://hadoop1.local:54310/data/out
&lt;/pre&gt;


&lt;p&gt;
This sets up environment variables that point to our HBase and Hadoop
installations. We then load our application in the context of our
Hadoop instance and add our HBase installation to the class path. The
“-p” and “-o” flags at the end are interpreted by our application.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-7&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-7&quot;&gt;For Great Justice&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-7&quot;&gt;


&lt;p&gt;
While this isn’t production code it should be enough to get you
started. We now have some data loaded and that will make it a lot
easier to explore HBase and evaluate the product. Once again, I am
amazed at how easy it is to work with these frameworks using Clojure!
A big thanks goes out to the developers and maintainers of the Hadoop
and HBase libraries that make this all so easy, as well as the
developers and maintainers of Hadoop, HBase and Clojure themselves.
&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Java Doesn't Need to Be So Bad</title>
   <link href="http:///posts/java-patterns/"/>
   <updated>2011-12-04T00:00:00-05:00</updated>
   <id>http:///posts/java-patterns</id>
   <content type="html">&lt;p&gt;
I do a lot of Java coding and I enjoy it. I admit that there is a lot
of typing, often a lot of boilerplate and getting even simple tasks
done can involve too much work. Most of the tools that try to fix
these problems trade one moment saved for another lost. Maven&apos;s XML
based configuration file is a good example: Thank you for making my
project easier to manage and I won&apos;t forget that you made me edit XML
to do so.
&lt;/p&gt;
&lt;p&gt;
These are the things that you live with, these are the things you
trade for using a language that thinks it&apos;s finishing what C++
started; a language that is loved equally by mammoth corporate IT
departments, mammoth corporate IT vendors and mammoth corporate IT
consultants. What you don&apos;t need to tolerate is this tendency to
crazily over-engineer even the simplest solutions.
&lt;/p&gt;

&lt;div id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-1&quot;&gt;We&apos;ve all Seen It: The Factory Factory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2&quot;&gt;I Point the Finger at You, I Have Already Pointed it At Myself&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3&quot;&gt;Premature Optimization and Re-factoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4&quot;&gt;Moderation in All Things&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;We&apos;ve all Seen It: The Factory Factory&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;


&lt;p&gt;
It&apos;s a long-standing joke because we&apos;ve all seen it and we&apos;ve all
grown to hate it: &quot;The Abstract Factory Pattern,&quot; euphemistically
referred to as &quot;The Factory Factory&quot;. As envisioned by &lt;a href=&quot;http://en.wikipedia.org/wiki/Design_Patterns&quot;&gt;the Gang of Four&lt;/a&gt;, this is a handy way to create families of related objects
without needing to know much about their concrete
implementations. It&apos;s a clever idea and when we see it used in a
library or if we end up coding something like this ourselves, our
reaction should be &quot;Yes, very clever!&quot; So why are we so often
complaining?
&lt;/p&gt;
&lt;p&gt;
I think we&apos;re having reactions like this because when we look at an
API that does this, it&apos;s often far too complex; it&apos;s overkill. If
you&apos;re using a library and you are noticing this pattern, as often as
not it turns out that the author of said library is doing it wrong:
there&apos;s a factory that returns a factory that returns one
implementation. One factory is a bad sign, that the one factory has
only one implementation to build tells us that this pattern isn&apos;t
applicable. If you are working with a library that uses this pattern
and it&apos;s not saving you a headache by hiding some obnoxious suite of
not quite interchangeable objects, then this pattern is wasting your
time and effort for absolutely no reward.
&lt;/p&gt;
&lt;p&gt;
In case that you don&apos;t do a lot of Java coding and have no idea what
I&apos;m talking about, take a look at this bit of Java code that does just
this: &lt;a href=&quot;http://sourcemaking.com/design_patterns/abstract_factory/java/2&quot;&gt;one abstract factory extended by two implementation factories&lt;/a&gt;. I&apos;d have written example code myself but even that would be
too much work. Note how silly and pointless this example is: it needs
to be that way because if it were more true-to-life, you&apos;d have a
really hard time following it. &lt;a href=&quot;http://www.apwebco.com/gofpatterns/creational/AbstractFactory.html&quot;&gt;Here&apos;s a more real-life example&lt;/a&gt;, notice
how boring and co-dependent these classes are; this is the kind of
problem this pattern is trying to solve.
&lt;/p&gt;
&lt;p&gt;
I blame Sun, I blame your boss&apos;s boss, I blame unimaginative managers
everywhere who looked at Sun&apos;s example code and thought &quot;Yes! Our code
needs to look like this too!&quot; Because it doesn&apos;t and 99% of the time
it really shouldn&apos;t. If you&apos;re starting a new project and you decide
that the first step is to draw a really big UML diagram and then try
to isolate key places to implement this or that design pattern then
you, my friend, are also part of the problem.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-2&quot;&gt;I Point the Finger at You, I Have Already Pointed it At Myself&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-2&quot;&gt;


&lt;p&gt;
It could be that everyone my age, or everyone who had a similar career
path, or everyone that read documentation from Sun when they were too
young to know how horrible such documentation actually is have all
ended up making these same mistakes. I toiled away on &lt;a href=&quot;http://twitch.posterous.com/java-swing-demonstration&quot;&gt;a project for four long years&lt;/a&gt; that heavily leveraged the now universally despised
&lt;a href=&quot;http://en.wikipedia.org/wiki/Enterprise_JavaBean&quot;&gt;EJB 2 specification&lt;/a&gt;. I myself have drawn out huge UML charts that
ended up being printed as a shockingly large sheaf of pages that were
then taped to a whiteboard. I personally have to live with the shame
of a very clear and vivid memory wherein I circled objects that
covered nearly four sheets of this paper and wrote, in my own hand and
with a clear-head: &quot;A good candidate for an abstract factory! :)&quot;
&lt;/p&gt;
&lt;p&gt;
Eventually I realized that the emoticon was mocking me. Laughing at my
presumptuousness, laughing at my premature optimization. Laughing at
my fear of re-factoring.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-3&quot;&gt;Premature Optimization and Re-factoring&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-3&quot;&gt;


&lt;p&gt;
That&apos;s what I was doing, I was trying to solve a problem before I even
had it. Before I had even written one line of code, I was trying to
decide how the pieces of the puzzle fit together. It was simply too
much detail too early on. No one ever gets it right the first time,
what made me think that I was so special?
&lt;/p&gt;
&lt;p&gt;
I&apos;m not saying it&apos;s not worth it to think ahead and to plan, but be
mindful that no one has perfect knowledge of their problem space and
just as things that seem simple can turn out to be complicated,
sometimes problems that seem really hard end up solving
themselves. Maybe that&apos;s just how business logic works: you talk to
the right person and it turns out that you had it wrong the whole
time.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-4&quot;&gt;Moderation in All Things&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-4&quot;&gt;


&lt;p&gt;
I&apos;m advocating an approach where you&apos;re mindful of these design
patterns but you keep them in the background. When you have &lt;i&gt;working&lt;/i&gt;
code that is starting to get messy, or when someone on your team has
been banging their head on a problem for way too long, that&apos;s when you
take out the patterns book. Because at that point you have a pretty
good idea of what the problem &lt;i&gt;actually&lt;/i&gt; is. You even have a solution
that&apos;s mostly working and that solution can be re-factored to fit the
pattern that seems most appropriate.
&lt;/p&gt;
&lt;p&gt;
You probably won&apos;t ever end up with a factory that creates a factory
that creates this one object that people only ever use once because
that is plainly wrong.  But if you ever do code up an abstract
factory, when people end up interacting with your API and they whine
about the factory factory you can confidently tell them that the
alternative is much, much worse and show them how you&apos;ve actually made
their life much easier.
&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Blogging With Emacs</title>
   <link href="http:///posts/blogging_with_emacs/"/>
   <updated>2011-10-09T00:00:00-04:00</updated>
   <id>http:///posts/blogging_with_emacs</id>
   <content type="html">&lt;p&gt;
I don&apos;t want to waste time explaining why I use Emacs all day, every
day, and eschew more traditional development environments. Either you
already use Emacs and I&apos;m preaching to the choir or you don&apos;t and are
very unlikely to read this article. I will keep it short: Emacs is the
easiest for me to customize, changing the way something works often
requires nothing more than adding a couple lines of code and then the
thing does what I want. It&apos;s like an amorphous blob that needs to be
shaped into the tool that you need but once so formed, that tool is
thereafter only one click away.
&lt;/p&gt;

&lt;div id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-1&quot;&gt;Org Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-2&quot;&gt;The Workflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3&quot;&gt;Setting Up a Publishing Project&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-1&quot;&gt;Setup your Weblog Directory and Jekyll Template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-3-2&quot;&gt;Setup your Org Mode Publishing Project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-4&quot;&gt;Writing Your First Article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-5&quot;&gt;Publish Your Weblog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sec-6&quot;&gt;Greatness Is Now Expected&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Org Mode&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;




&lt;a href=&quot;/images/org-mode-screenshot.png&quot; rel=&quot;lightbox&quot;
  title=&quot;Org Mode Screenshot&quot;&gt;
  &lt;img src=&quot;/images/org-mode-screenshot.png&quot; class=&quot;float_right&quot;/&gt;
&lt;/a&gt;

&lt;p&gt;
I use &lt;a href=&quot;http://orgmode.org/&quot;&gt;Org Mode&lt;/a&gt; when authoring all of my posts. I let Org Mode handle
exporting my completed posts to HTML, if you want to follow along at
home you&apos;ll need to have Org-Mode installed and be fairly comfortable
using it.
&lt;/p&gt;
&lt;p&gt;
If you use Emacs for any sort of meaningful work and you&apos;re not
actively using Org Mode, you need to revisit this as soon as humanly
possible. It&apos;s conceivable you have your own system, carefully crafted
and then fine-tuned over the years into a razor sharp knife that can
disembowel the dreary red-taped monster of your work-life, spilling
out the most crucial and important information onto the floor so that
you can nudge and prod it with your booted foot, but I sincerely doubt
it. It&apos;s more likely that you have information spread all over your
desk, your laptop, your office, perhaps your office floor. Post-it
notes, the back of errant print-outs so lengthy that you feel guilty
eventually (and inevitably) dropping them in the recycling bin,
well-meaning &quot;Get Things Done&quot; type applications that strand your
critical data on attractive yet inaccessible prison islands. Hear me
now: stop whatever your doing and setup Org Mode, then take the time
over the next couple of days to migrate all of your to-do items and
notes. Trust me, it&apos;s the right thing to do.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-2&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-2&quot;&gt;The Workflow&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-2&quot;&gt;


&lt;p&gt;
Org Mode provides the ability to export your files one at a time, but
you can also export them &lt;a href=&quot;http://orgmode.org/manual/Publishing.html#Publishing&quot;&gt;as an entire project&lt;/a&gt;. The entirety of my
weblog is contained in one project, once I&apos;ve completed a new article
and edited it to my satisfaction I simply export the &quot;weblog&quot; project
to the directory that contains my completed blog.
&lt;/p&gt;
&lt;p&gt;
That brings us to the second component of this solution, &lt;a href=&quot;https://github.com/mojombo/jekyll/wiki&quot;&gt;Jekyll&lt;/a&gt;. With
Jekyll, we can take a directory of articles and transmute them into a
complete and cohesive website. Jekyll will scan through the posts and
construct the website, ready to publish, in a sub-directory called
&quot;_site&quot;.
&lt;/p&gt;
&lt;p&gt;
Lastly, with the completed website in hand, you need to get it out
there on the internet. The simplest solution would be an &lt;a href=&quot;http://en.wikipedia.org/wiki/Rsync&quot;&gt;Rsync&lt;/a&gt; script
to copy new files out to your web space.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-3&quot;&gt;Setting Up a Publishing Project&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-3&quot;&gt;


&lt;p&gt;
Before we go any further, I need to let you know that this is not a
trivial task. It&apos;s not as bad as, say, setting up a new web-server
from scratch but it&apos;s definitely more involved then simply creating a
new account at &lt;a href=&quot;http://wordpress.com&quot;&gt;Wordpress.com&lt;/a&gt;. I&apos;ve been operating under the
presumption that you take your blogging much too seriously to be
satisfied with banging the text into the kind of brain-dead web-form
served up by most weblog products. If this is not the case, please
forgive the misunderstanding and be on your way.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-1&quot;&gt;Setup your Weblog Directory and Jekyll Template&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-1&quot;&gt;


&lt;p&gt;
The first step is to setup a directory where you will store your
weblog. I store mine in the root of my home directory in the aptly
named &quot;weblog&quot; directory. You are free to use the same name and
location.
&lt;/p&gt;
&lt;p&gt;
Jekyll reads through the contents of a folder called &quot;jekyll&quot; for
information about your blog, templates, and various bits of
information. It&apos;s possible to start with a nearly empty &quot;jekyll&quot;
directory but that is absolutely no fun. Instead we&apos;ll use a template
that &lt;a href=&quot;https://github.com/christianhellsten/jekyll-template/zipball/master&quot;&gt;Christian Hellsten&lt;/a&gt; has put together. Download the ZIP archive of
the project from the following link:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/christianhellsten/jekyll-template/zipball/master&quot;&gt;https://github.com/christianhellsten/jekyll-template/zipball/master&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
Go ahead and unzip the archive, it&apos;ll be named something like
&quot;christian-hellsten-&amp;hellip;&quot; Rename the directory to &quot;jekyll&quot; and place
it in your weblog&apos;s directory.
&lt;/p&gt;
&lt;p&gt;
The next step is to create a folder where you&apos;ll store the Org Mode
files that contain your posts and the contents of the various
pages. Create another directory in your weblog folder and call it
&quot;org&quot;.
&lt;/p&gt;
&lt;p&gt;
Lastly, you&apos;ll want to customize the &quot;_config.yml&quot; file in your brand
new &quot;jekyll&quot; directory. Leave the &quot;url&quot; alone for now but go ahead
and change the title, description, etc.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-3-2&quot;&gt;Setup your Org Mode Publishing Project&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-3-2&quot;&gt;


&lt;p&gt;
In addition to doing very nearly everything it can to help you
organize your notes and tasks and generally keeping you on top of the
massive, sprawling mess that is your life, Org Mode can &lt;span style=&quot;text-decoration:underline;&quot;&gt;also&lt;/span&gt; export
files as HTML. You can do this file-by-file as the need arises or, and
this is what we&apos;ll be doing, you can setup a project and export the
entire thing all at once.
&lt;/p&gt;
&lt;p&gt;
Open up your appropriate Emacs configuration file (you do have more
than one, don&apos;t you?) and add the following stanza to the end.
&lt;/p&gt;



&lt;pre class=&quot;src src-elisp&quot;&gt;(setq org-publish-project-alist
      &apos;((&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;org-weblog&quot;&lt;/span&gt;
         &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;Path to your org files.&lt;/span&gt;
         &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:base-directory&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;~/weblog/org/&quot;&lt;/span&gt;
         &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:base-extension&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;org&quot;&lt;/span&gt;

         &lt;span style=&quot;color: #586e75;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #586e75;&quot;&gt;Path to your Jekyll project.&lt;/span&gt;
         &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:publishing-directory&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;~/weblog/jekyll/&quot;&lt;/span&gt;
         &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:recursive&lt;/span&gt; t
         &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:publishing-function&lt;/span&gt; org-publish-org-to-html
         &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:headline-levels&lt;/span&gt; 4
         &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:html-extension&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;html&quot;&lt;/span&gt;
         &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:body-only&lt;/span&gt; t)

        (&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;org-static-weblog&quot;&lt;/span&gt;
          &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:base-directory&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;~/weblog/org/&quot;&lt;/span&gt;
          &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:base-extension&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;png&lt;/span&gt;&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;jpg&lt;/span&gt;&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;gif&lt;/span&gt;&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;pdf&lt;/span&gt;&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;mp3&lt;/span&gt;&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;ogg&lt;/span&gt;&lt;span style=&quot;color: #859900; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #b58900; font-weight: bold;&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;swf&quot;&lt;/span&gt;
          &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:publishing-directory&lt;/span&gt; &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;~/weblog/jekyll/attachments&quot;&lt;/span&gt;
          &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:recursive&lt;/span&gt; t
          &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:publishing-function&lt;/span&gt; org-publish-attachment)

        (&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;weblog&quot;&lt;/span&gt; &lt;span style=&quot;color: #839496; font-weight: bold;&quot;&gt;:components&lt;/span&gt; (&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;org-weblog&quot;&lt;/span&gt;
                               &lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;org-static-weblog&quot;&lt;/span&gt;))))
&lt;/pre&gt;


&lt;p&gt;
This describes two tasks, one will use Org Mode&apos;s HTML extension to
publish all of your &quot;.org&quot; files to the &quot;jekyll&quot; directory. It will
only write the body of these files, they won&apos;t be complete HTML
pages. This is what we want, Jekyll will template these pages and wrap
them up into final, web-ready content.
&lt;/p&gt;
&lt;p&gt;
The second task moves any other files that we might have out to the
&quot;jekyll&quot; folder. This includes things like pictures or charts or
whatever content you&apos;ve linked into your Org Mode files. Ideally these
links will work when they come out the other side of the content
grinder that is your website but your mileage may vary.
&lt;/p&gt;
&lt;p&gt;
With this in place, evaluate that function so that Emacs is ready to
publish your brand new site.
&lt;/p&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-3-2-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-3-2-1&quot;&gt;Note: An Odd Issue&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-3-2-1&quot;&gt;


&lt;p&gt;
Recently, for now reason I can discern, publishing my weblog project
would fail after attempting to export the first page to HTML. Org Mode
would export the file to HTML and then copy the exported text to the
clipboard, this should be text but it wasn&apos;t and this was breaking the
project export process.
&lt;/p&gt;
&lt;p&gt;
Adding the following line to my Org Mode setup fixed this issue. What
would I do with that buffer of HTML anyway?
&lt;/p&gt;



&lt;pre class=&quot;src src-elisp&quot;&gt;(setq org-export-copy-to-kill-ring nil)
&lt;/pre&gt;


&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-4&quot;&gt;Writing Your First Article&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-4&quot;&gt;


&lt;p&gt;
The stage is now set for your new adventure in authoring. In real life
it will likely take days and days of hand-wringing and second guessing
just to get a rough draft out and that will inevitably be followed by
another several days of tireless editing and pointless yak-shaving
while you get it &quot;just right&quot;. We&apos;ll be putting such gritty reality
aside for now and, instead, writing up a test article completely
devoid of any useful information.
&lt;/p&gt;
&lt;p&gt;
Naturally we need a place to store your articles. Create a new
directory called &quot;_posts&quot; in the &quot;org&quot; folder that lives in your
&quot;weblog&quot; directory. Pull up Emacs and create a new file called
something like &quot;2011-10-09-test_blog_article.org&quot;. At this point
you&apos;ll have a new, pristine Org Mode buffer spread before you.
&lt;/p&gt;



&lt;pre class=&quot;src src-&quot;none&quot;lang&quot;none&quot;flags&quot;&gt;&lt;span style=&quot;color: #2aa198;&quot;&gt;&quot;none&quot;&lt;/span&gt;body
&lt;/pre&gt;


&lt;p&gt;
Note that the title in the Org Mode header is just for you, Org Mode
and Emacs. Jekyll won&apos;t see it, it will ignore the entire Org Mode
header.
&lt;/p&gt;
&lt;p&gt;
The text in the first HTML stanza is used to tell Jekyll some of the
details about this post, this is called &quot;front matter&quot;. More
information on how this works is available on the &lt;a href=&quot;https://github.com/mojombo/jekyll/wiki&quot;&gt;Jekyll wiki&lt;/a&gt;. For
now, just paste this text in so we can move on.
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-5&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-5&quot;&gt;Publish Your Weblog&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-5&quot;&gt;


&lt;p&gt;
With your first article written, we&apos;re ready to publish your blog!
Invoke the &quot;org-publish&quot; function and Emacs will prompt you for the
name of the project to actually publish, type in your project name
(likely &quot;weblog&quot;) and press return. Lean back as Org Mode dutifully
exports all of your content to HTML.
&lt;/p&gt;
&lt;p&gt;
Now that the content has been converted from the versatile Org Mode
format into the unpleasantness that is HTML, you can turn your weblog
on locally to see what it looks like. Pull up the command prompt and
navigate to your weblog&apos;s &quot;jekyll&quot; directory and start-up your own
little web-server like so&amp;hellip;
&lt;/p&gt;



&lt;pre class=&quot;src src-sh&quot;&gt;rake server
&lt;/pre&gt;


&lt;p&gt;
Jekyll will be invoked and it will create an entire static site in the
&quot;_site&quot; folder. A web-server will be spun up to server those files and
you will be able to view your new blog at &lt;a href=&quot;http://localhost:4000&quot;&gt;http://localhost:4000&lt;/a&gt;. Well
done!
&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;div id=&quot;outline-container-6&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-6&quot;&gt;Greatness Is Now Expected&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-6&quot;&gt;


&lt;p&gt;
With the bare bones of your website now constructed, it is up to you
to generate the quality content that only you can generate. To get
your weblog up on the World Wide Web, you&apos;ll want to copy the contents
of the &quot;_site&quot; folder to the appropriate directory on your
web-server. You could setup something fancy like &lt;a href=&quot;http://navigium.grindcore.ch/2011/03/08/Blog-with-Jekyll.html&quot;&gt;getting it all in Git and using a commit hook&lt;/a&gt; or use rsync to copy the changed pages
out. It&apos;s up to you!
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 

</feed>
