<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christopher Miles</title>
    <description>The latest articles on DEV Community by Christopher Miles (@cmiles74).</description>
    <link>https://dev.to/cmiles74</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F128490%2F918e2fee-9017-4b2c-b92d-9f0f657cd209.jpeg</url>
      <title>DEV Community: Christopher Miles</title>
      <link>https://dev.to/cmiles74</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cmiles74"/>
    <language>en</language>
    <item>
      <title>PICO-8 Animation and Collision Detection</title>
      <dc:creator>Christopher Miles</dc:creator>
      <pubDate>Mon, 16 Jan 2023 02:58:43 +0000</pubDate>
      <link>https://dev.to/cmiles74/pico-8-animation-and-collision-detection-4kh6</link>
      <guid>https://dev.to/cmiles74/pico-8-animation-and-collision-detection-4kh6</guid>
      <description>&lt;p&gt;In &lt;a href="https://twitch.nervestaple.com/2023/01/07/pico8-intro/" rel="noopener noreferrer"&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;h2&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&gt;
  
  
  Create Our Tick Counter
&lt;/h3&gt;

&lt;p&gt;We're going to build on &lt;a href="https://twitch.nervestaple.com/2023/01/07/pico8-intro/" rel="noopener noreferrer"&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init_actors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;atk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;atx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
  &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;atk&lt;/code&gt; variable stands for "animation tick" and starts off at zero. The &lt;code&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update_anim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;atk&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;atk&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;atx&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;atk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fileh2cfu91ssyvmtvc8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fileh2cfu91ssyvmtvc8v.png" alt="More Sprites" width="800" height="795"&gt;&lt;/a&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&gt;init_actors()&lt;/code&gt; function to match the excerpt below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init_actors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;atk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;atx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt; 
  &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;alr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="n"&gt;aud&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've added two more variables to the &lt;code&gt;ply1&lt;/code&gt; table, &lt;code&gt;alr&lt;/code&gt; and &lt;code&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&gt;alr&lt;/code&gt; variable (animation left/right) references the first two sprites (0 and 1). The &lt;code&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&gt;ply1.spr&lt;/code&gt; variable that indicates which sprite to draw for the player.&lt;/p&gt;

&lt;h3&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update_ply1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;atk&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;atk&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aud&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aud&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&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&gt;ply.alr&lt;/code&gt; sprites if buttons 0 or 1 are pressed (left or right) and we use the &lt;code&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftwitch.nervestaple.com%2Fassets%2F2023-01-15-watch-the-player.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftwitch.nervestaple.com%2Fassets%2F2023-01-15-watch-the-player.gif" alt="Animated Player" width="844" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init_actors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;atk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;atx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
  &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;alr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="n"&gt;aud&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="n"&gt;hzd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;over&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&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&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&gt;draw_ply1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;draw_hzd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hzd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;hzd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;hzd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple right? We just draw the hazard on the screen. Update the &lt;code&gt;_draw&lt;/code&gt; function to call this one as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_draw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;draw_ply1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;draw_hzd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;collision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="n"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://pico-8.fandom.com/wiki/Abs" rel="noopener noreferrer"&gt;&lt;code&gt;abs&lt;/code&gt;&lt;/a&gt; function returns the "absolute value", the positive or negative information is discarded. For instance, &lt;code&gt;0 - 8 = -8&lt;/code&gt; but with &lt;code&gt;abs&lt;/code&gt; we just get back &lt;code&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&gt;update_ply1&lt;/code&gt; function. Go ahead and add the code below directly before the end of the function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;collision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;hzd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;over&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&gt;over&lt;/code&gt; flag to "true". This indicates the game has ended.&lt;/p&gt;

&lt;h3&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&gt;draw_hzd&lt;/code&gt;) that displays "Game Over" on the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;draw_over&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"game over"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&gt;update_ply1&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hsjpho53iqh0yv11h5q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hsjpho53iqh0yv11h5q.png" alt="Game Over" width="800" height="748"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdpt0ljyj1zkcweageux3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdpt0ljyj1zkcweageux3.png" alt="PICO-8 Cartridge" width="160" height="205"&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>pico8</category>
      <category>retrogaming</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Getting Started with PICO-8</title>
      <dc:creator>Christopher Miles</dc:creator>
      <pubDate>Sun, 08 Jan 2023 18:27:44 +0000</pubDate>
      <link>https://dev.to/cmiles74/getting-started-with-pico-8-4nla</link>
      <guid>https://dev.to/cmiles74/getting-started-with-pico-8-4nla</guid>
      <description>&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="https://pixelvision8.github.io/Website/" rel="noopener noreferrer"&gt;Pixel Vision 8&lt;/a&gt; and then saw the project archived, regrouped and decided to go with the  &lt;a href="https://www.lexaloffle.com/pico-8.php?page=faq" rel="noopener noreferrer"&gt;PICO-8&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;At first I started out working with the &lt;a href="https://pixelvision8.github.io/Website/" rel="noopener noreferrer"&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frny64w70l0srpa0r53ud.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frny64w70l0srpa0r53ud.jpg" alt="Snazzy Retro PC"&gt;&lt;/a&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&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="https://www.lexaloffle.com/pico-8.php?#getpico8" rel="noopener noreferrer"&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="https://www.humblebundle.com/" rel="noopener noreferrer"&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="https://gist.github.com/netvip3r/34a0f3519cfe3e0e7c44e8f1fcba76d4" rel="noopener noreferrer"&gt;create a "desktop" file&lt;/a&gt; to launch the application... Unless you are on Arch Linux, there is &lt;a href="https://aur.archlinux.org/packages/pico-8" rel="noopener noreferrer"&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5heih7d76kqi8qz5ixf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5heih7d76kqi8qz5ixf5.png" alt="PICO-8 Welcome"&gt;&lt;/a&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="https://www.lua.org/" rel="noopener noreferrer"&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xg54e1bgz1yr4ysmspt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xg54e1bgz1yr4ysmspt.png" alt="PICO-8 Editor Mode"&gt;&lt;/a&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="https://www.lexaloffle.com/dl/docs/pico-8_manual.html" rel="noopener noreferrer"&gt;the PICO-8 manual&lt;/a&gt;.&lt;/p&gt;

&lt;h2&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="https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" rel="noopener noreferrer"&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&gt;0&lt;/code&gt;, the one to the right of that would be &lt;code&gt;1&lt;/code&gt; and the one to the right of that is &lt;code&gt;2&lt;/code&gt; and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6h7jhf590epxq6jyj5yo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6h7jhf590epxq6jyj5yo.png" alt="Sprite Editor"&gt;&lt;/a&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&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&gt;
  
  
  The Game Loop
&lt;/h3&gt;

&lt;p&gt;The PICO-8 lets you write your game code with &lt;a href="https://www.lua.org/" rel="noopener noreferrer"&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

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

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

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

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


&lt;/code&gt;&lt;/pre&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="https://pico-8.fandom.com/wiki/Run" rel="noopener noreferrer"&gt;&lt;code&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="https://gameprogrammingpatterns.com/game-loop.html" rel="noopener noreferrer"&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&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init_actors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This code creates a new variable named &lt;code&gt;ply1&lt;/code&gt; that keeps track of all the data about our player: their coordinates on screen (&lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;) and the index of the sprite we're using to represent them (&lt;code&gt;spr&lt;/code&gt;). The data we've assigned to the player is called &lt;a href="https://www.lua.org/pil/2.5.html" rel="noopener noreferrer"&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;init_actors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;draw_ply1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;a href="https://pico-8.fandom.com/wiki/Spr" rel="noopener noreferrer"&gt;&lt;code&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_draw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;draw_ply1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpaq3d08a9zk7l5eiudyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpaq3d08a9zk7l5eiudyv.png" alt="Code Listing"&gt;&lt;/a&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&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update_ply1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;ply1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The PICO-8 has a &lt;a href="https://pico-8.fandom.com/wiki/Btn" rel="noopener noreferrer"&gt;&lt;code&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&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;update_ply1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&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="https://pico-8.fandom.com/wiki/Cls" rel="noopener noreferrer"&gt;&lt;code&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_draw&lt;/span&gt;
  &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;draw_ply1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&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="https://freepacman.org/" rel="noopener noreferrer"&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&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="https://pico-8.fandom.com/wiki/P8PNGFileFormat" rel="noopener noreferrer"&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="c1"&gt;--move the player&lt;/span&gt;
&lt;span class="c1"&gt;--cmiles74&lt;/span&gt;


&lt;/code&gt;&lt;/pre&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

save move-the-player.p8.png


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;div class="table-wrapper-paragraph"&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;/div&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F669wemysz9zv7935be5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F669wemysz9zv7935be5p.png" alt="PICO-8 Cartridge"&gt;&lt;/a&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&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="https://pico-8.fandom.com/wiki/Splore" rel="noopener noreferrer"&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;




</description>
      <category>pico8</category>
      <category>retrogaming</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Getting Started with Pixel Vision 8</title>
      <dc:creator>Christopher Miles</dc:creator>
      <pubDate>Fri, 09 Dec 2022 20:15:45 +0000</pubDate>
      <link>https://dev.to/cmiles74/getting-started-with-pixel-vision-8-1cmg</link>
      <guid>https://dev.to/cmiles74/getting-started-with-pixel-vision-8-1cmg</guid>
      <description>&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="https://pixelvision8.github.io/Website/" rel="noopener noreferrer"&gt;Pixel Vision 8&lt;/a&gt;!  My plan is to try an get some of the people at work interested so it's important that the console be free. 😉&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1siys2v22uxexetnzj4n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1siys2v22uxexetnzj4n.jpg" alt="Snazzy Retro PC"&gt;&lt;/a&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&gt;
  
  
  Install the Pixel Vision 8 Application
&lt;/h2&gt;

&lt;p&gt;Head over to the &lt;a href="https://pixelvision8.github.io/Website/" rel="noopener noreferrer"&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ye3bzrwe5rfx2tnlt09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ye3bzrwe5rfx2tnlt09.png" alt="PV8 Welcome"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty neat, eh? It kind of reminds me of the &lt;a href="https://www.gregdonner.org/workbench/wb_11.html" rel="noopener noreferrer"&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&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="https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" rel="noopener noreferrer"&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="https://github.com/PixelVision8/PixelVision8/wiki/sprites" rel="noopener noreferrer"&gt;Sprites in Pixel 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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1f1miewz9sy8obm52dp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1f1miewz9sy8obm52dp.png" alt="A Sprite"&gt;&lt;/a&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&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&gt;
  
  
  The Game Loop
&lt;/h3&gt;

&lt;p&gt;The Pixel Vision 8 lets you choose between using &lt;a href="https://www.lua.org/pil/contents.html" rel="noopener noreferrer"&gt;Lua&lt;/a&gt; or &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/" rel="noopener noreferrer"&gt;C#&lt;/a&gt;, for this project we'll be using &lt;a href="https://www.lua.org/pil/contents.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

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

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

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

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://gameprogrammingpatterns.com/game-loop.html" rel="noopener noreferrer"&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;InitActors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;posX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sprite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code creates a new variable named &lt;code&gt;player&lt;/code&gt; that keeps track of all the data about our player: their coordinates on screen (&lt;code&gt;posX&lt;/code&gt; and &lt;code&gt;posY&lt;/code&gt;) and the index of the sprite we're using to represent them (&lt;code&gt;sprite&lt;/code&gt;). The data we've assigned to the player is called &lt;a href="https://www.lua.org/pil/2.5.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;InitActors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DrawPlayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;DrawSprite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/PixelVision8/PixelVision8/wiki/api-draw-sprite" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;DrawPlayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3z8w9jqp9iu5zf9r54mw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3z8w9jqp9iu5zf9r54mw.png" alt="Code Listing"&gt;&lt;/a&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UpdatePlayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Buttons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InputState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posX&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Buttons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InputState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posX&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Buttons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InputState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posY&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Buttons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Up&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InputState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posY&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Pixel Vision 8 &lt;a href="https://github.com/PixelVision8/PixelVision8/wiki/api-button" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeDelta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;UpdatePlayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://github.com/PixelVision8/PixelVision8/wiki/api-clear" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;DrawPlayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://freepacman.org/" rel="noopener noreferrer"&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&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fq834apwc3lw0zvh425.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fq834apwc3lw0zvh425.png" alt="Update Game Info"&gt;&lt;/a&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&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6r5nlzsy0rzjzbo2tes5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6r5nlzsy0rzjzbo2tes5.png" alt="Your Sprite"&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>gamedev</category>
      <category>pixelvision8</category>
      <category>retrogaming</category>
    </item>
    <item>
      <title>Really Using Visual Studio Code Development Containers</title>
      <dc:creator>Christopher Miles</dc:creator>
      <pubDate>Sun, 26 Sep 2021 16:45:43 +0000</pubDate>
      <link>https://dev.to/cmiles74/really-using-visual-studio-development-containers-561e</link>
      <guid>https://dev.to/cmiles74/really-using-visual-studio-development-containers-561e</guid>
      <description>&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&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="https://code.visualstudio.com/" rel="noopener noreferrer"&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="https://code.visualstudio.com/docs/remote/create-dev-container#_set-up-a-folder-to-run-in-a-container" rel="noopener noreferrer"&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="https://code.visualstudio.com/learn/develop-cloud/containers" rel="noopener noreferrer"&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="https://docs.github.com/en/codespaces/getting-started/quickstart" rel="noopener noreferrer"&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="https://docs.docker.com/compose/" rel="noopener noreferrer"&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="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&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&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="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/install/ubuntu/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; (if you are on Linux) or &lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&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&gt;.devcontainer&lt;/code&gt; with a file called &lt;code&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&gt;devcontainer.json&lt;/code&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcr.microsoft.com/vscode/devcontainers/php:8-bullseye"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbgnv354viricefxh3rt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbgnv354viricefxh3rt.png" alt="Reload the sample project in Code"&gt;&lt;/a&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jsttxnlb873btbr5ret.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jsttxnlb873btbr5ret.png" alt="Install recommended extensions"&gt;&lt;/a&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3fw2fnmzabtup19c8m9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3fw2fnmzabtup19c8m9.png" alt="Reopen the project in a Docker container"&gt;&lt;/a&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&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbiaxig5kqvh8h6n45wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbiaxig5kqvh8h6n45wg.png" alt="Code builds and starts development container"&gt;&lt;/a&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm51vgysr3u366tte3awy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm51vgysr3u366tte3awy.png" alt="Code's Terminal opens session in the container"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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="https://flywaydb.org/" rel="noopener noreferrer"&gt;Flyway&lt;/a&gt; for database migrations and &lt;a href="https://httpie.io/" rel="noopener noreferrer"&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&gt;devcontainer.json&lt;/code&gt; file and replace it with a "dockerFile" attribute that points to our soon-to-be-created &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dockerFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dockerfile"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, create a new &lt;code&gt;Dockerfile&lt;/code&gt; next to your &lt;code&gt;devcontainer.json&lt;/code&gt; file and add the following recipe to it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/vscode/devcontainers/php:8-bullseye&lt;/span&gt;

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

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

&lt;span class="c"&gt;# install flyway&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/local&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&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="se"&gt;\
&lt;/span&gt;| &lt;span class="nb"&gt;tar &lt;/span&gt;xvz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/flyway-8.0.0-beta1/flyway /usr/local/bin


&lt;/code&gt;&lt;/pre&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&gt;apt update&lt;/code&gt; to pull down the latest list of packages for the distribution and then we use &lt;code&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsoct80kdwyc5j6np92p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsoct80kdwyc5j6np92p.png" alt="Code rebuilds the development container"&gt;&lt;/a&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjmdjpjqy3dmq6bbjeoc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjmdjpjqy3dmq6bbjeoc.png" alt="Verify our tools are installed"&gt;&lt;/a&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&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&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&gt;devcontainer.js&lt;/code&gt; file, remove the &lt;code&gt;dockerFile&lt;/code&gt; attribute and replace it with a &lt;code&gt;dockerComposeFile&lt;/code&gt; entry.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dockerComposeFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker-compose.yml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspace"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We point our devcontainer definition at our Docker Compose file, with the &lt;code&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&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&gt;docker-compose.yml&lt;/code&gt; right next to our devcontainer definition and add the following.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;..:/workspace&lt;/span&gt;
    &lt;span class="na"&gt;stdin_open&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;


&lt;/code&gt;&lt;/pre&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&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&gt;context&lt;/code&gt; attribute tells compose to look for files in the current directory, &lt;code&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&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&gt;.env&lt;/code&gt; next to your &lt;code&gt;docker-compose.yml&lt;/code&gt; file and add the text below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

DATABASE_HOST=mysql_db
DATABASE_USER=example_account
DATABASE_PASSWORD=applesauce
DATABASE_DB=example


&lt;/code&gt;&lt;/pre&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&gt;docker-compose.yml&lt;/code&gt; file to get our database server setup.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="na"&gt;mysql_db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql_db&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql/mysql-server:latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD=${DATABASE_PASSWORD}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_DATABASE=${DATABASE_DB}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_USER=${DATABASE_USER}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_PASSWORD=${DATABASE_PASSWORD}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&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&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&gt;devcontainer.json&lt;/code&gt; file and add the &lt;code&gt;extensons&lt;/code&gt; attribute listed below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dockerComposeFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker-compose.yml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"felixfbecker.php-pack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"cweijan.vscode-mysql-client2"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwpxn2feoehbx2j0oyk0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwpxn2feoehbx2j0oyk0.png" alt="Dev Container Extensions"&gt;&lt;/a&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjafgm307gc4zhwyw64y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjafgm307gc4zhwyw64y.png" alt="Connect to Database Server"&gt;&lt;/a&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="https://github.com/cmiles74/devcontainer-example" rel="noopener noreferrer"&gt;can checkout the full example project on GitHub&lt;/a&gt;.&lt;/p&gt;

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

&lt;h2&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="https://docs.github.com/en/codespaces/getting-started/quickstart" rel="noopener noreferrer"&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&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I encourage you to checkout the &lt;a href="https://github.com/cmiles74/devcontainer-example" rel="noopener noreferrer"&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;

&lt;h3&gt;
  
  
  Credits
&lt;/h3&gt;

&lt;p&gt;The banner image is &lt;a href="https://saveourseas.com/wp-content/uploads/2014/09/Website_Generic_Banner_06.jpg" rel="noopener noreferrer"&gt;from this website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>visualstudiocode</category>
      <category>devcontainer</category>
      <category>container</category>
      <category>devops</category>
    </item>
    <item>
      <title>Getting Started with Clojure, Even on Windows</title>
      <dc:creator>Christopher Miles</dc:creator>
      <pubDate>Thu, 29 Apr 2021 20:23:31 +0000</pubDate>
      <link>https://dev.to/cmiles74/getting-started-with-clojure-even-on-windows-9c</link>
      <guid>https://dev.to/cmiles74/getting-started-with-clojure-even-on-windows-9c</guid>
      <description>&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="https://www.surveymonkey.com/results/SM-S2L8NR6K9/" rel="noopener noreferrer"&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="https://en.wikipedia.org/wiki/F_Sharp_(programming_language)" rel="noopener noreferrer"&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="https://en.wikipedia.org/wiki/Clojure" rel="noopener noreferrer"&gt;Clojure&lt;/a&gt;, specifically when using Windows and we won't be using the &lt;a href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux" rel="noopener noreferrer"&gt;Windows subsystem for Linux (WSL)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtqx1hxs06rpv8xn1orp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtqx1hxs06rpv8xn1orp.png" alt="What is your primary development OS? Answer: Not Windows"&gt;&lt;/a&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. 😉&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&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&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&gt;
  
  
  Java
&lt;/h3&gt;

&lt;p&gt;Open up your preferred web browser and navigate to the &lt;a href="https://adoptopenjdk.net/" rel="noopener noreferrer"&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&gt;
  
  
  Leiningen
&lt;/h3&gt;

&lt;p&gt;Clojure projects are typically managed by a tool, we'll be using &lt;a href="https://leiningen.org/" rel="noopener noreferrer"&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&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&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="https://leiningen.org/" rel="noopener noreferrer"&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS &amp;gt; lein self-install
&lt;/code&gt;&lt;/pre&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Downloading Leiningen now...
&lt;/code&gt;&lt;/pre&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS&amp;gt; lein -version
Leiningen 2.9.6 on Java 11.0.11 OpenJDK 64-Bit Client VM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&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="https://code.visualstudio.com/download" rel="noopener noreferrer"&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&gt;
  
  
  Install the Calva Clojure Plugin
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=betterthantomorrow.calva" rel="noopener noreferrer"&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&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&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;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defproject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;org.clojure/clojure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"1.10.3"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;-main&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello!"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&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&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&gt;clj:tutorial:&amp;gt;&lt;/code&gt;). Type the following after the prompt and press return.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(-main)
&lt;/code&gt;&lt;/pre&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Jack-in done.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;clj&lt;/span&gt;&lt;span class="err"&gt;꞉&lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;&lt;span class="err"&gt;꞉&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;-main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Hello!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;clj&lt;/span&gt;&lt;span class="err"&gt;꞉&lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;&lt;span class="err"&gt;꞉&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  Working with Calva
&lt;/h4&gt;

&lt;p&gt;There's a good amount of documentation on the &lt;a href="https://calva.io/" rel="noopener noreferrer"&gt;Calva website&lt;/a&gt;, you should definitely check it out. There is a &lt;a href="https://calva.io/try-first/" rel="noopener noreferrer"&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&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&gt;(:gen-class)&lt;/code&gt; to the namespace declaration at the top, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:gen-class&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;-main&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello!"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\&amp;gt; lein uberjar
&lt;/code&gt;&lt;/pre&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\&amp;gt; java -jar target\tutorial-0.1-standalone.jar
&lt;/code&gt;&lt;/pre&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! 😏&lt;/p&gt;

</description>
      <category>clojure</category>
      <category>lisp</category>
      <category>windows</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Provision Pragmatically and Programmatically with CloudFormation: Part 3, Backup Jobs and Wrap-Up</title>
      <dc:creator>Christopher Miles</dc:creator>
      <pubDate>Sun, 21 Apr 2019 23:31:20 +0000</pubDate>
      <link>https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-backup-jobs-and-wrap-up-4plp</link>
      <guid>https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-backup-jobs-and-wrap-up-4plp</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--li3Odp1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/cmiles74/cloudformation-tutorial/master/template-diagram-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--li3Odp1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/cmiles74/cloudformation-tutorial/master/template-diagram-3.png" alt="CloudFormation Diagram" title="Diagram of our CloudFormation Tutorial Stack"&gt;&lt;/a&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;&lt;a href="https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-3gni"&gt;Part 1: we setup the VPC and subnets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/cmiles74/provision-programmatically-with-cloudformation-finally-some-instances-2dha"&gt;Part 2: we setup some buckets, groups and privileges, and our instances&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&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="https://aws.amazon.com/lambda/"&gt;Amazon Lambda&lt;/a&gt; service to perform the snapshot and cull old snapshot images. Since the Lambda script runs independently of our instances we can be sure it will always run on-time 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&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 manage snapshots and write logs to CloudFormation (so we can monitor those backup jobs).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialLambdaBackupRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda.amazonaws.com&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TutorialLambdaBackupRolePolicy&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
             &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
               &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:CreateTags&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:CreateSnapshot&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:DeleteSnapshot&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:Describe*&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:ModifySnapshotAttribute&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:ResetSnapshotAttribute&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;xray:PutTraceSegments&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;xray:PutTelemetryRecords&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;xray:GetSamplingRules&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;xray:GetSamplingTargets&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;xray:GetSamplingStatisticSummaries&lt;/span&gt;
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs:*&lt;/span&gt;
               &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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="https://aws.amazon.com/xray/"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialCreateBackupLambda&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ebs-snapshots-create&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create EBS Snapshots&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
      &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;TutorialLambdaBackupRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;REGIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AWS::Region&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.6&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
      &lt;span class="na"&gt;TracingConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Active&lt;/span&gt;
      &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ZipFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
        &lt;span class="s"&gt;code ellided for clarity&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;code&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="https://www.python.org/"&gt;Python&lt;/a&gt; because it keeps the code simple and nearly everyone knows Python. &lt;code&gt;;-&lt;/code&gt; And next, the Python code itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;boto3&lt;/span&gt;

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

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

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

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

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

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

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

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

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

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

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

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total_created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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&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&gt;
  
  
  Daily Snapshot Retention Job
&lt;/h3&gt;

&lt;p&gt;We don't want to accrue snapshots indefininitely, let's not forget that we get charged for their storage. &lt;code&gt;;-)&lt;/code&gt; This next job will enforce our retention policy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialDeleteBackupLambda&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ebs-snapshots-delete&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Delete Old EBS Snapshots&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
      &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;TutorialLambdaBackupRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;REGIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AWS::Region&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.6&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
      &lt;span class="na"&gt;TracingConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Active&lt;/span&gt;
      &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ZipFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
        &lt;span class="s"&gt;code elided for clarity&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dateutil&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;relativedelta&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;boto3&lt;/span&gt;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total_deleted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&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="https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/Create-CloudWatch-Events-Scheduled-Rule.html"&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-target.html"&gt;&lt;code&gt;AWS::Events::Rule&lt;/code&gt;&lt;/a&gt; resource can be used to do exactly this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPerformBackupRule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Events::Rule&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Triggers our backup lambda script&lt;/span&gt;
      &lt;span class="na"&gt;ScheduleExpression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cron(30 4 * * ? *)&lt;/span&gt;
      &lt;span class="na"&gt;State&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
      &lt;span class="na"&gt;Targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;TutorialCreateBackupLambda.Arn&lt;/span&gt;
          &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TutorialBackupRule&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;These two jobs use the familiar &lt;a href="https://crontab.guru/"&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&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-permission.html"&gt;&lt;code&gt;AWS::Lambda::Permission&lt;/code&gt;&lt;/a&gt; resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPermissionCreate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Permission&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;TutorialCreateBackupLambda.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda:InvokeFunction&lt;/span&gt;
      &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;events.amazonaws.com&lt;/span&gt;
      &lt;span class="na"&gt;SourceArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;TutorialPerformBackupRule.Arn&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;We add the &lt;code&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;WebServer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A reference to the web server&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialWebServer&lt;/span&gt;

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

  &lt;span class="na"&gt;BackupS3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3 Bucket used to backup data&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;https://${TutorialBackupS3Bucket.DomainName}\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&gt;;-D&lt;/code&gt;&lt;/p&gt;




</description>
      <category>cloudformation</category>
      <category>ec2</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>Provision Programmatically with CloudFormation: Part 2, Finally Some Instances!</title>
      <dc:creator>Christopher Miles</dc:creator>
      <pubDate>Wed, 06 Feb 2019 17:37:24 +0000</pubDate>
      <link>https://dev.to/cmiles74/provision-programmatically-with-cloudformation-finally-some-instances-2dha</link>
      <guid>https://dev.to/cmiles74/provision-programmatically-with-cloudformation-finally-some-instances-2dha</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fcmiles74%2Fcloudformation-tutorial%2Fmaster%2Ftemplate-diagram-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fcmiles74%2Fcloudformation-tutorial%2Fmaster%2Ftemplate-diagram-2.png" title="Diagram of our CloudFormation Tutorial Stack" alt="CloudFormation Diagram"&gt;&lt;/a&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;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-3gni"&gt;Part 1: we setup of the VPC and subnets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialAdminGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Group&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TutorialAdminPolicy&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
              &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucket&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucketByTags&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucketMultipartUploads&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucketVersions&lt;/span&gt;
              &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${TutorialBackupS3Bucket.Arn}&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
              &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:PutObject&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:DeleteObject&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:GetObject&lt;/span&gt;
              &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${TutorialBackupS3Bucket.Arn}/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-group.html" rel="noopener noreferrer"&gt;IAM Group resource&lt;/a&gt; to create our new group. The only property that we provide is the &lt;code&gt;Policies&lt;/code&gt; property that contains a list of &lt;code&gt;PolicyName&lt;/code&gt; and &lt;code&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&gt;PolicyDocument&lt;/code&gt;contains a &lt;code&gt;Statement&lt;/code&gt; that holds a list of &lt;code&gt;Effect&lt;/code&gt; instances; each of those in turn contains an &lt;code&gt;Action&lt;/code&gt; property with a list of permissions. We use the &lt;code&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&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&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&gt;--capabilities&lt;/code&gt; flag to our &lt;code&gt;aws&lt;/code&gt; commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation create-stack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; tutorial &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-body&lt;/span&gt; file://template.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="nv"&gt;ParameterKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;KeyPairName,ParameterValue&lt;span class="o"&gt;=&lt;/span&gt;cloudfront-tutorial &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Project,Value&lt;span class="o"&gt;=&lt;/span&gt;cf-tutorial &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--capabilities&lt;/span&gt; CAPABILITY_IAM
&lt;/code&gt;&lt;/pre&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialInstanceRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws-us-gov:iam::aws:policy/CloudWatchAgentServerPolicy&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws-us-gov:iam::aws:policy/service-role/AmazonEC2RoleforSSM&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws-us-gov:iam::aws:policy/AmazonSSMReadOnlyAccess&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ec2.amazonaws.com&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html" rel="noopener noreferrer"&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="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html" rel="noopener noreferrer"&gt;CloudWatch agent&lt;/a&gt; and submit events to the &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html" rel="noopener noreferrer"&gt;CloudWatch service&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;the next lets the instance interact with &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/what-is-systems-manager.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialInstanceRolePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Policy&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TutorialInstanceRolePolicy&lt;/span&gt;
      &lt;span class="na"&gt;Roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TutorialInstanceRole&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucket&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucketByTags&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucketMultipartUploads&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucketVersions&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${TutorialBackupS3Bucket.Arn}&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:PutObject&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:DeleteObject&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:GetObject&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${TutorialBackupS3Bucket.Arn}/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are creating a new &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-policy.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialInstanceProfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::InstanceProfile&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialInstanceRole&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html" rel="noopener noreferrer"&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&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="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialBootLog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Logs::LogGroup&lt;/span&gt;

  &lt;span class="na"&gt;TutorialKernelLog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Logs::LogGroup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html" rel="noopener noreferrer"&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&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html" rel="noopener noreferrer"&gt;Instance resource&lt;/a&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialDatabaseServer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Instance&lt;/span&gt;
    &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AWS::CloudFormation::Init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;/etc/profile.d/cloudformation-init.sh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;export BACKUP_S3_BUCKET="${TutorialBackupS3Bucket}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html" rel="noopener noreferrer"&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&gt;/etc/profile.d&lt;/code&gt; called &lt;code&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;            &lt;span class="na"&gt;/etc/amazon/amazon-cloudwatch-agent.json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;{&lt;/span&gt;
                  &lt;span class="s"&gt;"metrics": {&lt;/span&gt;
                    &lt;span class="s"&gt;"append_dimensions": {&lt;/span&gt;
                      &lt;span class="s"&gt;"AutoScalingGroupName": "${!aws:AutoScalingGroupName}",&lt;/span&gt;
                      &lt;span class="s"&gt;"ImageId": "${!aws:ImageId}",&lt;/span&gt;
                      &lt;span class="s"&gt;"InstanceId": "${!aws:InstanceId}",&lt;/span&gt;
                      &lt;span class="s"&gt;"InstanceType": "${!aws:InstanceType}"&lt;/span&gt;
                    &lt;span class="s"&gt;},&lt;/span&gt;
                    &lt;span class="s"&gt;"metrics_collected": {&lt;/span&gt;
                      &lt;span class="s"&gt;"cpu": {&lt;/span&gt;
                        &lt;span class="s"&gt;"measurement": [&lt;/span&gt;
                          &lt;span class="s"&gt;"cpu_usage_idle",&lt;/span&gt;
                          &lt;span class="s"&gt;"cpu_usage_iowait",&lt;/span&gt;
                          &lt;span class="s"&gt;"cpu_usage_user",&lt;/span&gt;
                          &lt;span class="s"&gt;"cpu_usage_system"&lt;/span&gt;
                        &lt;span class="s"&gt;],&lt;/span&gt;
                        &lt;span class="s"&gt;"metrics_collection_interval": 30,&lt;/span&gt;
                        &lt;span class="s"&gt;"totalcpu": false&lt;/span&gt;
                      &lt;span class="s"&gt;},&lt;/span&gt;
                      &lt;span class="s"&gt;"disk": {&lt;/span&gt;
                        &lt;span class="s"&gt;"measurement": [&lt;/span&gt;
                          &lt;span class="s"&gt;"used_percent",&lt;/span&gt;
                          &lt;span class="s"&gt;"inodes_free"&lt;/span&gt;
                        &lt;span class="s"&gt;],&lt;/span&gt;
                        &lt;span class="s"&gt;"metrics_collection_interval": 30,&lt;/span&gt;
                        &lt;span class="s"&gt;"resources": [&lt;/span&gt;
                          &lt;span class="s"&gt;"*"&lt;/span&gt;
                        &lt;span class="s"&gt;]&lt;/span&gt;
                      &lt;span class="s"&gt;},&lt;/span&gt;
                      &lt;span class="s"&gt;"diskio": {&lt;/span&gt;
                        &lt;span class="s"&gt;"measurement": [&lt;/span&gt;
                          &lt;span class="s"&gt;"io_time"&lt;/span&gt;
                        &lt;span class="s"&gt;],&lt;/span&gt;
                        &lt;span class="s"&gt;"metrics_collection_interval": 30,&lt;/span&gt;
                        &lt;span class="s"&gt;"resources": [&lt;/span&gt;
                          &lt;span class="s"&gt;"*"&lt;/span&gt;
                        &lt;span class="s"&gt;]&lt;/span&gt;
                      &lt;span class="s"&gt;},&lt;/span&gt;
                      &lt;span class="s"&gt;"mem": {&lt;/span&gt;
                        &lt;span class="s"&gt;"measurement": [&lt;/span&gt;
                          &lt;span class="s"&gt;"mem_used_percent"&lt;/span&gt;
                        &lt;span class="s"&gt;],&lt;/span&gt;
                        &lt;span class="s"&gt;"metrics_collection_interval": 30&lt;/span&gt;
                      &lt;span class="s"&gt;},&lt;/span&gt;
                      &lt;span class="s"&gt;"statsd": {&lt;/span&gt;
                        &lt;span class="s"&gt;"metrics_aggregation_interval": 30,&lt;/span&gt;
                        &lt;span class="s"&gt;"metrics_collection_interval": 10,&lt;/span&gt;
                        &lt;span class="s"&gt;"service_address": ":8125"&lt;/span&gt;
                      &lt;span class="s"&gt;},&lt;/span&gt;
                      &lt;span class="s"&gt;"swap": {&lt;/span&gt;
                        &lt;span class="s"&gt;"measurement": [&lt;/span&gt;
                          &lt;span class="s"&gt;"swap_used_percent"&lt;/span&gt;
                        &lt;span class="s"&gt;],&lt;/span&gt;
                        &lt;span class="s"&gt;"metrics_collection_interval": 30&lt;/span&gt;
                      &lt;span class="s"&gt;}&lt;/span&gt;
                    &lt;span class="s"&gt;}&lt;/span&gt;
                  &lt;span class="s"&gt;},&lt;/span&gt;
                  &lt;span class="s"&gt;"logs": {&lt;/span&gt;
                    &lt;span class="s"&gt;"logs_collected": {&lt;/span&gt;
                      &lt;span class="s"&gt;"files": {&lt;/span&gt;
                        &lt;span class="s"&gt;"collect_list": [&lt;/span&gt;
                          &lt;span class="s"&gt;{&lt;/span&gt;
                            &lt;span class="s"&gt;"log_group_name": "${TutorialBootLog}",&lt;/span&gt;
                            &lt;span class="s"&gt;"file_path": "/var/log/boot.log"&lt;/span&gt;
                          &lt;span class="s"&gt;},&lt;/span&gt;
                          &lt;span class="s"&gt;{&lt;/span&gt;
                            &lt;span class="s"&gt;"log_group_name": "${TutorialKernelLog}",&lt;/span&gt;
                            &lt;span class="s"&gt;"file_path": "/var/log/messages"&lt;/span&gt;
                          &lt;span class="s"&gt;}&lt;/span&gt;
                        &lt;span class="s"&gt;]&lt;/span&gt;
                      &lt;span class="s"&gt;}&lt;/span&gt;
                    &lt;span class="s"&gt;}&lt;/span&gt;
                  &lt;span class="s"&gt;}&lt;/span&gt;
                &lt;span class="s"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ImageId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ImageId&lt;/span&gt;
      &lt;span class="na"&gt;InstanceType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;InstanceType&lt;/span&gt;
      &lt;span class="na"&gt;KeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;KeyPairName&lt;/span&gt;
      &lt;span class="na"&gt;NetworkInterfaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPrivateSubnet&lt;/span&gt;
          &lt;span class="na"&gt;DeviceIndex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
          &lt;span class="na"&gt;GroupSet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPrivateSecurityGroup&lt;/span&gt;
      &lt;span class="na"&gt;BlockDeviceMappings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DeviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/dev/${RootDevice}"&lt;/span&gt;
          &lt;span class="na"&gt;Ebs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;VolumeSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;VolumeSize&lt;/span&gt;
            &lt;span class="na"&gt;VolumeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gp2&lt;/span&gt;
      &lt;span class="na"&gt;IamInstanceProfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialInstanceProfile&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tutorial&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Database&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Server"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://aws.amazon.com/amazon-linux-2/" rel="noopener noreferrer"&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="https://hub.docker.com/_/amazonlinux/" rel="noopener noreferrer"&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&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="https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/free-tier-limits.html" rel="noopener noreferrer"&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&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&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&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="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html" rel="noopener noreferrer"&gt;Elastic Block Store&lt;/a&gt; (EBS) volumes for our instance with the &lt;code&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&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="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html" rel="noopener noreferrer"&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&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&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&gt;cfn-init&lt;/code&gt; at start time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="na"&gt;UserData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="na"&gt;Fn::Base64&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
          &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;#!/bin/bash -xe&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;Documentation for &lt;code&gt;cfn-init&lt;/code&gt; is &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-init.html" rel="noopener noreferrer"&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&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&gt;/etc/profile.d&lt;/code&gt; with our custom environment variables.&lt;/p&gt;

&lt;p&gt;The &lt;code&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialWebServer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Instance&lt;/span&gt;
    &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AWS::CloudFormation::Init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;/etc/profile.d/cloudformation-init.sh&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;export BACKUP_S3_BUCKET="${TutorialBackupS3Bucket}"&lt;/span&gt;
                &lt;span class="s"&gt;export DATABASE_SERVER="${TutorialDatabaseServer.PrivateIp}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ImageId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ImageId&lt;/span&gt;
      &lt;span class="na"&gt;InstanceType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;InstanceType&lt;/span&gt;
      &lt;span class="na"&gt;KeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;KeyPairName&lt;/span&gt;
      &lt;span class="na"&gt;NetworkInterfaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPublicSubnet&lt;/span&gt;
          &lt;span class="na"&gt;DeviceIndex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
          &lt;span class="na"&gt;GroupSet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPublicSecurityGroup&lt;/span&gt;
      &lt;span class="na"&gt;BlockDeviceMappings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DeviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/dev/${RootDevice}"&lt;/span&gt;
          &lt;span class="na"&gt;Ebs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;VolumeSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;VolumeSize&lt;/span&gt;
            &lt;span class="na"&gt;VolumeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gp2&lt;/span&gt;
      &lt;span class="na"&gt;IamInstanceProfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialInstanceProfile&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tutorial&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Web&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Server"&lt;/span&gt;
      &lt;span class="na"&gt;UserData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="na"&gt;Fn::Base64&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
          &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;#!/bin/bash -xe&lt;/span&gt;

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

            &lt;span class="s"&gt;# download and install cloudwatch agent&lt;/span&gt;
            &lt;span class="s"&gt;wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm&lt;/span&gt;
            &lt;span class="s"&gt;yum -y install amazon-cloudwatch-agent.rpm&lt;/span&gt;
            &lt;span class="s"&gt;mv /etc/amazon/amazon-cloudwatch-agent.json /etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.json&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl enable amazon-cloudwatch-agent&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl start amazon-cloudwatch-agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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="https://console.aws.amazon.com/billing/home" rel="noopener noreferrer"&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&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="https://aws.amazon.com/sns/" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialAlarmTopic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SNS::Topic&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;TopicName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TutorialAlarms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialDatabaseCPUAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Database Server CPU Usage High&lt;/span&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialAlarmTopic&lt;/span&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CPUUtilization&lt;/span&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/EC2&lt;/span&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Average&lt;/span&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;89&lt;/span&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanThreshold&lt;/span&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;InstanceId&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialDatabaseServer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialDatabaseDiskAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Database Server Disk Usage High&lt;/span&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialAlarmTopic&lt;/span&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;disk_used_percent&lt;/span&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CWAgent&lt;/span&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Average&lt;/span&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;89&lt;/span&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanThreshold&lt;/span&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;InstanceId&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialDatabaseServer&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImageId&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ImageId&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;InstanceType&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;InstanceType&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;device&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${RootDevice}1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fstype&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;RootFsType&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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&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;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-backup-jobs-and-wrap-up-4plp"&gt;Part 3: Backup Jobs and Wrap-Up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>cloudformation</category>
      <category>ec2</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>Provision Pragmatically and Programmatically with CloudFormation, Part 1</title>
      <dc:creator>Christopher Miles</dc:creator>
      <pubDate>Fri, 25 Jan 2019 18:37:18 +0000</pubDate>
      <link>https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-3gni</link>
      <guid>https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-3gni</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fcmiles74%2Fcloudformation-tutorial%2Fmaster%2Ftemplate-diagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fcmiles74%2Fcloudformation-tutorial%2Fmaster%2Ftemplate-diagram.png" title="Diagram of our CloudFormation Tutorial Stack" alt="CloudFormation Diagram"&gt;&lt;/a&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="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&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="https://github.com/cmiles74/cloudformation-tutorial" rel="noopener noreferrer"&gt;over at GitHub&lt;/a&gt;. After slogging through this article you can follow along with second and third parts of this series. At then end of the series we'll have one template that &lt;em&gt;literally&lt;/em&gt; does it all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/cmiles74/provision-programmatically-with-cloudformation-finally-some-instances-2dha"&gt;Part 2: we setup some buckets, groups and privileges, and our instances&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-backup-jobs-and-wrap-up-4plp"&gt;Part 3: we put backup and retention jobs together&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&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="https://aws.amazon.com/cli/" rel="noopener noreferrer"&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="https://aws.amazon.com/powershell/" rel="noopener noreferrer"&gt;AWS Tools for PowerShell&lt;/a&gt; which do much the same thing but, you know, with &lt;a href="https://docs.microsoft.com/en-us/powershell/scripting/overview?view=powershell-6" rel="noopener noreferrer"&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="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html" rel="noopener noreferrer"&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&gt;:-)&lt;/code&gt;&lt;/p&gt;

&lt;h2&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="https://yaml.org/" rel="noopener noreferrer"&gt;YAML&lt;/a&gt; or &lt;a href="https://www.json.org/" rel="noopener noreferrer"&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&gt;template.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provisions a simple cluster with a web and a database server&lt;/span&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;KeyPairName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name of keypair used when provisioning instances&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://github.com/cmiles74/cloudformation-tutorial/blob/master/template.yaml" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;InstanceType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Instance type to provision&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;t2.micro&lt;/span&gt;
  &lt;span class="na"&gt;ImageId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Image to use when provisioning instances&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ami-035be7bafff33b6b6&lt;/span&gt;  &lt;span class="c1"&gt;# Amazon Linux 2&lt;/span&gt;
  &lt;span class="na"&gt;RootDevice&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name of the root device on provisioned instances&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xvda&lt;/span&gt;  &lt;span class="c1"&gt;# varies based on Linux type (Ubunut /dev/sda1)&lt;/span&gt;
  &lt;span class="na"&gt;RootFsType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;File system type for root device&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xfs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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="https://aws.amazon.com/vpc/faqs/" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&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&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&gt;Type&lt;/code&gt; of resource: every CloudFormation resource has a type, they are all clearly documented in the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/Welcome.html" rel="noopener noreferrer"&gt;CloudFormation API Documentation&lt;/a&gt;. In the example above we are creating a new &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html" rel="noopener noreferrer"&gt;VPC&lt;/a&gt; instance. Next we set the &lt;code&gt;Properties&lt;/code&gt; for our resource, the values provided here will be particular to each resource. Most resources accept a &lt;code&gt;Tags&lt;/code&gt; property but not all, if you set &lt;code&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&gt;CidrBlock&lt;/code&gt; property and provide &lt;a href="http://jodies.de/ipcalc?host=10.6.0.0&amp;amp;mask1=24&amp;amp;mask2=" rel="noopener noreferrer"&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="https://aws.amazon.com/premiumsupport/knowledge-center/create-connection-vpc/" rel="noopener noreferrer"&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&gt;EnableDnsSupport&lt;/code&gt; and &lt;code&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation create-stack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; tutorial &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-body&lt;/span&gt; file://template.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="nv"&gt;ParameterKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;KeyPairName,ParameterValue&lt;span class="o"&gt;=&lt;/span&gt;cloudfront-tutorial &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Project,Value&lt;span class="o"&gt;=&lt;/span&gt;cf-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to provide your own key pair name where we have used &lt;code&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&gt;StackId&lt;/code&gt; with the &lt;a href="https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html" rel="noopener noreferrer"&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="https://console.aws.amazon.com/cloudformation" rel="noopener noreferrer"&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&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&gt;file://&lt;/code&gt; URL&lt;/li&gt;
&lt;li&gt;We set the one template parameter, &lt;code&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="https://console.aws.amazon.com/vpc" rel="noopener noreferrer"&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&gt;Name&lt;/code&gt;) and the tag we passed into the &lt;code&gt;create-stack&lt;/code&gt; command (&lt;code&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation update-stack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; tutorial &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-body&lt;/span&gt; file://template.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="nv"&gt;ParameterKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;KeyPairName,ParameterValue&lt;span class="o"&gt;=&lt;/span&gt;cloudfront-tutorial &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Project,Value&lt;span class="o"&gt;=&lt;/span&gt;cf-tutorial
&lt;/code&gt;&lt;/pre&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation delete-stack &lt;span class="nt"&gt;--stack-name&lt;/span&gt; tutorial
&lt;/code&gt;&lt;/pre&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&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/Welcome.html" rel="noopener noreferrer"&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&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&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&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="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialInternetGateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::InternetGateway&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Internet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Gateway"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There isn't a lot to configure for the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-internetgateway.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialVPCGatewayAttachment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::VPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialVPC&lt;/span&gt;
      &lt;span class="na"&gt;InternetGatewayId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialInternetGateway&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's not a lot to configuring the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc-gateway-attachment.html" rel="noopener noreferrer"&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html" rel="noopener noreferrer"&gt;&lt;code&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPublicSubnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Subnet&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialVPC&lt;/span&gt;
      &lt;span class="na"&gt;CidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.6.0.0/25"&lt;/span&gt;
      &lt;span class="na"&gt;MapPublicIpOnLaunch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Public&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Subnet"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPrivateSubnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Subnet&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialVPC&lt;/span&gt;
      &lt;span class="na"&gt;CidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.6.0.128/25"&lt;/span&gt;
      &lt;span class="na"&gt;MapPublicIpOnLaunch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Private&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Subnet"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the stanza above, we &lt;a href="http://jodies.de/ipcalc?host=10.6.0.128&amp;amp;mask1=25&amp;amp;mask2=" rel="noopener noreferrer"&gt;take the rest of our address space&lt;/a&gt; and allocate that to our private subnet, again we use the &lt;code&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&gt;MapPublicIpOnLaunch&lt;/code&gt; property to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPublicRouteTable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::RouteTable&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialVPC&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Public&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Internet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Route&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Table"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;There's not much to the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route-table.html" rel="noopener noreferrer"&gt;RouteTable resource&lt;/a&gt;, we indicate that we're going to be managing routes for our VPC with the &lt;code&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialInternetRoute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class="na"&gt;GatewayId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialInternetGateway&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPublicRouteTable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route.html" rel="noopener noreferrer"&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&gt;VPCGatewayAttachment&lt;/code&gt; is already setup with the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html" rel="noopener noreferrer"&gt;&lt;code&gt;DependsOn&lt;/code&gt; attribute&lt;/a&gt;. Like the &lt;code&gt;Retain&lt;/code&gt; attribute, you can place this on any resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPublicSubnetPublicRoute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPublicRouteTable&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPublicSubnet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With our route to the public internet setup, we can use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet-route-table-assoc.html" rel="noopener noreferrer"&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="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialNatGatewaySubnetRouteTable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::RouteTable&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialVPC&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NAT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Gateway&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Subnet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Route&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Table"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialNatGatewayInternetRoute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TutorialVPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class="na"&gt;GatewayId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialInternetGateway&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialNatGatewaySubnetRouteTable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html" rel="noopener noreferrer"&gt;EIP (Elastic IP) Resource&lt;/a&gt; to get a handle on a new Elastic IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialNatGatewayElasticIP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::EIP&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vpc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We set the &lt;code&gt;domain&lt;/code&gt; value to &lt;code&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialNatGateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::NatGateway&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AllocationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${TutorialNatGatewayElasticIP.AllocationId}&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPublicSubnet&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NAT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Gateway"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-natgateway.html" rel="noopener noreferrer"&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&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&gt;AllocationId&lt;/code&gt; associated with that Elastic IP instead. Here we use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html" rel="noopener noreferrer"&gt;&lt;code&gt;!Sub&lt;/code&gt;&lt;/a&gt; function to get the &lt;code&gt;AllocationId&lt;/code&gt; of our &lt;code&gt;NatGatewayElasticIP&lt;/code&gt; and provide that value to the &lt;code&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPrivateSubnetNatRoute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TutorialNatGateway&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class="na"&gt;NatGatewayId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialNatGateway&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPrivateRouteTable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPublicSubnetRouteAssoc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPublicRouteTable&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPublicSubnet&lt;/span&gt;

  &lt;span class="na"&gt;TutorialPrivateSubnetRouteAssoc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPrivateRouteTable&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialPrivateSubnet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet-route-table-assoc.html" rel="noopener noreferrer"&gt;SubnetRouteTableAssociation&lt;/a&gt; to attach our table of routes to the matching subnet.&lt;/p&gt;

&lt;h2&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="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPublicSecurityGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SecurityGroup&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Web&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Server&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Group"&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialVPC&lt;/span&gt;
      &lt;span class="na"&gt;GroupDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Web Server Security Group&lt;/span&gt;
      &lt;span class="na"&gt;SecurityGroupIngress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class="c1"&gt;# Public Internet&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;              &lt;span class="c1"&gt;# HTTP &lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class="c1"&gt;# Public Internet&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;             &lt;span class="c1"&gt;# HTTPS&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class="c1"&gt;# Replace with your IP Range&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt;              &lt;span class="c1"&gt;# SSH&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.6.0.0/24&lt;/span&gt;       &lt;span class="c1"&gt;# Inside the VPC&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-1&lt;/span&gt;            &lt;span class="c1"&gt;# All traffic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group.html" rel="noopener noreferrer"&gt;SecurityGroup resource&lt;/a&gt; to create our new group for the public subnet. The &lt;code&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&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&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="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group-rule.html" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="na"&gt;SecurityGroupEgress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class="c1"&gt;# Public Internet&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;              &lt;span class="c1"&gt;# HTTP&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class="c1"&gt;# Public Internet&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;             &lt;span class="c1"&gt;# HTTPS&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class="c1"&gt;# Public Internet&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;587&lt;/span&gt;             &lt;span class="c1"&gt;# SSL SMTP&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;587&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.6.0.0/24&lt;/span&gt;       &lt;span class="c1"&gt;# Inside the VPC&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-1&lt;/span&gt;            &lt;span class="c1"&gt;# All traffic&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Public&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Subnet"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialPrivateSecurityGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SecurityGroup&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Private&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Group"&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TutorialVPC&lt;/span&gt;
      &lt;span class="na"&gt;GroupDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Private Subnet Security Group&lt;/span&gt;
      &lt;span class="na"&gt;SecurityGroupIngress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.6.0.0/24&lt;/span&gt;       &lt;span class="c1"&gt;# Inside the VPC&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-1&lt;/span&gt;            &lt;span class="c1"&gt;# All traffic&lt;/span&gt;
      &lt;span class="na"&gt;SecurityGroupEgress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.6.0.0/24&lt;/span&gt;       &lt;span class="c1"&gt;# Inside the VPC&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-1&lt;/span&gt;            &lt;span class="c1"&gt;# All traffic&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class="c1"&gt;# Public Internet&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;80'&lt;/span&gt;            &lt;span class="c1"&gt;# HTTP&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;80'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;         &lt;span class="c1"&gt;# Public Internet&lt;/span&gt;
          &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;443'&lt;/span&gt;           &lt;span class="c1"&gt;# HTTPS&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;443'&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Private&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Subnet"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;TutorialBackupS3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AccessControl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Private&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Backup&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bucket"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple enough! We use the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html" rel="noopener noreferrer"&gt;Bucket resource&lt;/a&gt; to create our bucket and we set that bucket to be private with the &lt;code&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&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. Once you've recovered from all of this YAML, check out part 2!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/cmiles74/provision-programmatically-with-cloudformation-finally-some-instances-2dha"&gt;Part 2: we setup some buckets, groups and privileges, and our instances&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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




</description>
      <category>cloudformation</category>
      <category>ec2</category>
      <category>aws</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
