<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ginos Tech Blog Feed]]></title><description><![CDATA[Ginos Tech Blog Feed]]></description><link>https://www.ginocoates.com</link><generator>GatsbyJS</generator><lastBuildDate>Fri, 31 Dec 2021 04:58:17 GMT</lastBuildDate><item><title><![CDATA[Moving Around in VR]]></title><description><![CDATA[Oculus sample framework provides some prefabs to enable player movement in a Unity VR Game. Lets add one to our VR scene. The provided…]]></description><link>https://www.ginocoates.com/2021-12-21-VR-moving-around/</link><guid isPermaLink="false">https://www.ginocoates.com/2021-12-21-VR-moving-around/</guid><pubDate>Fri, 31 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Oculus sample framework provides some prefabs to enable player movement in a Unity VR Game.&lt;/p&gt;
&lt;p&gt;Lets add one to our VR scene.&lt;/p&gt;
&lt;p&gt;The provided Player Controller Prefab uses custom hand meshes as controllers and enables player stick movement, so first remove the static OVRCameraRig added in the &lt;a href=&quot;./getting-started-with-vr-unity-quest-2&quot;&gt;first article&lt;/a&gt; in this series.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 478px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACoUlEQVQ4y5WS20uTYRzH97foVW3ztL17D7666Q5OltkBIoMiqMigoosibBMxOkBF5I3OA0EK0lUE2V0EBR3oQEHYXHPzMLMUdnIr3ftufmJv3URd6AMfnosvfHh+z+9rUl2tuH3tuNw+GgQRp9OJLMvIsoLZbKaqqorq6uotY+rcvZt2vx+/vw2fz4fX68XtdtPsdKKqKpIk0dDQgM1mQxDs2O12hP9RyWw2TKraiNfroevgQY4cOcyhQ13s37eXjl0BvB4P7tYWPO5W4+WSJKMojUiy/C+STFNTEybRf5S6wHG6zvZz4Ew/gRNBAsd76Ozupb7zFLUdJ9nRfoy93SHujE1yZWCMm+EJboUnjLvCjaFxBsYmOXr+GqaaegeORhdtgU7aAnvwtHcgqi1IqguH6kJudmOud9B3+SqV83VpifX1dTbLZcqlEuVyCV3TjOzuvXFMFvNOBLsNh2A3EAU7kkNAdAhIooAiS1gtOwle6qFY1IjOzLC8/I18oUA2myWTyZBKpShqGkNDg5gsFiuC4PjzD9JfiJKEoihYrFaCwRC6rhP7EiOZTJJcXGRlZZV8Pm9IS6USw8PDFaEFQRB+C0TxH2lFaDWEQTRdZzY2SzyeILm0ZAizuTVSqTSaphMOh7cmrEzRGwrCps5sZJpY5DM/cmm0n3k2Cll+rmWAEqMjWxDKikKt1cK5iyE+rsKDZ594+DLK81iG1/MFXs0VeBHP8WEFrg+MbE1YV2Ph9IUQTxfh/os4D95/ZypSYCqS53GkwKPpHE/mN+m7PbL9kROxKIuJWeIzn1lZTrLxY41CLm1ko8Ph7SwlZFRjYWGBRGKOyHSEmegX4vE50pkMul4ivN0tV6rx7s1botEomWzW6F86nTZqUywWGRwc5BcerJPhZhLWBAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Delete Camera Rig&quot;
        title=&quot;Delete Camera Rig&quot;
        src=&quot;/static/a129786f5b328c1e6c5e78cbec6e254e/50978/deletecamerarig.png&quot;
        srcset=&quot;/static/a129786f5b328c1e6c5e78cbec6e254e/772e8/deletecamerarig.png 200w,
/static/a129786f5b328c1e6c5e78cbec6e254e/e17e5/deletecamerarig.png 400w,
/static/a129786f5b328c1e6c5e78cbec6e254e/50978/deletecamerarig.png 478w&quot;
        sizes=&quot;(max-width: 478px) 100vw, 478px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Next drag the Player Controller prefab from the Assets &gt; Oculus &gt; SampleFramework &gt; Core &gt; Locamotion folder into your scene.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABF0lEQVQY00WQSW7CUBBEfSBmMMq38fA9ftuxE4xQIpOEHAAhsYBDcCu2LLjSi9xSkkWpu9VDdZXleR5aa0EYhsRxjO/7BEFAnueSu47DauXiuv9YLpdMp1Nms9lfnM/nWEVRsFgs5IAxRhpJkhBFkSwFOsPXGe7KJ00S6fUkPVlPHmqNyY3M27aNdbvduF6vdF3H4/HgcDhwOp243+/sdjvCuOT59R0dpWw2LW3byrHtdkvTNPLE2/cXVVUxmUywjscj+/1e5J7PZ9brtQxdLheMKfDzlvjlE0+nVFVJWZZiT13XZFlGpDXNR0eSpiLdGo1GjMdjwXA45LceDAYSlTaopMZ+clDqCaWUWOQ4jlgiuVIit7frBz6mtx6UVtaRAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Player Controller Prefab&quot;
        title=&quot;Player Controller Prefab&quot;
        src=&quot;/static/ebae7af59ff91ddd32f94a8336ced8a7/5a190/playercontrollerprefab.png&quot;
        srcset=&quot;/static/ebae7af59ff91ddd32f94a8336ced8a7/772e8/playercontrollerprefab.png 200w,
/static/ebae7af59ff91ddd32f94a8336ced8a7/e17e5/playercontrollerprefab.png 400w,
/static/ebae7af59ff91ddd32f94a8336ced8a7/5a190/playercontrollerprefab.png 800w,
/static/ebae7af59ff91ddd32f94a8336ced8a7/5a6dd/playercontrollerprefab.png 802w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 329px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 94%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACiElEQVQ4y41U2W7bMBDUBwVIYju6ZV2kJEuWZIk6rCtNUrRA25e2b3ko+ulTkI6FpnUcPwwogqvhzuxypTAMUVUVuq5DXddo2xaMMZRliSAIQAgR8DxPwHXds5Asy0IcxxinCY9PTxjHEdM0YRgGpGkqiCmlIvhIeg7S1dUVFosFNiHFNt4giiKEYQR+kWlZcBwHsixjuVxiuVod1jOQbNtGHAYYvz+j//QVw75B1+1R7HLkeYZdniGgRMi/BJJuGHBtG9v+EcPHz6iaAUX7gLKZUNQDqv4JUVLAcWy4l3h4fX0NWVEgLxegvoc0Z3AihnVQwN008JIeXtJC1U3Id3dC/jlIt7e3UFUViqpC1TQQ30NeVHCjAkE+wk8akLSDG+6gqhoURRbxb0G6ubmZN/wGXddRNw2cIIe/3YNs9/AiJohtmkLTDSiKchkhDzRNE1VVCwLKM4xrQezFtbjkkKUyx79LaBgGWMUllzORn7TwNgyG5UCR5Vfx70rmleKS1zQV3nFSP24ELCeAfLcSpG9lORPyAw5N00AJQZbxKlcgaQ83YofCRKXw8ZyXrwhFlqslvDhF/eMXbJIKL4Xs+FBtvufSVU2/zEPXccDaDlH/BSTZC//8l5UT66YNebWY2+c/ycc+nMF70fOQ51zmcGibTXUgFpXORLVNh57sSfFS/vaQvxg/yVD//A2H5i+VbgQpzScExSNM2589/BeS7/vzaKoYQ5pXSNiI/P4bknJEwu6RlBO21QPiogcJNi9z0Tk9vo69x4cEYyWSXQey7UCTFkHWgaYdgqxHVHxAuJuwdgl0XRP/nIKQfEyXPztN06EZFnRjDU03D9+mLfZ8VTVDeHdKLpf8B7runu3nJbR0AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Player Controller In Scene&quot;
        title=&quot;Player Controller In Scene&quot;
        src=&quot;/static/c797ec6572d7ae0e2d31f0e7bf1810d1/004a5/playercontrollerscene.png&quot;
        srcset=&quot;/static/c797ec6572d7ae0e2d31f0e7bf1810d1/772e8/playercontrollerscene.png 200w,
/static/c797ec6572d7ae0e2d31f0e7bf1810d1/004a5/playercontrollerscene.png 329w&quot;
        sizes=&quot;(max-width: 329px) 100vw, 329px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When you build and run movement is enabled with the left thumbstick, while rotation is enabled with the right stick and via HMD rotation.&lt;/p&gt;
&lt;p&gt;However, pushing the left stick all the way forward will show a red laser pointer and player movement will stop. This is part of the teleportation functionality provided by the prefab, which we are not using for this demo, so lets disable it.&lt;/p&gt;
&lt;p&gt;Go to the LocomotionController child of the Player Controller and uncheck the teleportation components.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABYklEQVQoz2WSW2+bMBhA+StZWKv1Yd0G2A6XhJsJgUAIl5Bq0ypN0/b/f8GpTNq+9OFI9oOPPx3bSpKEH98e39FFSqoTttqnaGLickeY+7iBw1f1HZlE5LogjndIKXFdF8/zcBwH3/exyvGZqGhRyR6x3XOcC67/jgx/D5x+Z1S/AvZPgvJJ0j5vqeecOE6IomgRvAkNQggsFUQYpB/iCp92Lvn5f2D+01FdXfRoU4x36IU1h9GhKA7UdUVZliil3jGXWEpKpBAoJRCuID9uqK+SfLgnGz+hp/uFfLxb9tXFo64b+v5M3/dkWUYQBItMa41lzKaFUhLhbdhVD+jLiny0P5CNK8rJoTzUnE4tXdctEiMMw5A0TW/CN6S3Ia4f0LOZzCaf1q/Y6Okz+bSivgra9szlMjEMw8cJTchbUA/XEUTFF9LzmuRk3+heWdYrsuaROM6Ww03TLB3NTzEPZBq+AJHU9LXXQjHRAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Locomotion Controller&quot;
        title=&quot;Locomotion Controller&quot;
        src=&quot;/static/7f1d6767d350c7a6528da8c4d654550c/5a190/locomotioncontroller.png&quot;
        srcset=&quot;/static/7f1d6767d350c7a6528da8c4d654550c/772e8/locomotioncontroller.png 200w,
/static/7f1d6767d350c7a6528da8c4d654550c/e17e5/locomotioncontroller.png 400w,
/static/7f1d6767d350c7a6528da8c4d654550c/5a190/locomotioncontroller.png 800w,
/static/7f1d6767d350c7a6528da8c4d654550c/c1b63/locomotioncontroller.png 1200w,
/static/7f1d6767d350c7a6528da8c4d654550c/29007/locomotioncontroller.png 1600w,
/static/7f1d6767d350c7a6528da8c4d654550c/3e096/locomotioncontroller.png 1718w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That should leave only stick based movement in the scene.&lt;/p&gt;
&lt;p&gt;Build and run and you can now walk around your beautiful terrain.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACbUlEQVQozwFiAp39AACy/wCx/wCv/wWv/wKp/wCl/QCt/xV7rS9SUidLSQ19sQat/wik+Qyp/AWm/QOk/Amo+gyq+wan/Auq+wAjjK8miKkamsoRot4IqvIFrPYUc5woRUklR1AoSEwfXG8Bq/gEtP8Gsv4Asf8BtP8AtP8Asf8Asf8As/8ARWEtRFw5R1lKQ15NN2dlOGVlNkxHLEpNIkdKLE5QK0ZFHWiCD6jmEK/uFKzqFp/UGZO/GJzQG5vOFJnNACtRGCpQGixNHytNGylJFydKGyJNIyVIIixMJi9NJTBPLTdIHjtbRTxhSj1YO0FRMkFPKEBQLkJVLT9QLQAmRQEpSgAoSQAmRwEkRgEdQwAXPgsjQxcvRREySRU4Tho3Rws/cVpGhIM4PQA7TBw+UB89Thw9Thw9Th0ALkQEMEkFLEQFKEIFIjgILTsISIqLPFo2P00XPVEfPFEeOU0ZQF0ySbnkcKaiS1onOUkWP1AhPU4fPU4gAC1BAypBBCk9BSxCDDA7AEB8c1XC7VWBazhNGDpOGjpKEzlIDDg/ADmPnE7U/1+1zEBZLTtMGD5QHz1OHAAqPAUlOQQpQQcxRQVllIRGxP1euNBLclU4SA47TRZPfWlVmJxJakZGnK4Us/8Ftf8zj5w7Rws8Tx08TxsALUAGJz0IKDoAUHdaTc3/NrTuUYFtUJujW5iWOUsTO10zTYp/a8nnT8r/Lrj7Ba//GKniPVUmOkwWOU0XAClEBSY9AjhPElm73Aqv/yaz9V3M/VuipT1dMDZOGDhMEzZFB0BYI2a2yE3J/xux+A+0/zpyXzhHCD1QHeYE0bdoV2U1AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;VR Movement&quot;
        title=&quot;VR Movement&quot;
        src=&quot;/static/d48b6c146f413f2995198d1c90184380/5a190/vrmove.png&quot;
        srcset=&quot;/static/d48b6c146f413f2995198d1c90184380/772e8/vrmove.png 200w,
/static/d48b6c146f413f2995198d1c90184380/e17e5/vrmove.png 400w,
/static/d48b6c146f413f2995198d1c90184380/5a190/vrmove.png 800w,
/static/d48b6c146f413f2995198d1c90184380/c1b63/vrmove.png 1200w,
/static/d48b6c146f413f2995198d1c90184380/29007/vrmove.png 1600w,
/static/d48b6c146f413f2995198d1c90184380/c72f7/vrmove.png 1915w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To prevent the player falling off the edge of the world, simply create some box colliders at the edges of the terrain that the player can&apos;t pass through.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACL0lEQVQoz22RS08TURiG+0fcoAsXSJjpdOaMzFCgM9PSUnohrWWBphUiMSqRIP8BNMa4AIN4i0bjBRNjVEKKCRCXiAqUWowLVi4gEcW4gsfMBOoFF29yNuf53osvGo3S1BREDSieDNOkcPY8+XP99PQP0tN/wdOJM33kunvpLPTQEgqh6zoBVUXTtKoMw8DXbEcIxxJoRw0UVeC0JXnz5TsfgZUdKO3A8jYsbUMZmCytEU8mMU0D0zSrMHUX7tPMEKbVitpoIYtGnFSO528XuTg2wuDwMMM3xhgaH+XSzRGuP3vKy6UKbckEwgUJQSAQQFEUTy7Upyh+NFVFFwK/VE8smeJ+cZpjuQia48eM1hBqr6cxXEd3XxdTy6tEYnECiuI5dGO6ziRJQpZlfO4FIQRC1/HLErFkmsczcxzPx7GztdiZw7Tm6rDTMqcHChRLFVpsB8Xv94DBYJCGhgaP4UX+CyhJtKXSTMzOkem0iWQ1nMwRrEwNZvwAvYN5iisVrHAEL5kQ1e5cuZx9wHgqzZPZOfL5FF2FLFZHADt7iFDqIL0DJymWVrHCYQ/oLu393XW3z6G853BmhkjUQAr6MVpriHbUEk/onOorMLVUxt51KMRv4J587jruQ/c6lIm2J3hdXuXK3VsM3b7K5XujjE885NqjB9yZfMXs5zUsJ+ytKvT/AP/swF0p5ISZLn/iw+ZP3m1ssbCxxfz6N+bXN3n/9QcvFhZptiyqyf4B/gLO7p88cZnEkQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Boundary&quot;
        title=&quot;Boundary&quot;
        src=&quot;/static/038a260dfb7191ed3533e28fc4a2b25e/5a190/terrainboundary.png&quot;
        srcset=&quot;/static/038a260dfb7191ed3533e28fc4a2b25e/772e8/terrainboundary.png 200w,
/static/038a260dfb7191ed3533e28fc4a2b25e/e17e5/terrainboundary.png 400w,
/static/038a260dfb7191ed3533e28fc4a2b25e/5a190/terrainboundary.png 800w,
/static/038a260dfb7191ed3533e28fc4a2b25e/c1b63/terrainboundary.png 1200w,
/static/038a260dfb7191ed3533e28fc4a2b25e/29007/terrainboundary.png 1600w,
/static/038a260dfb7191ed3533e28fc4a2b25e/09ede/terrainboundary.png 1710w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Adding Terrain to a VR Game]]></title><description><![CDATA[Add a terrain object to the scene (GameObject > 3D Object > Terrain). This will add a square terrain of 1000 meters to the scene.
 Click on…]]></description><link>https://www.ginocoates.com/2021-12-21-VR-adding-terrain/</link><guid isPermaLink="false">https://www.ginocoates.com/2021-12-21-VR-adding-terrain/</guid><pubDate>Sun, 26 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Add a terrain object to the scene (GameObject &gt; 3D Object &gt; Terrain). This will add a square terrain of 1000 meters to the scene.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 497px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 97.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAD3UlEQVQ4y32US28bZRSG/QP6H5pQNUFcQpCoVEDdsYYNiwoJsWCBukUI2EQJVKA0lVJCQSqhqO2OBW1FQW2tNKSkgVKJtqkvcTyZ8dhjezxXz82XiZ32QfO5SSyEsHR0zje2Hr/nvN+cVK2us6WU0U0HraZjNAxMw6RarWJbtjjXqjUaegPLtOh2Y/7vk7qf3+JOvs7yfZmHJRvTtKioFQGzTFOcjUaDer1OVdO4s7qGUlKQJAlJKiIVi4MszhKpjz/5lE7cI5svkMnmKRSK2I5L0/Nxmx6KrPBoPYOuG1y+cpXDh57h2fExDo6MMHro8F6MjI5ycGSU1Om5WXjSo6JIqFJBREUuErgWO92IhlZCymeImjZXL//EgQMHGB8fY2xsnFdfP8bR147xwsQkk5OTHHl5ktT0l6dZN3ZIP1C5+XeJ5VyDlQ2LNdnnXrnFquSynDf4sxRy6+5DPp+ZZnZ2lsXFRS5dusjFCxf45uzXnPlqgeMnPiQ1fWqB39Qe59MZfvyjRLrYIi11SEttboq6zdJWh1/yIcVGRH+7i1pS6XS6PH78mH5/B548odWN+WDFIHVqbh52YpTNPGHTod+N2G75bLeDvei1A+LII/BcDMMgl82iaVWCIKDZ9Ah8H920eOtXldTc3Bzxdo98Lo9hWgRhhB8E+EH4NAZ10/Nwm00cx2azUEBVy7RaLfF9K4owHZfjSxVS8/Pz9HoJMIdhmERRJP45iTAM97LneTSbTSzLopDfwLZt8dz394Hv3dZIzczMEMcx5XKZ8D9g/wY6jkOxWBTPO522EBBFoQCeWNNITU1NsR3HKIqC5/mijWFlu9n3fTGvRGG5rCHLCrncBrJcEr9pWA4f3asOgP1+X7Qhb8li6Al4GDaoQ1qhT+iaVGQJs6bR8mwiz6HfDvE9h8/uPwUmM0wUlkolNgubNBqGgCWqBjkgCn2kRsDNDY8rdxVuZG1uK21W5JaIpU2PUw9q+wp1XRftJMNO6sTV3RkmTiZA2QhJ5xx+/ktmVYlYLbX5/WlekTwW1mv7ClVVRdM0sV2S9nZhw9Aw8PAsnbIsoWsqke8St0O2Rcsui9n6vsIElqysZIbDDu8BfV/cgm4cU6/pYpW1kjuYuB2FuI7DD4+GZqhplcGKqlap1/UByB+Ykqyv5No8ymTEe/zFyZN8e/4Ct4pVljY1kdMbZc6tDwMrFep6XShNTNl1dxfYbne4du2aWFMvPf8cb7z9DmeyFgtZm4WMydmczbkNZx9YqVSo1WpC4fB1SUxK3E/q69dv8OLEBEePvMKb774vAN8VXBYLLt+LcPgHFKInYGBauRQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Add Terrain&quot;
        title=&quot;Add Terrain&quot;
        src=&quot;/static/a0a6b80ce82658151d6b777314a77c3d/15d25/addterrain.png&quot;
        srcset=&quot;/static/a0a6b80ce82658151d6b777314a77c3d/772e8/addterrain.png 200w,
/static/a0a6b80ce82658151d6b777314a77c3d/e17e5/addterrain.png 400w,
/static/a0a6b80ce82658151d6b777314a77c3d/15d25/addterrain.png 497w&quot;
        sizes=&quot;(max-width: 497px) 100vw, 497px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Click on the &quot;Create Neighbouring Terrains&quot; and add a 2x2 set of terrains.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 527px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAIAAADUsmlHAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACQElEQVQ4y3XT23KbMBQFUH4mxs7YOGCBQBK6oSsg3BA7k9d+Qf//rWOpZJJpux7RaDhns8kGNwszXpqujgAAh8OhqqqyLA+Hw/5fnp6ezufz8/NzppWitJdSUEqNMdZajLGU0lpblmXxxTkqy1JKaYxhjGVSDs65+/3+8fExTZP33jnnvZ+mSWvNOTfGrOu6LItSSmttjEmn1tqMM6K1fn19necZRyTCGPd9TyPGGKW0jzDG1lpjDKU0s9ZwztO1boMxRgh1XXc8HgEAEMKiKE6nEwAgHRVFUZZlNs9BCFHXNYSwjSBsKaUAgP1+n+d5VVVN0+R5vtvtAAB1Xe92uzzPj8djppREUdd122WAUHsBoDg9crpcLgCAl5eX8/kMIazruizLKsqkgoLLOcyEkDgwwuTa00VwPmzSXikIhJBz7s/OCOH0KB0/7tOfdvw1jj/e3tbb7fb+/j5NU13XX0aDacdsWcL1el3XNYSwLMs4ejkYpUbOOd0wxvh3QghCSOa9G8fRe2+tHcdxnoNzVkrRdV0aB/3H483ejyEEF3nvQwh93xNC1nW11g7DoKLhCyGEUkpKmQmLOBd+9On9qQaEkDR2v2EbSqmNHoEZp6QcZDQMw2dP2rZLtSGEtG372e2iKAAATdP0ff8oifeOMfaZYdu2HYI9JVJIxpiImqZJe6a00ziZ1jo1Pk2SOlTXoKoqEKVfFUL42aWUJYQwazbpS6bkQgj32/0WpV/NGAP/knXfpc211m6TohZCtH/5DRh6AbiC83sOAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adjacent Terrain&quot;
        title=&quot;Adjacent Terrain&quot;
        src=&quot;/static/fc27dd67dd0df84d72944fef9615ad81/44385/adjacentterrain.png&quot;
        srcset=&quot;/static/fc27dd67dd0df84d72944fef9615ad81/772e8/adjacentterrain.png 200w,
/static/fc27dd67dd0df84d72944fef9615ad81/e17e5/adjacentterrain.png 400w,
/static/fc27dd67dd0df84d72944fef9615ad81/44385/adjacentterrain.png 527w&quot;
        sizes=&quot;(max-width: 527px) 100vw, 527px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now paint the terrain using the terrain painting tool and brushes.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADbElEQVQ4yzWS60+bZRjG+1cYo0YTS4mJhVag59JSynFYxxZgjuGADyabZMNkTrTOKRTXmICAJJyyOiKGrcM4wtGxBGjJMDgoRdbSFCLDgRz6lhAqiaf4M++78OHK9Xx5ruf33Nctq7/mpOHax9R/6qTxupMbn33CV24XN647aXB+SIOzTvJ6Zx1fNLpwuVy43W6amppoaWmhublZOh9LZqu8jO5sDdbzl9Gc/4DXympRlNQgP1PLC2+9y3OFVTxfVM3LJ6t4w2BGlaJEpVKTkpJCcnIycrmcpKQkFAoFKpUKWaE/Qd50ghNTh1juJyiZOqTMl6Bm7oiPFo648PMRFf59SifWsJ+txlGQR25ePtnZ2ZjNZrKysrBYLJLbbDZkp2d2OeXfwTG9Q9HkDqemtynzbfOOb4tzvi0qfFtU+X+nfGqT7NJyjNoMtDoder1eIlKr1aSmppKWlvaM8GogxtWgwJVgjNqgwPuLMS4uClQHBKoXBC492qNufo8rczvknyknz27Dbs/BbrdTUFCAw+GQ6KxW6zPC5lCclnCc1pU4bZE4rZE4TSsC7rBAYyhOw3Kc+uU4ny/ucrKimvwcOzk5uWRmZkpftVitWDIzycqySo/IOkJ7dIX26AnH8KzEuBXd52Z0n87IPu1hga+Xd2kLxWldFtDb83nlpRcxmkwUFRWh1xswm4zoTHYK3yymtLQEWU9kH0/0gN7VA26vH9I9v0GHP4Qn+JSb4Rg90QO6g5t0BJ5izDuBSvk6SqVSosnIyECr1aDRGTEYzZiMRmS9yzt8F3iCd36NgYe/0Dnoo/vuOP2DY/T9OEPbkJ/adi9dYzPkFpeQLH+VJIVCKsVgMEhNGwx6Senp6chuB3+jfzqAd+IhfSPTfDvqp3/cj3fEh3fYR2f/MF/euodnNkLle5fIybZJJYhBosRZHssoEg5v/sHY1hGj0W2GAlHu+gP0jc/QOxzA88MS7f0PGFhY5fv1BKWVlVKg2KZIKAaI0mq16HQ6qSTZ5O6fzAh/MXfwLwuJ/3h08A+T0S2+GZula9DHnZ/CPNj7m9HNI4rfPodBr8NkNkuXxTmKCy6GazQaKVQ2+PhXRkLrjIXXuR95wkR0g/HIBkNLq9yZXWIgEOXe43W8i2sUFp8mTa2WWj7eO9FNJpMUJgb+D/CnhoSkygO4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Paint Terrain&quot;
        title=&quot;Paint Terrain&quot;
        src=&quot;/static/91e3dff14f4a1b1627a780859831cdc2/5a190/paintterrain.png&quot;
        srcset=&quot;/static/91e3dff14f4a1b1627a780859831cdc2/772e8/paintterrain.png 200w,
/static/91e3dff14f4a1b1627a780859831cdc2/e17e5/paintterrain.png 400w,
/static/91e3dff14f4a1b1627a780859831cdc2/5a190/paintterrain.png 800w,
/static/91e3dff14f4a1b1627a780859831cdc2/c1b63/paintterrain.png 1200w,
/static/91e3dff14f4a1b1627a780859831cdc2/32ac3/paintterrain.png 1249w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now to color the terrain using textures.&lt;/p&gt;
&lt;p&gt;Import terrain assets and speed tree from the &lt;a href=&quot;https://assetstore.unity.com/packages/essentials/asset-packs/standard-assets-for-unity-2018-4-32351&quot;&gt;Standard Assets&lt;/a&gt; package on the unity asset store.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 107.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAEA0lEQVQ4y3VUy07jWBA1IFjAFjWwBCRWIJGH7ThxfB3bSWzHr8RJoOkeYGjNjhke4rFF7Nghdc8K0RpA/QEs+IGevwA2SPzHGVUFp3s0mkWpbip1zz11qsrS3t4fODk+xunpKQ4PD/H77i729vb4THZwcMD+6OgIx295Z2dnODk54bz9/f1/mTQ/P4+FhQU2Oi8tLWF6ehqSJGFsbIz9yMgIJiYmMDU1hfHxcY79r9VqNZimyWYYBizLwtzcHCYnJ7G4uIiZmZnhA2Sjo6OYefcOs7Oz/zHKlRzHAVmj0UC1WkWxWEShUEAul+OzoqjI5/PI5fJYWVkBEbi+/gtfvvyJq6sr3N3d4ebmBnffvuH682dIlUqFk2zbZq/rOihWLpc5ZlpN1JwIptOCaXmoOSGC9CO8oA3r7R5VRV6v6JA0TUO9XucglUxApVIJqqoyY0p0XRfNZhO+73OuWiqjXNHhuD9iVb0K0RKQCIACdJEAFUVhMDJmbTUhzAFzivGDqsqyRGkHURSxXFSV6L4BZhoSoCzLDEosXbeJsuFg49M+4rVfIRoRKjWPLxtCwA+i4T3KN1NzUDIxIVDqNCXTn9QMx7FRNuqoOgkqdgzb76Fi+dD1KtI0ZaO7QggIQ8BetweAFCAw/kMIUIyYUpOCsI0oWYcfpAiiHpJ2n/9vt9totVqo6DpXpJU0mF1z0GUSPNOQGkGxLN7r9bC1uYl+r4dOuw3f86AqCmpOA1EnRaNe54epKpEKSMSMLpHFccwiU+eCIEC32+WGNV2XH8r0VWUZcqWKuP8enXYClx5R1QHDrMs0NpRMxqIbBgtOLD3PY59VosgyVF0g6q0jjkLWnwCHXSYwop0NNG0IbQclZYNOMdoeMlUhQANxbx1JHCMIwwGJ1IBEpVCJZNn6hWGIjY0N7OzsYG1tbSgJnZMkeWOYAUYsCWlo921IpAu9PthbBcvLy7i8vMTr6yseHx/x9PSE5+dnPr+8vOD797+hUO5PGnLJigqrb0Gi0gg0A6aSSD9aN9L2Z8+TIARkuQjXbyFJu2j5Po8PSWN0DEikE80VXcjWjX5TCVQ+PUj6kqcZlRUFcrEAw2kiWf+AlufCrNV+NCXb39vbW5yfn/MXmc7b29t4eHhAv9/H/f09f40vLi5w/fUr8qurKJkO2h+30UliHpvhHBIbAgzDCJ7nwxQm4ijmrnXaKfskTobrSXnFQh4aAX7YQhS0UCVSsjIAJORioQjD1FHSFKglBYY5GBNh6SjkCxA1HbJSRFnXUKlqPNi63UDnwxYC3x9+9rjLmlZCbrWAdLMJL7ZgNap4/1vAu/nLbsjarH3yYTcNeEkN3U0Phi5QbzRYOwIiralxdbeOfwCjjB2ceWJwEwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terrain Assets&quot;
        title=&quot;Terrain Assets&quot;
        src=&quot;/static/5088919c010f225429c76d4d4bb2e693/5a190/terrainassets.png&quot;
        srcset=&quot;/static/5088919c010f225429c76d4d4bb2e693/772e8/terrainassets.png 200w,
/static/5088919c010f225429c76d4d4bb2e693/e17e5/terrainassets.png 400w,
/static/5088919c010f225429c76d4d4bb2e693/5a190/terrainassets.png 800w,
/static/5088919c010f225429c76d4d4bb2e693/e548f/terrainassets.png 975w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Click on a terrain tiles, then Create a terrain layer and select the texture for the terrain.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 526px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA80lEQVQoz22SWY6DQAxEuRRL2LcACfsiuP9JPHqWPMqE+XgqunF3lQ1Omqbyer2kbVupqkpYZ1mmLMsidV3L8/mUeZ5l33c5jkPKspQ8z2+w7/CwrqvCBcMwKH3fqxH6fr8VDDFC/0MvJFHXdZosCAIJw/DG4/GQKIpUqQHf92/qeZ44FHIzznEcK+yBPZt+HjZszfskScQxd1onKbMcx1HbRqdp0rabppGiKP5AEIN6zju4k46WgctR3GyPGlq3DoAgltBGhToc5CsCTjaLb2wfdV1Xa/mQpIffhFZgB83xk++ZUU+A67pk2zb9nc7z1BH9ABO+FxPHvWvCAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terrain Layer&quot;
        title=&quot;Terrain Layer&quot;
        src=&quot;/static/afad00989b95abc733237f21ebe453af/2d7ab/terrainlayer1.png&quot;
        srcset=&quot;/static/afad00989b95abc733237f21ebe453af/772e8/terrainlayer1.png 200w,
/static/afad00989b95abc733237f21ebe453af/e17e5/terrainlayer1.png 400w,
/static/afad00989b95abc733237f21ebe453af/2d7ab/terrainlayer1.png 526w&quot;
        sizes=&quot;(max-width: 526px) 100vw, 526px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAB90lEQVQoz5WSTU8TURhG5/cU6cfYdmaalq+2NIQQ+ie66M4f4LdoNLIwcaGiiV0obS2SYpNCWGiiGxY2ERe4oDBFnGm1limiEWzNMfcOSwNhkpOZZHLPfe57HyWRSDA2NsbIyMiZGR0dZXh4mPHxcZLJJENDQyjT09NMTExIaTwePxOpVEquE450Oi03UEKhEOFwGE3TTkTXDXQjcozhortvTdPRdB3DiKDous5JuDIdNeAnMOhB9Q6g+gY57xtE9Z1z8Q4QOOZUoUgQVANcuHiT2/NvuPbkFZcfLnLpwUuuPl7iylyZG0+rzORWmMktny4UxxCJ7uQqPDdhrnbIvddfuLu8xf13bWZXGzx6/5vcJ8htgHLa7ITQ6/XybD5P99cRm41d1mrr1NY3WKt9ZNO0aHX2sduORDHkcEUS478JIxFX+KJY5G+/x9eWjblVZ7fRYKu+Satps991+PljX6Koqipv2e/3EwwGJeLmBeJbSD0eD/l8nsOjP9i2Tb1eZ3vbxLIsTLPBzs5n9vYcHMdxeygWZrNZ2SPRq2g0iij81NSUTB6LxVhYKCGerrNH027yvdOh3f6GZdkcHBzQ7/fp9Xook5OTUpjJZGT7RfPFMYVQ/BNzjEWjXL81y+rbDywurVAs5CkUClSrVSqVCqVSiXK5LPkH9+7qQ6RgXdIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terrain Layer&quot;
        title=&quot;Terrain Layer&quot;
        src=&quot;/static/46fc7a741f68052160c373efa3e617a0/5a46d/terrainlayer2.png&quot;
        srcset=&quot;/static/46fc7a741f68052160c373efa3e617a0/772e8/terrainlayer2.png 200w,
/static/46fc7a741f68052160c373efa3e617a0/5a46d/terrainlayer2.png 300w&quot;
        sizes=&quot;(max-width: 300px) 100vw, 300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If your terrain is shiny, click on the cog and select the diffuse material instead of the standard one.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 103.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADYklEQVQ4y21UW2sTWxidBzn6IBxEsIIgHgu9pOY6M0n2XDKTTDLTpsmkaas9aguKrQr64Iv4WIvVV6HanpceBEVBpPig/2/J+ubsJJbzsLL3ZO9vfeu7bSNoKgSBhyRJ4LoulFLwPA9hGCIIAjSbTVkdx/kNUbsNz3Nxb+cp3hx+wfM3R3h58BGGchR83xeQqF6vC0maplheXsZwOJS10WgIEZ1mcFCvK2xsDvF8/xHeflrD4dc1GJ1OR4z7/f5ICVWSuFaroVqtCrgnbNsW6P/aSQfX52dw6cpFmGoGBr0tLi5KyCSaNC6XyygWiwLTNEfpELWuC8us4P7DJ3j47AWuTi/g7vYtGFRElTokElGdJtZKSdRqtcR5t9sVm7Dh4+6DJ3jw+iNmOndQjnoZIS9pUq2ORDzjqlNAJzpcu1qFWSogWr2HKXUTF6au4PyFqTFhHMeSbE3IPYvTbrelYHRGMM9KOSgWS8jn5nC14MK4nMeZs+fwx58XM8KlpaVRGLoQNN7c3BT0ej05Z7V5Z2dnG0dH/+D9uwO8PTrG3sEH7O69wvbGnXFRqIYkDI+kynERxQmazXCkjndzuRy2trZwcnKC4+N/8eXzZ3w/+YYfP3/gcO81DIbACkdRJOFRsc7T6ZbR36x6oVDA7Owspqence3aX5ifm4NZdzKFDJmErCKVErow/wftRK/7+/t4/PgRarX/CJkXFoWhk5zrpOFpWJaFSqUifcrv3d1d9NMUNcvOisJkE7qiTIOea+aOcz05dnREUp6TWDtwfT8jpDqCZJNjNUmSPQrZnndcP0QU91BXjnQAowrDZhYy1bE1Jhub5EwFzzklGvyfDv1mgri/AccLMBikWFlZQeO0Qk0oBr4vxWFYY4XOWGGyCn91G5bbQhj4Mtu0MWikC6HbR7dQksSjJ43EhOMoyRn7M037cJRCqVSSAgkhf0ion63JR9R1PSjOs3LQ7sSo1ZXsTcsSh+vr6+KQZCxKtWqPQ+bBaPBtG56jMFiOMejGsv69NsCg28Gwt4jQd5HP51GplKXBdaMXCnkYzBtDZVPrR8BvNNBuhVjvd3ArTQQ301hwe7iEqKFgmhbCIJDoaE9BJJXRY5X5ajMMfpOcq12tYT63gNzCjRHmidzCb+8kXx/uGfYvm2XhiMOgySgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terrain Layer&quot;
        title=&quot;Terrain Layer&quot;
        src=&quot;/static/87e184195e723f3bd98d83cbce208802/6af66/terrainmaterial.png&quot;
        srcset=&quot;/static/87e184195e723f3bd98d83cbce208802/772e8/terrainmaterial.png 200w,
/static/87e184195e723f3bd98d83cbce208802/e17e5/terrainmaterial.png 400w,
/static/87e184195e723f3bd98d83cbce208802/6af66/terrainmaterial.png 640w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Add the layer to the other tiles by selecting them and choosing Edit Terrain Layers &gt; AddLayer menu. Select the same layer you set up for the first tile. Adjust the material on the tile also if they are shiny.&lt;/p&gt;
&lt;p&gt;Create a few more layers, selecting the textures you want.&lt;/p&gt;
&lt;p&gt;Now, click on a layer in the properties window, and paint the terrain using the layer texture.&lt;/p&gt;
&lt;p&gt;Now add some trees. Import the &lt;a href=&quot;https://assetstore.unity.com/packages/3d/vegetation/speedtree/free-speedtrees-package-29170&quot;&gt;free Speed Tree asset&lt;/a&gt; from the asset store.&lt;/p&gt;
&lt;p&gt;In the Free_SpeedTrees folder, click on each tree and regenerate the materials.&lt;/p&gt;
&lt;p&gt;Add a tree prefab to paint with.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 330px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 92%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAC1ElEQVQ4y21Uz08TQRTe/8eWQrvdnd1t6RZogdIfNIQLIaQhHDx5IPFXIuGoBzVFEw9Aogn2xMlLJWlEgnKDu2g0oiEGtYKCnCCf+WZ9dTVu8mVm57355r3vvRkj42dQLCtYlolcLodisYhCoQDHcf4LpdRf//Tnvkwmg97eXhh+NoNCNa2NAwMDKJVK2olG3/e1ozhns1n09fXpkTbXdTE8PKz30Z5Op2FYloV4j4lkMolEIoGuri7tVKvVMD4+jnK5jJGREb1penoaU1NTmJmZwcTEBEzT1OBe8ti2DYMp5PM5Hbagv78f+Xy+E52kKVFI5J7nIZVKdcCINWG1WsXo6KhOd2xsTEcTTpcpEvIvoI+A9lTKCwiZ4tDQkNaO6UlhGFEYYYIwhNwTQm5mdCQjOf855yiHENz07yHhwzqEEh0JwmRCLhCNqa+MAla/Q0gyggaSENzAgwSDg4N6nT6cy6G0cU4NHUfBYGVIRie2jUDaIYAJy7JhKxXAVrBsG5YtcwWlHLieC4O9Q0JWmWDFWWlqSm1EcNdRsOMxKLMHKpkIYPbATnQHMLsRj0VgsL9Et3CVSUgy2i0zgdrFS7jxYBWXbz/E7M1FzN5axJU7j3B9oYFr9ce4Wm9g7t4KDDYnCXgjqBFTp36cM0KmwpPn7i5j9SOwvHOCemsP95/vY3nnFI3XQGMXWHkFPHkHGOz8SqWiwaioHa8RtaMc1DgajaK+sIDjn6d4v/cBu2/eaux/OkD72yEOvnzF59/QhFIUElIvEsrLwijj8TiWlpbA78f3I7TbbT0eHR3i5PgE5+dnOD8LoDUkWRi8l4yOoCSs+vz8PLa3t7G+/gzNZhOtVgtra2toNp/i5dYWNjY2sLn5IujDcMMSTJNkPOzPPU0hFovp10jayvU8ZH1fr0ciEVyIRANCRsUHQW6HPEUklMblOuXhGsk5spCTk5N6neBr8wtm08SdaZivAwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Paint Tree&quot;
        title=&quot;Paint Tree&quot;
        src=&quot;/static/d27e8dfa0c20b2fa4e48199d928835ed/d9ecf/painttree1.png&quot;
        srcset=&quot;/static/d27e8dfa0c20b2fa4e48199d928835ed/772e8/painttree1.png 200w,
/static/d27e8dfa0c20b2fa4e48199d928835ed/d9ecf/painttree1.png 330w&quot;
        sizes=&quot;(max-width: 330px) 100vw, 330px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 606px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 96%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACi0lEQVQ4y52UTU8TURSG5+ewKHQ+Ou183DvTznS+SzstGiAETTCBahExutHI0q2J/h8WkqgksmCDiaUlUX/Ia84tA01FqC7e3JOZnOe+99xzj/Tp8xHOvp1heD7EaDzCaEQ6v9QkHl+MMb6geIifv37g3Ye3WF29j729ZxgMBuj3+1hfW0M7TSH5gYcwCNFsNlGv19FoNODWG3BcF47jCBmGIaTrOnzPx+HhIY6/HOPo4xFOvp5gOBrh++kp3g8GkOI0QJ53kaYpoigC5xxRmiLNMgRBIMQ4A2MTua4L1+EwTVNsQqtlWYjCEL7nQbK5AduyYdv2VRKJT8WFU7deh2nU4GUdtLoriKMQYRgKcLGZxBwDzJ4kFlDbnsSzIveqrGD7+RNs7+5gudVCnueiXLIsi1yJkUMCsOvEaaeF6Du5LC8peHWwh/0XjxFHiQBS7RVFETW+AjJ2M2gaSA51TcO9h5tYe7CJLM1E3enIqqoKqMS4OTeQcQ5Dr4B3VhEu58jiAMutDC5nUMqL0DSVamj9k8OqXkW2voXuxiP4YRte1EGNNVExPciKRrdsikux7wQycM6gqjU8ff0GOy8PEPX6aK/vwm1tQfc3YDk+JOqj6Ru+26GOpJujs9JDGodwOENFU6AqZSiyDOm6VearYa1ahZ92kOYrSJMEhmGiXC5D0zQhibp8biBj4mWUFpcgKwps24LNTNEuCwsLKJVK/weU5bJY2+02er0OkiRBpVK5bJs5YLNASiQAvQ6Ki6YWwNBvwLLsO0HTr4hA1MgEITCJ6ideShL4fwXOwgoVk+YmSXWH33rk6X/UNrTSzCRorVb7Q1Ixem4bCrTGcYxer4csy8R0oTwCzDr8DQR+sns4ZU7fAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Paint Tree&quot;
        title=&quot;Paint Tree&quot;
        src=&quot;/static/30d74a33e6511dac41ab73f8750fb062/4d4a2/painttree2.png&quot;
        srcset=&quot;/static/30d74a33e6511dac41ab73f8750fb062/772e8/painttree2.png 200w,
/static/30d74a33e6511dac41ab73f8750fb062/e17e5/painttree2.png 400w,
/static/30d74a33e6511dac41ab73f8750fb062/4d4a2/painttree2.png 606w&quot;
        sizes=&quot;(max-width: 606px) 100vw, 606px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Add a couple of tree models then you can paint trees, mass place trees, and randomly rotate trees.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAC6UlEQVQozz2SS28bBRSF/UdACW6KHdvjOs74HU88nvHYjp+JHT/Hr3GaBOTmVSAkRSUihTRp6xBVoIpAK1XthscCFoEFailPIUS6QCAqumTBip/wIRvE4ugurvTpnHuuyelyM26zYbPZEAQBn9eL0ylgNpt53mrFYhew2hzY7HbGxsYYGRlhZHQUu+AkFAwSCATweDxDTU5OYnr9s+946+Gv7D36jevfPqH/w1MOvv6dw+//4PKHX3Dh8ha79z+i/82/+ytfPmbn5EdevfsJqdkCM4kEqqqiKAqapmE6fPwXR6d/cu3hKde+OmX/5AHb926z/s5VSj2DYi2JvlCgsb3G1c8fcPOnpxw/+Zsbj34hns3j93oQPR7cbjeiKGLafO8uRk+ncT5NZTFOth4gkbeiZkbJzjso1ALkKhKJoki8LJJvSswud4jWm0iRKWRZRpIkIpEIoVAIU2/nNZp6hO5SDMMI0Kq5SJdcZEoWtOJzJIoWsiUHs5UJcjUn8aqLYMaNTfKiqCqJ/yJHo9Eh2HR46wb3mmf5dMHKx+1n2a09w3zlHLN6njl9jpyeIlmVKJUd9KpmNmrjrNWdLFb9aDEFNab+73IIvPXBTfqVM9xu27nTHudAt7DZmmels8HLxjqr7WXW9SCrtXNcLAtcKVs4bgm8WfOQTapkClUiioYsR4Zg09vH7/JieYoLDZWdVoCDppNXWiV2jQW2Ootstepc163sVc6wUbRxqTrJgeGnVwowl4oRVeOEJWnY8OCOpqM771OqJGk1cjSaM6w2p1mvi2xWRbbrEke6wH7VzF7FzFLFS7cSZqWrkS7EkKelYRGDX/T5fPj9fkwnP5+y3M3zgpGk15mi19UwaiJrxbP0Ww76hsC+4eWNtpdLSworzSjnjTSrnQSaEh46GxQyiDuYpuWLL5HSJGKyj7jsGkoJ24mFBNJRES0qkpcniAQnSEcm0KZFFDlEcSZMbkYim82QSqVJJhND4D8i0sJ/mDThkAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Paint Tree&quot;
        title=&quot;Paint Tree&quot;
        src=&quot;/static/006ba7e22e501947b80e57c430fef1d7/5a190/painttree3.png&quot;
        srcset=&quot;/static/006ba7e22e501947b80e57c430fef1d7/772e8/painttree3.png 200w,
/static/006ba7e22e501947b80e57c430fef1d7/e17e5/painttree3.png 400w,
/static/006ba7e22e501947b80e57c430fef1d7/5a190/painttree3.png 800w,
/static/006ba7e22e501947b80e57c430fef1d7/c1b63/painttree3.png 1200w,
/static/006ba7e22e501947b80e57c430fef1d7/60fa0/painttree3.png 1315w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can add details such as grass and other vegetation in almost the same way as trees. Select the detail tab, select a grass texture and paint away.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 412px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 135.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAbCAYAAAB836/YAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAD+0lEQVRIx6VWS0hcVxi+G9euXelCrSM6PmZ07sy9d0bneZ33jK82VGxKylCaQmkhptCCUKntZmghoNCiCF25sIFIs6qlgYLFndqkNYXaWIg1ZohZBEydfOX7zWlvrLHQHvjmP/fc83/nP//rjhYOh2An/TAME9lsBqlUCoZhQNd1+Hw+kQp+v/+vde4JBAKwLAuZTAaRSETmmmEGEOzX0dvbi2AwiFAoJAqJRAKFQgEDAwPI5/OCeDwuz0NDQ0JOkJR66gCNyqYZlEWewMWenh5RzmazsG1bSCj7+/tlneSKUFmtpMafQiEv1gwPDwtJLBaTA0zTlEMIPqtDud7X1/cPyJVJqKwgksmk+JEbaBFxmjJd4wTXeJAQkiCdTgsZJYkp6excLicWK6WzIIQ+n45EwoZt/22dspTgnMRqnQdxrm7hdAcDo/HHjkcRi/Sht8cjEf4/0AKGifjI24i9OAHDHoNpGGL6f8Fx2vhNRF95H8nX30Pk3OV/9ZUK1GmwrCA0PWAhef4t5EqvIfrSOwiFzna8ZZmwxCLjGXCduajpfgOJc+8iff4DxIfefCb3ToKOT6UzyOYLT1EUmSsUkcsX5J1UitfrhcfjFckqYRlSKvCZ7yzDj/K1dXx6Yw8ffrmOj67dwsfLP2L66ibKX+/gynf3j/OQ5UQw3wgWuvOZ82g0ilQyibXNn3F79yFurG1i5fsNfLv2A75Z3cDtuwf4Zf/RMWGxWBQMDg5KCRKcs2YpWZKqmn66dROVe7u4ubGOyv493N/7Hft7u3j4oCLQ6MhwOCxWURKM2MnS4j76cWfnNzx+/Afu/HoHDw4OUH3yBBxH1arMNQaBFUHZ3t6O5uZmtLa2wqfr6HC74XK1wOvxiA9JOjk5iXK5jKmpKUxPT2Nubg7z8/NYWFjA7OwsNLfbjZqaGoyNjWF5eRlLS0tSXpqm4ULpIj658hki0ZikDKuK1no8HnR2dsKn+9Dd3S3zrq4utLW1Qauvr0epVMLJEYtG8PKrb+DzL66iWByU4JCQzYJu4Vy1OdXSJA+ZFtvb20JyeHgo4FhdXcULLS1g87h+/StcmpjA6OioBIhW8mbKMgW6ReNVKpWKkFSrVRwdHcl8a2tLrsYsGBkZkbRhrdIqSiqflq8ag0DfKUKCY2ZmBk1NTXIVnk4FFoGSzo+XE1pjYyPq6uqwsrIi1hGLi4uora2VqNMatVkROdsVrSLUO0lsWtnR0YHx8XFJ8IaGBgE3kfB5oC4DxMDQcvpV8pAVwVykX7hoPO2JVDDO6I/q88G5y+WS/JVKUe2eDmeJ8XRe43mEKh/57WbKcB91mJPSbZztiVHnIconzu+vE+pfg9qnLP4TinYjV5PckwIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Paint Grass&quot;
        title=&quot;Paint Grass&quot;
        src=&quot;/static/41e2636df55710f4b379cbcc421ade8a/9e32a/paintgrass1.png&quot;
        srcset=&quot;/static/41e2636df55710f4b379cbcc421ade8a/772e8/paintgrass1.png 200w,
/static/41e2636df55710f4b379cbcc421ade8a/e17e5/paintgrass1.png 400w,
/static/41e2636df55710f4b379cbcc421ade8a/9e32a/paintgrass1.png 412w&quot;
        sizes=&quot;(max-width: 412px) 100vw, 412px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACrUlEQVQozz2R224bBRCG97I3tXdtRyo3RW2Spklbx0ptr+09H32K2/i8Dk28btaOYwep7iESSNBetQhxgQQCISTEK/AAvA2Cx/iQt4SRRpqrb75/RjBdG1kpUVZL6IaC5btoWhE5v09BfkxZLVJQZB4e5Nh7sMPO3n1U3UDTDRRFoVQqkcvlyGazyLKM8ObMxguPmLwMWF20CC6GnL9/x+VywOJVwPjlM05XAaNlh9GsSzAbYXs2tVoN3/cxDAPTNLFtG9d1EX4Pt3gdqrxdOPwy2eWb04d88d23fL1w+OGPP/n+t1/58MLly69OWF7WWb0Z4VcVNM2IjfL5PAcHBxQKhXgW6v4mXf8uUeseTm2XYWeXUXcP+0mW6OpzVlcBg/5tGsEu9f42zf4OnlOII5umgeM4sWGlUolPIBjGNllzi8Vom15/i/Zgk+DZXVqfbdJ+eote91MOh49oDnO0ggL1QZ6ar9JoNKnVqnHsa+g6vtCb3MKfb+CcpzGWadypSDW8iRcl8aYprFma9liiFX3C4fM7dI/v0WhYWN5hbHgNUlX1o6ESQyTcaYrKcoPy5Qb2PBPDnEhCXkhUFinMmUT1LEH15AZWv4VzdIZSKaGq2v9PWbdQnYg4UQrzPI2zhvzXbiRRn6SoRbdxphlqYZL6qYg1SaJV8+iajWWZMWwd+/rrgjURUWYpSvM0pYsMxjSFF4rYZyLafIPG3MaP7mCHN7GeS2hra/sRR0/aGGszx0UuFtF1Hc/zENzTBE6YwIhE9EjEHSfwxgn0qcTjZQr5QmK91AiTqPM0D8Y3mF+FrOvHn36mPwhoNhuUy2UqlTKC/TSJ0RHRuxJGV8LqiBg9ifIwRf44Q+E4TaUnoXYk5GGG+70E7ZMmf//1D6vVC/b399E0jWKxSFEu8S9Jl49S+XRwJAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Paint Grass&quot;
        title=&quot;Paint Grass&quot;
        src=&quot;/static/312c2f71f9f19221c8b45271a8bcfcc1/5a190/paintgrass2.png&quot;
        srcset=&quot;/static/312c2f71f9f19221c8b45271a8bcfcc1/772e8/paintgrass2.png 200w,
/static/312c2f71f9f19221c8b45271a8bcfcc1/e17e5/paintgrass2.png 400w,
/static/312c2f71f9f19221c8b45271a8bcfcc1/5a190/paintgrass2.png 800w,
/static/312c2f71f9f19221c8b45271a8bcfcc1/c1b63/paintgrass2.png 1200w,
/static/312c2f71f9f19221c8b45271a8bcfcc1/f8836/paintgrass2.png 1214w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Build and run the app, and enjoy your new terrain in VR.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAP/2gAMAwEAAhADEAAAAZ+FMMtJgv/EABoQAAICAwAAAAAAAAAAAAAAAAABAgMREjH/2gAIAQEAAQUClaxTNzLdlvT/xAAZEQADAAMAAAAAAAAAAAAAAAAAARICIVH/2gAIAQMBAT8BlLRGPD//xAAYEQACAwAAAAAAAAAAAAAAAAAAAhITMf/aAAgBAgEBPwGbaWsf/8QAGhAAAgIDAAAAAAAAAAAAAAAAACEBMRARcf/aAAgBAQAGPwJ7gssbZHMf/8QAGhAAAwADAQAAAAAAAAAAAAAAAAERIVFhMf/aAAgBAQABPyGuIzvpBitTNtW2R7iS4UGf/9oADAMBAAIAAwAAABCjL//EABgRAAIDAAAAAAAAAAAAAAAAAAABIWGR/9oACAEDAQE/EElAoYf/xAAYEQACAwAAAAAAAAAAAAAAAAAAATFBYf/aAAgBAgEBPxB2YNT/xAAaEAEAAwEBAQAAAAAAAAAAAAABABEhMVFh/9oACAEBAAE/EGIjKQIIKIKSqTJuvb5Gw+C+wYdlj1G6x18iRn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terrain in VR&quot;
        title=&quot;Terrain in VR&quot;
        src=&quot;/static/409b7680f1e272806e79fe8e4889fedd/4b190/terraininVR.jpg&quot;
        srcset=&quot;/static/409b7680f1e272806e79fe8e4889fedd/e07e9/terraininVR.jpg 200w,
/static/409b7680f1e272806e79fe8e4889fedd/066f9/terraininVR.jpg 400w,
/static/409b7680f1e272806e79fe8e4889fedd/4b190/terraininVR.jpg 800w,
/static/409b7680f1e272806e79fe8e4889fedd/e5166/terraininVR.jpg 1200w,
/static/409b7680f1e272806e79fe8e4889fedd/b17f8/terraininVR.jpg 1600w,
/static/409b7680f1e272806e79fe8e4889fedd/3ed48/terraininVR.jpg 1918w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Adding a Sky Box to Unity VR Game]]></title><description><![CDATA[VR game was set up in Getting Started article. We will be adding the following skybox to the project to make it look a little more…]]></description><link>https://www.ginocoates.com/2021-12-21-VR-skybox/</link><guid isPermaLink="false">https://www.ginocoates.com/2021-12-21-VR-skybox/</guid><pubDate>Tue, 21 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 774px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAC/UlEQVQ4y43P3W8UVRjH8fk3TCDYV15ENGg0EY1BGgMEbCAaQzQaiwlRUYKaWm1qY2Kshu2L9V3Y3e5uRbFqgRbrCq2L7W5tQ9nZrQutF97AnDlzZs+UUg2ZG75mukU3TS+8+OTk5Dzn9zyPsb7XpiIqqI5YrDlmUR0RPHRCcnu4dK8MCyrDFlURiw09grsTgpqIxaqjFtu+FWz5WizWVUVKGUZtj6A2JqiMCLZ8I2gZdZiyFzj8S5GaqKA2KhZD18UEu08KdvQLVh0TbP/B4YkBhztipZqgdm1UYGyIWazvsVi31P3xUxYvJK/wzqhFU0qyKS6oCAsqIoKNX0l2nnRoGVOc+3OOaM7lsf7SBsH0QZZxV8LiznjQxWJ1RHBbWFAVtdgUD1xdXCNYsTps0TBk05SyefHnq9T1XaHuO4uDwy6P9NlsjFtsPi4w7jsu2Nwr2J906JryODDs8sAJSWWwbsKmNiFYExPUJARVcYvVPRZrewV7BmxaxyShCckzSZt7+2zqTtsY9adsdvbbPD1k05gStI1LWtOK+kHJPd9LHh6QPDvisHVQsusnxb4Rlz1nJQ0pydYzgvsHBA/+KNmWdNgx7GA0/Sp5/bzNgbOCvYM2j56W1A9Jnhtx2J102JWUHB61OXRe8O6E5EjWpSHtsD2l2DumeDKj2DeueOo3xf5JF6M5o3gjrWjOODRlFK+lAw6NacnbEw6vjCueTysOjju8PKF4aVLx6gVFY9blzVyRt/JFmqeLtBQ0rQWN0W0W+dAs0pHVhLJF2pcEk7yfLdJmFnkvp2nLaz6Y1oR+13QWNF2XNN2XNR/NaD6Z9fjsjxKjM6vpzmm6TE2nqekwNe2mJmRqjuRKQnlNe17TMa3pKmi6C5qPL2k+vezx+YzHl7MeR2c9vpjxMDLqBtk5n4sBz2fK87lQbu4/U9d8Li7JXvMx531y8z75eZ/p6z6T3g2Mv4G/yiwsc32ZhRWU/zfcmze5Ra3AKbPSu7tMKRD49wTU/3SrvjzjHxzah3AD2M/9AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Skybox&quot;
        title=&quot;Skybox&quot;
        src=&quot;/static/99476a0da9fc586dea9fc8add8cd5b84/41d3b/skybox.png&quot;
        srcset=&quot;/static/99476a0da9fc586dea9fc8add8cd5b84/772e8/skybox.png 200w,
/static/99476a0da9fc586dea9fc8add8cd5b84/e17e5/skybox.png 400w,
/static/99476a0da9fc586dea9fc8add8cd5b84/41d3b/skybox.png 774w&quot;
        sizes=&quot;(max-width: 774px) 100vw, 774px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;VR game was set up in &lt;a href=&quot;/getting-started-with-vr-unity-quest-2&quot;&gt;Getting Started&lt;/a&gt; article.&lt;/p&gt;
&lt;p&gt;We will be adding the following skybox to the project to make it look a little more interesting.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/2d/textures-materials/sky/fantasy-skybox-free-18353&quot;&gt;https://assetstore.unity.com/packages/2d/textures-materials/sky/fantasy-skybox-free-18353&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Add the skybox to your assets.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 416px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA5klEQVQY012Py0rDQBRA80W6KyLdWPwFH1hB+g2CC/ELuvCFutUa7FpEP8IXulMRxahgQyudzCRiM5M5MkMw6OJwGO65AzfoyYLHvuFpUHEfG+56/xwbHuK/ndv7SApMYdElwdpZRm1dML2X0NhNmNoRLBxKFkPFXMdZMt+RNEPJzL70c9e5fmJDsHqa8Z1bok9DOrIEKycpY+0h9W3B5KbwUTNUtLrKf7R0pH7fswfSN476lmC8PWT5OAVr6auCLLcEzwPD1avm9l1zU3IRac5fSkeVL6Oqcf31m/an58bylVtG2vID3TkOst7Es7UAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;AddToAssets&quot;
        title=&quot;AddToAssets&quot;
        src=&quot;/static/a2f6e3b614b59a8b065b4b825a9b3323/b0122/addtoassets.png&quot;
        srcset=&quot;/static/a2f6e3b614b59a8b065b4b825a9b3323/772e8/addtoassets.png 200w,
/static/a2f6e3b614b59a8b065b4b825a9b3323/e17e5/addtoassets.png 400w,
/static/a2f6e3b614b59a8b065b4b825a9b3323/b0122/addtoassets.png 416w&quot;
        sizes=&quot;(max-width: 416px) 100vw, 416px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Open Package Manager in Unity and download/import the skybox assets into the project.&lt;/p&gt;
&lt;p&gt;Import the skybox that you want to use, here I&apos;m using FS000_Day_05.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 349px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAzCAYAAACXICiDAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAF3klEQVRYw51XW2sbRxjdP9MQ20ksW3uTtKuLJVkrWZa0uqxuju+OmzilEHBamlIoaQl9KYTQvPXV0As0jiEptIXGhkCeCn3oY2mhj02cSyEUAqeckUaRrYvVCA4zK2aPzvfN+eYbKf++fInDp8/x4sU/ePz3IZ49e4bHj5/g8Mkh/tfn+XP8dfs2lPt7u/ju6x18v/ct7n6zg/v37mHv7h7u3NnF/v4BDg6OYn9//ygePMDBw4f4aXcXO1tbUBa3r2Px5iOMv/0DrA9/hhHPQVenYZgmVNUPv78FVVUxPT0tRk3TeqAbBgKxGJQrn3+FTx8Axo1XSHwJ2O4a0skYUk4as7OzSKfTcBwHqVQK2WwWyWQS4XAYkUikF7YNpXR9B9ceAev3gNqPr+A0VpDLpFFwXczPz8N1XRQKBczNzaFYLApiy7IEqW3bPVAu3foEN//8Fbd++wU3f/8DmVodpq6Ll0KhEILBoIB85ksk60GkBcW2DASDU0g5M7BtS7woIUOLxWKCLBAIDISpmTBVE0owGEIoZCMSiaLZaGI+mxU5Y+5IREUkNU0ThmH0ha7qCBfCiJajUPiF+PVgUJBsbGygVqui6LqCSO6wrusdkLyDgAl9WkfyYhLpK2ko8peZJ75sh0LILl5AoeyhkM+LTeBux+NxMVI113eTkjC+Hodz2WkRSlLm4vSpU0hVmnC9OopuAbl8XuxyJpMRu8x0SMWCONBF+I7TCrn7F89OTCDVXEO+UkWxUBDWyeVyIh35fF6AcyIajUI39KOEUp0knBgfQ9pbQLFah3uMkMbmnIrpT5pc5HUY4ZmJcczWV5EvV0XIkpAh59s5lTkXdjHNERTWFnsUyhxyHLgpAxXWlnsUsvQ4MlRZjolEYsQc1pfhHlNIQuaQz1TJZ1rpREKhsLqIQqU3h1TFHMpjTJg8cFLI42NwGqvCh8dzyFA5p10IlqVhGi3CjYGE40h5CyhUakd8KEPmTlcqFZRKJWFyTddOIhyD01zrIaRCmT+WH/PHWheHw0kKHW8BrlfraxuqYg5Z9zz6OzkcGnKf0mPI+a66JvHMzMybl57MIedUKttBJ4f9CA3DhOb3w2muI1/2eowtbcO1sj10drkvoWlCU/2Yo7H72IY7K0uvO6ohCg3omip82E8hcyjLjvMTS48K/VNTyDRWehSSkHNC5nPkWu5XeiRhyDwDaRtxWxi19NJ9So+KSMhneVNgq32j0iNIyNwRJCbYtDon9oml59VEyCSi5zjKM5G5I3hAjKRQlp4krFarYpRtgKrkETbUh92lJzeFRJ7ndRRKL9LgBHOpTqlIbCQG77JQ2C69bkJ5yJKUfuSYSCag+oYQihO7sdbpKXNtQuk/5pC7y44nb2TalIbE+hCF6er5zuGQ7SLknMpI0rk5jNT16it9FRLccWkfWX5DQ251vaWBCknCUR5l8UR8hBxWl1oH7IAcMn/yxO60gMGXpXGkuw4HkrApSVUMmf1EXo25MWJTjit8fZ17C0nvvCi9UtHFfC6Her0uQqU6WXblcrnTY/w+/2tCqY475/P5WidzqQy3VBaLaVzZP1hqVCchyi8WPWpsox0qXxwbG8fm5qYoNSoiiaq2/9y0r8P9/vC0almGbOgwDUPkY2zsNFaWl1H1KuI6HItGYOgaAqYh1vQFa5kK12fgXE5BMYIWjKANOxzBpG8KqxubKFUbyORcBKwI9EAIrTUDELKgqUHELzhIvTsP5VxlG5OV9+CLzOHaZ19g4YNbyF6+ITCz/jEmK1dxrrwNrhPjMUxWtnGmcBXh9y8h/tEWFN2KwZ5J4azPjwsXt1CsNpArenCyBZh2DIYVg25Fj6H1vYAdg2ZEEFtPI7mVhWLoqmidApoK1d+aM3f9YLKxi3XTYp2cq+IdHQqtwAt5KtWyBm3D53C7Z9DAVhuchywL8Xiis47WyYh5Rmyswr7QbDZFeXHB6uoqlpeXxUJaSd4FJfgSy5DrlpaWhL3474tzevM/St15VPjgI68AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ImportSkybox&quot;
        title=&quot;ImportSkybox&quot;
        src=&quot;/static/56f6ca78714ca16e2fcfd2af723088bc/e9bf8/importskybox.png&quot;
        srcset=&quot;/static/56f6ca78714ca16e2fcfd2af723088bc/772e8/importskybox.png 200w,
/static/56f6ca78714ca16e2fcfd2af723088bc/e9bf8/importskybox.png 349w&quot;
        sizes=&quot;(max-width: 349px) 100vw, 349px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To use the skybox, go to the lighting configuration screen.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 555px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 102.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAEIUlEQVQ4y52U3W9TZRzH+1ewwRW3RMQV0cEYhrcbNZAYX4BuMAbrWDeY+m9oQmI0xkh8i3phIgIy5GLd3Fg7RgzqNnr6srWnXdee9bz0vLVdu7B8zHnGigOvPMknv/Oci09+3+f3nMdn2za12iq27ZAvlFBUg7JRZn19nf/z+EZHw4yFw4xNRolKy0RjefLLChXXZW1tjUaj0eTx48eUSiWmpqaIRqOCSCSy5d23Z/cLvLh7NydOnEDTVLKyzPLyMl7nHpZl4TgOhmEI6cjICNu2tdDa2kpLSwvbd+xgW0uLoHX7dny7du1i586dnDx5Ek3TSC8uEnsUQ5IkdN3AcWzUkko8JiHLWcKjo7zs99P20h7aX32F1w51cuhgB4c6D/LmG6/jO3LkCAcOHOB8by+6rpPL5sjKWfJLS5RKquhSKSrkcjnUUok5KcGXP9/h29uj/PDb73x3O8z3d8YFP96dwNfZ2cm+ffvo7u4WQi/uysoK1WpVRPWErutSqVSoVlyKukERKIGoBSAPLAG5dfDt37+ftrY2AoEAuq6RjCdENE/i7d/mXpqWJaRSMsUvY+PcvjfFrYlJbo5PcGNsXHzz8Hlx/X7/E6FOZnGR2b9nUVVNSD2Z16kn94byYGaGgWCQ4cEhroRCXB4YYMjj0iXBVqGmIcsycSlOoVBoCkWHptkU9vf2cLm/j6Ggx8Ut/KcwlUyJiXsRN2N7wnqjwcz9afrPn2Wo3xNcEAwGe5s8F1nOZEgmkiykFlAUpdmlZZk01tZ4MHOfYE/XhqDvPKEmPYLnhN7BTiSSSDFJSC1r43CXyyb1ep3paGRD6Eku9jzHM0INOZMmNv+IbDaLaZaxRWTryR7WiUbu0Xf2NKELZxno9ehuErpwbqtQ1XQychY5u4Rp2Zi2s1EtG6NsUlttMB2NirgfDA0wPNjPcCjIlYGgqIObkff6/bxzuovrsybfTKb5enyB63M2N6UaN2JVbkhVUb319b80vgin+PinCJ/djfHVvRzXJmSuTeYIR/542uGZQICsojMbz/DnfIrciknBcFnWHUHBq5pNXrXEei6eYS4hIytlNLeBatcprKhPhV2BAKahsZhKkstksMoG1YpDxbGpuE8xNBWlkCcpxUgl4sTm58nnspR1ldxS7tk91FhYWCAeT6Bpurh0vQlvTto7PkVFoaSV0A0dvWygGTqarqHpOmWz/K8Ou7rEJIvFIoZRFpeDR61WE3jv3rHxbh3Ltlitr2K7Nm7Vxa24OK4j1htD2buXU6dOEZckHj58KH6vaHSaeDxOOp0WZDJpFKXIyK+3+PyTq1RrFaq1qpBbtontWAJx27S3t9PR0cHRo0c5fvx4k2PHjnH48GHOvPs2Hw4GeT/Ux/Cli5x77y0+vfoRhmnguHZTZjs2/wCUuxmhnLskPQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;RenderLighting&quot;
        title=&quot;RenderLighting&quot;
        src=&quot;/static/db0e422dd8251d4e46836d6acc4b926e/cd039/renderlighting.png&quot;
        srcset=&quot;/static/db0e422dd8251d4e46836d6acc4b926e/772e8/renderlighting.png 200w,
/static/db0e422dd8251d4e46836d6acc4b926e/e17e5/renderlighting.png 400w,
/static/db0e422dd8251d4e46836d6acc4b926e/cd039/renderlighting.png 555w&quot;
        sizes=&quot;(max-width: 555px) 100vw, 555px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Create new lighting settings.&lt;/p&gt;
&lt;p&gt;Click on the Environment tab.
Select the skybox material you imported earlier and select Skybox as the source for environment lighting.&lt;/p&gt;
&lt;p&gt;Note unity documentation seems wrong here, they tell you to do this on the scene tab, but the settings actually live on the environment tab.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 617px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 126.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACgElEQVQ4y5WVW1MaQRCF599oeUERYQdY9sKyIAoBLR9SRUxVLo95yeX/n9TX2tbWBo0+HGaZ6T5zuqd7JuRlpbKqFUdjdbtd5XmmwWCgy8tLw2QyUVEUyvNcWZYZ+GYOu7OzM0Ov11NVVQrD4VCTNFWapua83W41m83MoSxLrddr3d7eGjabjTabre7u7nR/f69ZXQv/0Wik8Xis6XSqwM9qtdLNzY3t3O/3FWNUjImSONQgiaZkMOjbyDqKOp2ORdTrXejiAnSVJIkCyq6urlTXtal8JIuKSVQcRhVloem0UlGUqqqZ0nSi45NTnZx2bHQcHR2bUiMkNIBaRghRk6aZPj780O7LT336Cn4ZHr79/gefv//RYvlBgdhdIYQkHemA/NT1TNOytPxm2RMm+1EUuQKOHAInhDoUNwkXi4WWy+XzIb0EhFkO+YAMzOdzGyFi0fNJbph7DdiTpoADCiBrkrDIZrvdzkrp+vraKuE1UFYWMqo8LCdkHmUYEfZLQIifAQgoYRJwKCTf5aOQ2vTC3QfWsKGrDg8PFUgo6gjJFWLkJBxS+/8+YvKIb4CdkCkXFjxkDLynm3mlK+hb5hmb3/gEiEgoCv3oHZC4ApTRdo7z83NrPYf/t04hmSQYpU7kI4rcmG9X40oZfYODg4NHhagD5KlZOjjzv6lonzLs/AoLTUVtdX7afsu8BAj9lgpNojbZ883zRljrtR3bSt+L8D8171UcvCDfGpLn1PPqbw+wy6Fp4NgXCs5sTiM4KDf6GFB2dF3wV83vO8Z9rYUdJPS73+x+VwJI7ZGimL0O/dbwDZrAmY5i3Z9SNmnDCDF02W7ob7DDHdrzzbcawr84UlUHwNHvLgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;EnvironmentLighjt&quot;
        title=&quot;EnvironmentLighjt&quot;
        src=&quot;/static/32e2d37372769828f833884a6159a2e7/bc962/environmentlight.png&quot;
        srcset=&quot;/static/32e2d37372769828f833884a6159a2e7/772e8/environmentlight.png 200w,
/static/32e2d37372769828f833884a6159a2e7/e17e5/environmentlight.png 400w,
/static/32e2d37372769828f833884a6159a2e7/bc962/environmentlight.png 617w&quot;
        sizes=&quot;(max-width: 617px) 100vw, 617px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now return to the game and the scene should be rendered in the skybox.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACUklEQVQoz43R3UtTcRzH8f0pFdWmznO2s53tnMPCLHIqZKbYAzjN4U1PFARWUDeGBFIQVIhBVmp0UyJhSSpdZOrIZhahMwtvfDjHc+YkLyaUvWM2ZZFWFy++Fz9484GfrTAYJD9/N6qqIggCHknC7XLhsNtxOnMRJBmPEiBb8rNdlHHJfpRdeWQJbhyChEtWcLo85IhuckQJ276CIGUVR8jP34umab8EAgT8XgLl1WTf/YK9aYLCh5PUPp3C0TTBjttjiHfGUVpiZDVPsqf1E87mGI6mGDavLKOoGj6fH78/TVFRJAHP/koc7RZbWuYo7jS4MjTPoS6DbfcMstsNSrtMLryOc74/Tk6bwc4HOjZZ9qIoCoriT18FRVVRPCK+kkqcbToFj2cJdujY70+T0zrN1pYZslpnCHbMUNo5zaneWaq755AezaSC8mpkbd1a0CeJ5JWFKH9mUvtijov9OlU9BvITneLnOge6dSp65inp0TnYp1PUq1PYZ/wezFzodYsUVIS4HIlz7pVO+KXO6X6DMwMGJwcM6iI6l6LznBg2OR41OfvOou6D9ZegS6TocIhrowkaoyaNIyZXoyYNIyb1Ixb1oxYN7y2uf7S4MWZxczzOrVh846CqarhFgfLKY0Ti34iYSQbNJANrrCSDVpKheJJIfJk3C8u8TSwzvJDcJKhpiIJAdThMEljK8HUDme9/fErKWjBUU4MF6N9X0FdSfmzKSFsPZtLSwapwmARgpln/web1eldX+Xy+damokJvL0VCIqaUlJhMJPqcsLv7TTz64bkazetq7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;skyboxRender&quot;
        title=&quot;skyboxRender&quot;
        src=&quot;/static/27a879b2dcc8d7b27e42b30783b75f24/5a190/sceneskybox.png&quot;
        srcset=&quot;/static/27a879b2dcc8d7b27e42b30783b75f24/772e8/sceneskybox.png 200w,
/static/27a879b2dcc8d7b27e42b30783b75f24/e17e5/sceneskybox.png 400w,
/static/27a879b2dcc8d7b27e42b30783b75f24/5a190/sceneskybox.png 800w,
/static/27a879b2dcc8d7b27e42b30783b75f24/c1b63/sceneskybox.png 1200w,
/static/27a879b2dcc8d7b27e42b30783b75f24/c425d/sceneskybox.png 1279w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Getting started with VR Development with Unity and Oculus Quest 2]]></title><description><![CDATA[This article is all about how to get started building VR games on the Oculus Quest 2 using Unity. I followed this guide on the Oculus…]]></description><link>https://www.ginocoates.com/2021-12-20-VR-getting-started/</link><guid isPermaLink="false">https://www.ginocoates.com/2021-12-20-VR-getting-started/</guid><pubDate>Mon, 20 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACLUlEQVQoz12Sy27TQBSG86oIxAoJRF+BBWyQkJC6ALHgAWhZoapIbBFK0iZNc0/jOBfHY8djO77O2P5QEnrjSJ/O6J/RrzM6f62qKna16wcO54fl+z6u6z7S7t9XPPSo3QsPgbIsCYJgz2q1wrIssixDKbW/+79ufWq3gipgm0KcV8RZgWnOMU0T27bxPI8oikjTFK31vqs0oVQZZZ5SFfrOuFaWFWvTZDI06M22zDYwMBwG/R5CCBzHQUpJGIYkSYJWiiiOsZcmReAQWHOM4Yyb0QqtNDVjahL5PmEK3eGSpYSL9oRup4PnSaT0SZKUPFdkWU5RFDjCod9uo0OJORmTRxFxlDPsj6l1O31msxmW8BlNLcwNjMaCnz/OaDQbDIc9ms069fofxuMB3W6H87NzjN4IFQYsDYONEMznJletNrXlwuLk23ea9TqTqcBwwZbw+fgrz5+84OjVG44//uLLp98cvX7Hs6cvef/2A7EXkPoBwlxwedni9OSUyWh8WErgB3Ra10yGU0yn3H/bckuajQUXzQ29jmJwrblqebQbBqEbkobR3nR+M+OyeYW38Q5Luc2Q52UMbiTGWmOuNQtbI7wK4WpskSJEitxkbH2F78T4zpbACVlM13gieJzDXZrltmS8KjHWBaZdsBAFK0djuxrX03hSI3dmMiOUKaEXI0WIu/CJ/OQ+h7cT6mKXv4okP5DmFZmqyFWF2qH/oUq0Kg7kGpUpCl3cTfgXoa46HRa+LJMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Defaults&quot;
        title=&quot;Defaults&quot;
        src=&quot;/static/141e997536f88ad850f7a008086d7fb7/5a190/quest2.png&quot;
        srcset=&quot;/static/141e997536f88ad850f7a008086d7fb7/772e8/quest2.png 200w,
/static/141e997536f88ad850f7a008086d7fb7/e17e5/quest2.png 400w,
/static/141e997536f88ad850f7a008086d7fb7/5a190/quest2.png 800w,
/static/141e997536f88ad850f7a008086d7fb7/587b0/quest2.png 970w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This article is all about how to get started building VR games on the Oculus Quest 2 using Unity.&lt;/p&gt;
&lt;p&gt;I followed this guide on the Oculus website:
&lt;a href=&quot;https://developer.oculus.com/documentation/unity/unity-gs-overview/&quot;&gt;https://developer.oculus.com/documentation/unity/unity-gs-overview/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Go here and set up Standalone headset and unity as defaults.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 280px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 115.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACjElEQVQ4y61UXVPaUBDN768v9i/YNztFZ/pUp77UEQdEIYGQD0iCgnEsQ+69ScAPkNPZjReDpR3sNDMnu3dzs/ec3WSN5XKJ/3HpPAbdnp+fcX9/z/bp6YntbDZb+bR5sViwr9dkdUwnI8sJPc/Dh50d1Op1fKlU8O3oCPv7n7G39wnfj48RxzHOz89hmiYajQbber2OWq2GarWKMAyhiXHC+XyOPJ/i8fGRmc4IsxnyPGdLTOgZMSb78PDAoDVZel9fRlmKpq8laf/tngLLNUt7CIYQAmmaYjqdIssy9ilG7GgtpeSYlAJZKpCqAuy/QEnB+wiGUgo3NzdcjyAMMRqN+IHjOKgcHPAzwmQiEI0kRrcSw1uJaCgxjCWuY4m7seQDiYhBLIIg4OJTEiowJfxxcoLd3Y98AMWEVGh5Cp2+ghspmJ5C0y1Ah6RKQgj5KpkWZMuSx+MxEiFYOseyQiqBfA31wo4ZEhty6MUkSTBJksJOEmZK8SJGdjOSpHiXE9JNJ90EXextsWJIzSG5uru0/tMhf8OKYXwb4/rqCj2/hyiK8PPuDkK+dm9rhjpzEAZoXTTw9eAQlcohqqenSLP83dJXDHV3i49Yolzbf6qhhu7WOraXbhSb5Ypd0ZScLUH/dnrf1jVMU4XrqwFs24brOrC7DjzPxWAweC9DsWIXhQEuLi7hOg4ajUtYZhO+3+OE4qUUayWR8rc6G7IUpImjZ1wx+wpfy9fzsfCzjazXJA8GEU9jy7JgtkyYrSZal0202zY6loWObeOsegbbsmA73maGOiGPsdEQjuOi3+/D931Gt9NBt+ui7/voBwHa7TaXxO8FGz+tNcnlX48kZSWp1HmKU1nKw/et5F9VmcbZWUcyqQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Defaults&quot;
        title=&quot;Defaults&quot;
        src=&quot;/static/f4a4f01c8a22354931a8a47020f2d4ca/908b1/Screenshot-2021-12-20-142905.png&quot;
        srcset=&quot;/static/f4a4f01c8a22354931a8a47020f2d4ca/772e8/Screenshot-2021-12-20-142905.png 200w,
/static/f4a4f01c8a22354931a8a47020f2d4ca/908b1/Screenshot-2021-12-20-142905.png 280w&quot;
        sizes=&quot;(max-width: 280px) 100vw, 280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install Oculus app and pair the quest 2
&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.oculus.twilight&quot;&gt;https://play.google.com/store/apps/details?id=com.oculus.twilight&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enabled developer mode on the Oculus quest app&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 221.99999999999997%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAsABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAIEBQP/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHn+8bjkAuagAuMw0Mw/8QAHRAAAQQCAwAAAAAAAAAAAAAAAgABEBEDBBMiQf/aAAgBAQABBQIRc0euYMsGXiIt3rFR5FtNq3Vr/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwFf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwFf/8QAGxAAAgMAAwAAAAAAAAAAAAAAAREAAiEQMHH/2gAIAQEABj8CVaswFE5vDTiFd97v/8QAHRAAAwACAwEBAAAAAAAAAAAAAAERITEQUWFBof/aAAgBAQABPyHITqQnKmULXg8jX4DUapZo2W3pxFNFDMfH7ypbKirsqbPQrs//2gAMAwEAAgADAAAAEAMNMBPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPxBf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPxBf/8QAIRAAAgICAgEFAAAAAAAAAAAAAREAMSFREEGRYXGh0eH/2gAIAQEAAT8QNDSwcfMVBAlnsvuE1QHQpi9jDHs2AAcu8C+HYgyLZiNeYCNKFALlXzApqn+TQRPSeZlZaikiyEYdqf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Settings&quot;
        title=&quot;Settings&quot;
        src=&quot;/static/497e0f3ed579e39a87e422ace27a77e4/4b190/Screenshot_20211220-135143_Oculus.jpg&quot;
        srcset=&quot;/static/497e0f3ed579e39a87e422ace27a77e4/e07e9/Screenshot_20211220-135143_Oculus.jpg 200w,
/static/497e0f3ed579e39a87e422ace27a77e4/066f9/Screenshot_20211220-135143_Oculus.jpg 400w,
/static/497e0f3ed579e39a87e422ace27a77e4/4b190/Screenshot_20211220-135143_Oculus.jpg 800w,
/static/497e0f3ed579e39a87e422ace27a77e4/47311/Screenshot_20211220-135143_Oculus.jpg 1080w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 221.99999999999997%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAsABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAECBAX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHmVCUAAAzjQzD/xAAYEAADAQEAAAAAAAAAAAAAAAAAARIwQf/aAAgBAQABBQLm1MplM//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BX//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BX//EABQQAQAAAAAAAAAAAAAAAAAAAED/2gAIAQEABj8CF//EABwQAAICAgMAAAAAAAAAAAAAAAAhAWERQTAxUf/aAAgBAQABPyFeJo2kTp55UdlhYf/aAAwDAQACAAMAAAAQwAAAAM//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EF//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EF//xAAdEAACAgEFAAAAAAAAAAAAAAABEQAhYTAxUYGR/9oACAEBAAE/EGKH0BRgMHSCFRTjbGravC0XmXP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Developer-Mode&quot;
        title=&quot;Developer-Mode&quot;
        src=&quot;/static/881998d47b6d3221699a46c8254d2591/4b190/Screenshot_20211220-135159_Oculus.jpg&quot;
        srcset=&quot;/static/881998d47b6d3221699a46c8254d2591/e07e9/Screenshot_20211220-135159_Oculus.jpg 200w,
/static/881998d47b6d3221699a46c8254d2591/066f9/Screenshot_20211220-135159_Oculus.jpg 400w,
/static/881998d47b6d3221699a46c8254d2591/4b190/Screenshot_20211220-135159_Oculus.jpg 800w,
/static/881998d47b6d3221699a46c8254d2591/47311/Screenshot_20211220-135159_Oculus.jpg 1080w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Install Unity, I&apos;m using &lt;code&gt;2021.2.7f1&lt;/code&gt;. Ensure to include android build support.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 255px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 105.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACK0lEQVQ4y41UTW/aQBDl18OJY5qWa6OkOXDihoTEpSBuoLYHoAWVpMRgLzbGX2tjXjVjxjHEgaz0tLOzs2+fZ2ZdsZ0tnK0Lzw/ewA9C7DwfEvMRVLbuDqu1iR8/f+HPbI7J9DfGkymvv/f6WDw9cyCRfoS44u48PP9b4uvdHb40Gri/f8Dt7Wfc3Hxi38O3x1zlNULar5CxsR0YqzUTL18M/F08MV6MFfsk+Jo6JpRA+nRSWwZRdw38yWJEkYaOY0RaI0kSxHFcQPIOYiTJHvv9HkEQQm3sV0JyytBa83w4HC6CRhiGfFbrGKaljoTOlp0UNJlMUK1W0e/3+QCpTdO0FDRs22ZSUpsTUlFoNBoN1Ot1NJtNjEYj9tFFdJguk7loy0wpywkJNIbDIVqtFmq1Grrd7onCc0JZi9ITQsohSc42InQ6HRiGwetLZDIDZwoph7Qxn88xnU7hui6UUlgsFpjNZpyjIvk5cYnCrCimaWK5XHKiLcuC4zjwfZ/Vl6m8QOhyrt4bxXyVV/vwllDapuzApV5M06wf6UGcfDIpJFJpkzJb1kVkQlKEUSSELhPK66AgIpeXIG0jPVm8WNZpuufCmdbmtMpUhCAI+LDneVxpeYrr9TpPy9WiSGMPBgNuExrj8Ri9Xo9tIm6323nhMjIcccgvZUL5j5FkAW3STC1DjU4gxTRrHeU+AcVTrKU2r4SWpdjBONpqI7bKbKXydRGkjH5d9IP9D3urSZySKT23AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;UnityVersion&quot;
        title=&quot;UnityVersion&quot;
        src=&quot;/static/1e064fe5787caf12456f20d86dea746d/b8c8f/Screenshot-2021-12-20-135830.png&quot;
        srcset=&quot;/static/1e064fe5787caf12456f20d86dea746d/772e8/Screenshot-2021-12-20-135830.png 200w,
/static/1e064fe5787caf12456f20d86dea746d/b8c8f/Screenshot-2021-12-20-135830.png 255w&quot;
        sizes=&quot;(max-width: 255px) 100vw, 255px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 666px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAB6ElEQVQ4y52SXWsTQRSG54dV8R/0wguv/R1FeqNFsEV66b2/wGxDwQaaBHNdsq7BmBDI5gvysZ/ZmdnN7ivvxKkGLFIPvMyZ2TPP+ZgVL86aOL708fxygOP3Qzx9M8DR6wGe/NLRP7SP+4Fnb8d4eVaDOP3YxfktcNFQeHej8cpROKk9Up8ynF4DJx9uIAbfPaBS2EZrFDJGqSJUKgLyBKjkb5XZw0IO2ufrKwj3q4ckKxBGMZTOkSmNTCpInZu9VBpKF8ikRhDGiJPUnAXR3s+LEjrfGWDNuYK4cz3c+QWCMEQYhgiCAJvN5sCP4xhFUSCKIpRlaS7zTGtt/N1uD3QcB8L75qGqSkgpzWFVVbD2p08QL3L9m09rNBoQvV4PMo0wHo+RpqmpitWxAgvkmue5qVYpZc6SJLn3LbDdbkN4nmc2hMxmM9PWer02IpxJaATyjG0yAeM4BvoW2Gq1DoGj0chUYYMJ4UojaLFYmNGwTcIZ9yCQgdvt1rRhIQy24iXGMAn3TGD9A6Druqat1WqF5XJpZNu0Q6cxEb9xtrYjO0NbgAF2u11TGSH2V+HA7etZ8TJbZCy/2Uehb4HNZhOi3+/fZ/xfYxJap9OB+NLpYD6fg2Df9zGZTB6l6XSK4XBoGPV6HT8B7K0LhIOqBGMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Modules&quot;
        title=&quot;Modules&quot;
        src=&quot;/static/8687cab566fdc9796a220a6997fb605e/ace37/Screenshot-2021-12-20-140122.png&quot;
        srcset=&quot;/static/8687cab566fdc9796a220a6997fb605e/772e8/Screenshot-2021-12-20-140122.png 200w,
/static/8687cab566fdc9796a220a6997fb605e/e17e5/Screenshot-2021-12-20-140122.png 400w,
/static/8687cab566fdc9796a220a6997fb605e/ace37/Screenshot-2021-12-20-140122.png 666w&quot;
        sizes=&quot;(max-width: 666px) 100vw, 666px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;
&lt;p&gt;Create a new 3D project in Unity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enable device for debugging and development&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Follow this guide:
&lt;a href=&quot;https://developer.oculus.com/documentation/unity/unity-enable-device/&quot;&gt;https://developer.oculus.com/documentation/unity/unity-enable-device/&lt;/a&gt;&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Install Oculus integration package from Unity Asset Store.
Search for Oculus Integration - install the free package&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAC0klEQVQ4y6VTS0hUURi+ixRsIdT4mBkIUmIMyiLzwSx6bFq1aZxxmhlRCULCRQMGIbYRNYZoIVMtClJaRQY+CGGoaJGtphYuQumlJsOIM3rnvt/3zhfnjDMkLTvwcc/5f853vv/7/8scrqlBbW0tqqurwTAM3G43mpub4PF4SnATuOFuaEB9fT3q6uoqaGxspHcIqg5VlfbXIxGEQiFEIhFEo1H09fVRDAwMoL+/HyQf7ulBKBBAd3c3wuEwRTAUQiAQQDAYpPHe3l7EYjEw2F/FYhH/s8r3Gdu2YZomHNsG2f+9HMeBIstQFIXmDMOge1VVIUkSBFGEs0+mGg4s2wEjiiIymQxyuTzyOzlwbAEsW0ChUICsyFB1DYqmUnICy7JKXyKA52HxHGyaK1JiZn7xDdo6/PB3tqPN34XW9jZ0dXbA6/Hi7ugIVjbWsPz1C7ayGezmd8HzPH2MqJS5AnRRRHH/MUo4MTFJu3PsxBHEzngRPeVBk+8ojQ3eGoJu2BB4HqQSQRDBcTxYdo+WzhYKMEyzYg8BMz4+Xmn9teMu3DjrrZwHh+LgJAMCKcu2oegWJM2CYVowCQyTqis3hSpMJpNwuVxoaTmJS5cv4NxpH3y+FjqPicT9f5pEv0XQBwRBoI06QEgC6XQaCwsL+L25ibXVVaRSKbx7/xZrK9/x7OkMElMPkfqwjM31daQ/p7Hx6wd4QaAToOv6QUJyWFpaQjwep4a/np1FcmoKk2NjGL4ziqv+m7jSOoTzFwOYfvkCDx4/wvKnj8jndyvqqH+2XfKQBIjh29ksTRK1M9PTWJyfx6u5OSRGnyAx8hy3h+/hZ3YD33Jb2N7OQZFVcBxXUWjvjxNDu8Wy4AUJumlD07QSdJOan9vbAcvv0aElJUqCAEGUoOkGJSyrtMsKyV8iyzJUzYBpOXS+CCEhI5dURYFpWJUcIaDkqkaFlP0rE/4BzBmAGUJjlBIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;SearchIntegrationPackage&quot;
        title=&quot;SearchIntegrationPackage&quot;
        src=&quot;/static/4955d19d5933829f030ac1e0df4ab8ef/5a190/Screenshot-2021-12-20-142231.png&quot;
        srcset=&quot;/static/4955d19d5933829f030ac1e0df4ab8ef/772e8/Screenshot-2021-12-20-142231.png 200w,
/static/4955d19d5933829f030ac1e0df4ab8ef/e17e5/Screenshot-2021-12-20-142231.png 400w,
/static/4955d19d5933829f030ac1e0df4ab8ef/5a190/Screenshot-2021-12-20-142231.png 800w,
/static/4955d19d5933829f030ac1e0df4ab8ef/c1b63/Screenshot-2021-12-20-142231.png 1200w,
/static/4955d19d5933829f030ac1e0df4ab8ef/20785/Screenshot-2021-12-20-142231.png 1307w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;
&lt;a href=&quot;https://assetstore.unity.com/packages/tools/integration/oculus-integration-82022&quot;&gt;https://assetstore.unity.com/packages/tools/integration/oculus-integration-82022&lt;/a&gt;&lt;/p&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;Import Oculus Integration using package manager&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACh0lEQVQ4y1VTbU4bMRTc4yCBBCpkd+1dr+3d7PcHmxAaqERa4Ae9RCOqSlWReoRyBI5Br8Efco6pxk0qGmn07Lf2PL+ZFy8vKzRth64fkE1zCCEQRZGLUkrEcQylYgRBAN8PXJxMJhBC4vDwEPv7+zg4OHA4OjqCF0kJaw2SRLnLYRg68CIJsyxDUTauaF0V6PsOWWYRhj4iKSDCAGHgIwh8d99LkgRFUTikaQruSej7PsJQoGS+GJDmA0xaI8trmLREmneo6h5K5/CFgR9IaJ3AU0ohz3MHVmA7JCPYNvPapNC2hJ22MGkBk1bQaQ1tcyS2hMlaRLF2XXrUy1oLY4yL3BNvCcuqQdPUaJsKXddi6Dv0XYN+ux4GylE5Dk9rjbqunVYEX0lQQxKWZYmibNH1p2jb1slCbSfbLvytWVyTy5PbF5KEzvHjZHtQSOlIHx9/YfP6iufn39hsNliv185VdvLWREeoVYyymKLIM2RWI/RPEEyO4Z8cI5bCOfn09AT+Xl5eXHx4eMDe3h6EFNtX+k57Gurl/QKny48O+fAeuhphmrmL1ewSw+IS1zd3uP/2E+uvP/Dl/juWH24hZIJEKUdCY9lhalN4QXsFff4Z5vwOcrzFu2aFY6JdQYy3sPNPmI0zjOOIqqrcePV9j/lsjuVyiYuLC6yuVri5vsFisYBnkhh1WaAqpg55ZmETBf/kHUQwcXJ0XYezszNnHk2hm9SL4Mu4J6bT6d+xMdZCa4NYKUgZOVAbCk0CgodJ8HZOd2ZwzTzd9zgqHA1qwWpMElEUI9GpmwCScWTcCP33r1IQkUEoon/kHj/w4G4OdwLHsYI2GZqmcTm1NWAHdrYrTrAgdf4DtFX8lYx6KLIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PackageManager&quot;
        title=&quot;PackageManager&quot;
        src=&quot;/static/45440da8cdc35b741d1dadd491e143b6/5a190/PackageManager.png&quot;
        srcset=&quot;/static/45440da8cdc35b741d1dadd491e143b6/772e8/PackageManager.png 200w,
/static/45440da8cdc35b741d1dadd491e143b6/e17e5/PackageManager.png 400w,
/static/45440da8cdc35b741d1dadd491e143b6/5a190/PackageManager.png 800w,
/static/45440da8cdc35b741d1dadd491e143b6/27b7a/PackageManager.png 804w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I got this warning when importing. Clicked Use OpenXR for now, but its says its easy to switcvh to legacy backends if I need to later.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 399px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 91.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADRUlEQVQ4y5VUW27bRhTVDryF9CvdQPqXfqVbSIF0H15ElmD425/5MVKkelCWSFmURIqUKPEpik9J1tt6WHJiGKe4V2CKwkGBEhjc4eXM5bnnnJmcWK1iPp0hTVJMJndI4gSHwxH/93n+9g3Ox4/IiaKINE1hGAY6nQ6m0ynu7+8xm82wXK6wXq+xWCywWq34fbFYYrlc8jt9y+Jut8NyOEROEAT88eEDfnnzBj+/fo2rqytEYYxSsQS5LuO23oBcr6NYLCGfL0ASJciyjMpNFZJUgySKkOUmVqs1Nvs9coVCAb+/f4/z83P8+vYtLi4ukCQpPn/+EzWpBqFUhlASoKpt7oA6URQVrVaLo9JqwXUH2Gy3WK9WJ4S/vXuHn169wtnZGS4vLxm+ZZpwXQ+O7cCyLMznc6YiazEblKORzXOVSgWe60KSJNRqNYzHYwyHAXRNQ7PZYmTtdpvbr1ZP7Z2ifEKoqPyTrGiuWq3i+fkZj4+PPI7HIzodA4JQ5iLUrmU7SJIYYRixgBSjKEQQhPB9H7PZ/J+ChPDh4YGVJPUyVWkTIQ2CAGk6wt3kjouNRiNMJhOMxxPEccxriaJ/ITwcDt+52W63iMIAgnAD+lapiKw4KSxKEgr5AsrlCsrlGxQKRchyA91ul4XcbDYvC2YeNPt99IwedL0L27ZYYdt2YNs2ojjhzZlI5F1CyqL8CKHvD6FrOtptDaZpwjB66PV6UFWVBaI8DXKBZVq4m0553w9bJj6IaFJY1zvQdR2qqsHoGjzXNB2KonDBJB2xnej0/CeHcRTDcVwWhNDGUcReJJH8wQBRFCMKKWczBQTiuw9fFtwgihL0en0MBj4X9TwPruPAcZxTEcviHM3NvsnKE6cvENJ53O/3MIw+vnz5C3QTNRtNNBsN9iTRcFurc2yrKlRFRf22jkajidl8flKZfEgFsxuEkmEQot83oXd7cAcB7ncP3A61RpRQ3O22nKP1GTpGSMft6emJFxI6Wvz18QhvskG+5SDf6ONTpYNuMINpdFkgOpbEKdlr4HksVBhFp4LX19dIkoQ5IXUHvo+h70M1bBSlFkpSA3mxBaVro60oqMsyU0AWIkE0TWNz0y3keS7+Bum8+e81nh2+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PackageManagerWarning&quot;
        title=&quot;PackageManagerWarning&quot;
        src=&quot;/static/8ad58651b4fa0bdb9fc187ed92e02c4f/a307d/Screenshot-2021-12-20-143441.png&quot;
        srcset=&quot;/static/8ad58651b4fa0bdb9fc187ed92e02c4f/772e8/Screenshot-2021-12-20-143441.png 200w,
/static/8ad58651b4fa0bdb9fc187ed92e02c4f/a307d/Screenshot-2021-12-20-143441.png 399w&quot;
        sizes=&quot;(max-width: 399px) 100vw, 399px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;8&quot;&gt;
&lt;li&gt;Configure build settings for the Unity project&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 732px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 83%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAARABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHnTcIoQNhP/8QAGBAAAgMAAAAAAAAAAAAAAAAAATEAESD/2gAIAQEAAQUCrRYc/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAFBABAAAAAAAAAAAAAAAAAAAAMP/aAAgBAQAGPwIf/8QAHRAAAgEEAwAAAAAAAAAAAAAAAAERECExcUFhof/aAAgBAQABPyFsR0RsbvhHOB69rTP/2gAMAwEAAgADAAAAEAAHP//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8QH//EAB8QAAICAgEFAAAAAAAAAAAAAAABESExcYFBUZGhsf/aAAgBAQABPxCRaUzll+7gksPwES9Amp0a3BlSLkZNHwOnR//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;BuildSettings&quot;
        title=&quot;BuildSettings&quot;
        src=&quot;/static/7e9dc715b6388508d959f0dba5598b00/5c23a/Screenshot-2021-12-20-144408.jpg&quot;
        srcset=&quot;/static/7e9dc715b6388508d959f0dba5598b00/e07e9/Screenshot-2021-12-20-144408.jpg 200w,
/static/7e9dc715b6388508d959f0dba5598b00/066f9/Screenshot-2021-12-20-144408.jpg 400w,
/static/7e9dc715b6388508d959f0dba5598b00/5c23a/Screenshot-2021-12-20-144408.jpg 732w&quot;
        sizes=&quot;(max-width: 732px) 100vw, 732px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;9&quot;&gt;
&lt;li&gt;Configure player settings
App name and version&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 83%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABwklEQVQ4y4WTWbKiQBBFa0GNDwooChBExXkMpw/3v4/bcfOZBm1j8HEiCzK5lROmXaxQlGNEUQRrLbIsQ5okco7jWLAvnz4ncYwwDBEEfxAEgdjRaATnHMxsNkNRFPB5LmKzeYvleos8z+G9F1vVDVyW4ye0+Ik6hJEwoo0sUudhttutCKpAVdWoJ42IE75zPkecpIhs/EHSIYZNUpjdbofJZIKqqoSyLH4z9h4ZoehLmJf+4j/I4VwqcWa9XmM8Hkv9hE4+U8C/BIn6le67NE2lx4yXksuyfJdHwbquxTKQ2dLHocmArBUopEPSdjHOnE4nKVkzYdlsw3K5xHw+B4c2RNu2wnQ6heGBt0nznRPB6/UKXsTsF4uFCBP98BP1M7H3UFSQ5+fziePxKGKr1Uoypjj7vdlseqGf2Rrtm/aAAzkcDnIze6nwIrXf4Lemu28quN/v3z3hrWqHYLuMrke3h7fbTbJkyaRpmvf5Gxwi4/4RZIZcIfaKTpaoCz8EY/ltb4aXywXn81mazaGwfNoh/suQgrzpfr9L2ZwuA9kfWmbeh/pkD/syfDwegi44h6Ti3+gV1D+F2REN1pUZgmvzFxChSoc3A5sSAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PlayerSettings1&quot;
        title=&quot;PlayerSettings1&quot;
        src=&quot;/static/bb40f14fc829f0680219bdc7b75f828e/5a190/PlayerSettings.png&quot;
        srcset=&quot;/static/bb40f14fc829f0680219bdc7b75f828e/772e8/PlayerSettings.png 200w,
/static/bb40f14fc829f0680219bdc7b75f828e/e17e5/PlayerSettings.png 400w,
/static/bb40f14fc829f0680219bdc7b75f828e/5a190/PlayerSettings.png 800w,
/static/bb40f14fc829f0680219bdc7b75f828e/dc61a/PlayerSettings.png 1147w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;App identification and location&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABuElEQVQ4y4WUWW4CMRBEfR34QGIb9oFh32FYBPe/SEevo4ocQ8hHaWxPu7q62naYzWbW6XSsXq9bo9GwZrPp41ar5Wi327++gJhqtWqVSuUHtVrN10NRFNbv963X6znxZrOx6/Vq+/3edrudLZdLI4bEwnw+d2g+nU5tsVg4T1itVk4mQgIej4edTic7Ho+2Xq99jQ1AZCCeM0Z92G63lue5jUYjGwwGnu1yudjhcHAyFJI5yzJPyCYsSUG5TohCNhCMT6h5Pp9OCBn/z+ezJ1EibGGdhDGGw6EFBhB2u11XgUKVzCYAEaRUA/A2BZ5jW8AnmFEIIQ243W52v989CG9IClGqSJBaRAUUQSSFEEImRRASQ/n/wRWyCYUxIceGMlSejhDefQJ7A0Qwxx7SlLIsnYRS8FOE8vEdnBDvQKwQD9VRSqbz8uovUPJkMvlWKEDIIo2SKgLT2xEfbs35OqHUxSXTFErWwVZJjHUrUrD+QsiYGzMej71RHHRAoneIHw7gdzkl5BoiHy8B5IB1jTVHEVWoZJK8lEywHgTKZxPEfFOwToxenbeEBOFf3BQ8+tRdjhOxHOwvvS1YxP5xS4IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PlayerSettings2&quot;
        title=&quot;PlayerSettings2&quot;
        src=&quot;/static/58d9ad20e00d71b2f4c448cba2384aac/5a190/PlayerSettings2.png&quot;
        srcset=&quot;/static/58d9ad20e00d71b2f4c448cba2384aac/772e8/PlayerSettings2.png 200w,
/static/58d9ad20e00d71b2f4c448cba2384aac/e17e5/PlayerSettings2.png 400w,
/static/58d9ad20e00d71b2f4c448cba2384aac/5a190/PlayerSettings2.png 800w,
/static/58d9ad20e00d71b2f4c448cba2384aac/84bf8/PlayerSettings2.png 1162w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;10&quot;&gt;
&lt;li&gt;Install Oculus XR Plugin&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This will install the plugin from package manager.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABGUlEQVQ4y+WSyW6EMBBE/TksF4Z9B7OjHOD/f6ai6sgzZASJkrlEyqFYbNfrctuq73ukaQrP83C73eD7PsIwRJZloiRJEAQBoigScY7/juPAtm2RZVlwXVc4qus6FEUhZg60bYtpmjAMA8ZxRF3XMv4ss54qy/IuNc+zDDIJVVWVgAiO41iK5Xl+Kc7Tw8J8K5pNJQI4MS8LtNZomuZHYgi1LItUeSQsMY0D2AoueNYVjEHoUeu6SnQDrNse3fAG3Y3QupWkRgQe+3UUQxGqtm2TD8I+epkhSZn40dejzg7InIEcCq8Nr8MnkxiTS+NZkTtw33fZO0+IMpNXSb5NyAd7eLzILwG/2sKvgGf9eQl4NP5N4NH8P4DvUhA4ENXov+IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;XRPlugin&quot;
        title=&quot;XRPlugin&quot;
        src=&quot;/static/0555044f9245fa3ca5859f555e7d2147/5a190/OculusXR.png&quot;
        srcset=&quot;/static/0555044f9245fa3ca5859f555e7d2147/772e8/OculusXR.png 200w,
/static/0555044f9245fa3ca5859f555e7d2147/e17e5/OculusXR.png 400w,
/static/0555044f9245fa3ca5859f555e7d2147/5a190/OculusXR.png 800w,
/static/0555044f9245fa3ca5859f555e7d2147/cdef6/OculusXR.png 1163w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;11&quot;&gt;
&lt;li&gt;Select target device&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA7klEQVQ4y+2SS26EMBBEfSAQAok/2HxNYDFw/8vUqHrkkRMl0mScZRZPuBt4LjeoxX6g7XpkWYosy1CWJYqiQJo+asK1D3tJkiCKoidxHEtfTdOEpmlQ1zWqqoLWGsuyoO976bE2xqBtW3RdJ/hrMgwD6OGzat93edEl443runCeJ6y1Um/bhnmef4QBHGpdV0nIdBSudsPtdopIdjVGEozj+BKSkLGZkGj9EDyP5x3tFdRxHDITylxK4jb4LYrz4tCd0F3fRXGoeZ5/2sWJ305IqZtBaErl/1skWPhd7BCp+trwpX8mDPrK/8Jg4R0bQjg7D6YBxwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;TargetDevice&quot;
        title=&quot;TargetDevice&quot;
        src=&quot;/static/8b5e3f13ced7623987e61cc8c76422d3/5a190/targetdevice.png&quot;
        srcset=&quot;/static/8b5e3f13ced7623987e61cc8c76422d3/772e8/targetdevice.png 200w,
/static/8b5e3f13ced7623987e61cc8c76422d3/e17e5/targetdevice.png 400w,
/static/8b5e3f13ced7623987e61cc8c76422d3/5a190/targetdevice.png 800w,
/static/8b5e3f13ced7623987e61cc8c76422d3/9937c/targetdevice.png 1156w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;12&quot;&gt;
&lt;li&gt;Configure Rendering Settings for Oculus Quest&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABpklEQVQ4y42T6Y6DMAyE80a9aUtPzoReoB4S0r7/c3j1WXKF2LK7P0bBjjOZiY1Ls1yOSSrr9Vrm87lEUSSHw0GWy6UsFguNbe2C3GQykfF4rBiNRpp3IQRJ01RWq5WSgP1+L5vNRguI2ZvNZjKdTt8rhAgg5ps1WkTiLpeLFEUhcbyR7XYrcRzL8XjU3G630xyKuQAQ22qgHlEIcefzWU6nkx6GDOuovt/vwmXee8myTPI8/xXUAEeQJMnbMvK54Pl8SlmWqpS1+93NdQGXg7Wua7UBIe+GwsfjoQVYocYO/AXHbRxAIYCQjev1qkq6dv5j22GPg11CU8j7sYdKYHaHoAqtQ3TLLBNTQNcADQMW92F7PJszEps3vlFGU7CNWlNAHpW+B815r+PjzGrfctu20jTNm9CHIFV1kqqqfiBUlU4Gzj4Slj7Iq/2Surnr7XrgUkvhK8nLoGsfZThLkmYDhKVXhYwTVmxWrdsAe33wloOWX6+X/ik2tLwnF9xuNwUWu+CP07H5REiBKbR/nQ5at4c6zqT8qpBZpDHYtlEaGmr26PI3yGgzXPNdcLgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;RenderSettings&quot;
        title=&quot;RenderSettings&quot;
        src=&quot;/static/5a3b682b8154fab8e26341d96f379a4d/5a190/rendersettings.png&quot;
        srcset=&quot;/static/5a3b682b8154fab8e26341d96f379a4d/772e8/rendersettings.png 200w,
/static/5a3b682b8154fab8e26341d96f379a4d/e17e5/rendersettings.png 400w,
/static/5a3b682b8154fab8e26341d96f379a4d/5a190/rendersettings.png 800w,
/static/5a3b682b8154fab8e26341d96f379a4d/1132d/rendersettings.png 1158w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;13&quot;&gt;
&lt;li&gt;Configure quality settings&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAB0klEQVQ4y41U2XbqMAzM38AjkEBWEuIlix2glNMX/v8/pmeUhoY2d3mYWJJlWRrJCYyxUEojyzLs93scDgfUdf2iE5R/6tvdFtvtN9I0RdA2DVRdI0mSJxiMm5MexzGOxyOUUjDGoHWt6LTP/SRg3/fixM1pgxlQn2zUy7JE13UwncH5dkZVVWKfLpwQDMMA7z1Op5PcWhTFS/B5hvTrfQ/Xu0U/ImB2WuvnQa68nelPoC2KIsmIOv2oh2Eo61wOyMvlckGe509OeEldn0SWA2GI3W6HzWbzxLwZEyRg0zQSYM6FtQ2MtSIzK/rc73fhsG3bRdCH+wE/1tqXko3tYGwjXWXHD1/2Ofl/QsBm8OCcZKM1rDHCJanY77854vo3BNOgzseEJfAS6hxg6o/HQ8p2zi2j7zF4j+DneCRJDGP0SEOcIAojlGUFrQ3SNBuRjUg4AZyELznL86WACby/4HK7wrQGSlWwVqFttciqKqGqcdVVJVDlaKO8EDCFc1e0Q4dmsOidwe3m8PFxxdubw817vHs/rsOA+/ks8mT/FZCQJuUFoh2bECHPCyk7jpNnmUtg6Yslkz/nvfx1pre8Xq+xWq3+icWAHKXxtdQvT+1/8An8YR5ajqlqZwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;QualitySettings&quot;
        title=&quot;QualitySettings&quot;
        src=&quot;/static/c0fecedf4d6fb78ebf31992f18e78ced/5a190/qualitysettings.png&quot;
        srcset=&quot;/static/c0fecedf4d6fb78ebf31992f18e78ced/772e8/qualitysettings.png 200w,
/static/c0fecedf4d6fb78ebf31992f18e78ced/e17e5/qualitysettings.png 400w,
/static/c0fecedf4d6fb78ebf31992f18e78ced/5a190/qualitysettings.png 800w,
/static/c0fecedf4d6fb78ebf31992f18e78ced/b7936/qualitysettings.png 1155w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;14&quot;&gt;
&lt;li&gt;Create an android manifest&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 752px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADM0lEQVQ4y4WS309bZRzGzzX/gQbp1lZApFtpG/qDMAjxzsRobHSYCO4v8IoLd8O2G0IUtRZhbPGiFiZCREa92OUQNje7ltKWFnrOAU4Hpe1hlDbMdIPwMecYgplT3+TJ8743nzzP9/0Kb7/zLh1db3HGXI+p/g0cTg/1TRYsVjumhibe85hoaqzHfN5G3dmzWLo/xXDOg7ftFc6/aaLO2IDB+Dqv1p6h+ZwN4fMvvmR07AbfjoziHx7hxs3vGBkd4/rYTb7y+bk+/DU+nw+ff4RAcJxbMyHGJ6eZDYWYnJ5hfGqG4A9TBALfMzn9E8Jv9++zmk4Tj8eJLS2xFF0imUgSjTxiU31Kfr/K82qVZ9VnrMsy4toqkphhbS2DmMkgihkkSdSl3YXW1lY6OzpwOBy0t7ejvZubmzHU1bGweA9ZXmdjYxNRkmhoaKC29jVMZjNGo1GXyWTCYDBgNpt1F9ra2mj3eOi4cIGuri46OztxuVzY7XZikTA7Wwr7T1RyjxXsNhvNFgs1NTUIgvByud1uNGkQp9Opu9vtwe1y8fPdCHdTBe7JZR6IRb7xDzM0NMTVa9fo6+uj/8oVBgYG6O/vZ3BwEK/Xi+ByOnXQCexEDoedO7+GWVyWiWRyxLcqpLdKrEsiO7k8+XwetaiinePjY93n5+cRXgSdAh3EY1FKakGvXP3jgNz2NivJFZKJBIl4AlmSKZVKqKrK4eEhU1NTp8AXwdr7fa+X7u5u3T+7fJlioYAsy7oya2soikK5XGZvb09PGAwGNaAG0Cqfgk7gVquVlpYWGhsb+fDiRT1hNBLVFQk/QhQlKpWKDtRK//hvCU8/x422BTabjd7eXnbVor6LkiiSVRQq5TJPDw4olf5KeGti4p8J/w7UXIO2WK183NPDdi7P8nKSeGKFRDKFJG+Q2ymg7u5RfX7E7O05hFOY86VAl8uNrcXKBx/1cGdll5kH68z+ruh+O5xlLrxJKPKYhewR/onQ/wO1ytqSf3LpEoXiLunVjJ4ulc6gZLcpqE/Y269wdAxzoV/+e4YnlbUZar8dj8VYXFggEY8TfviQaCRCOp0ilVohm1UIBAL8Cev38KRPDEybAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Manifest&quot;
        title=&quot;Manifest&quot;
        src=&quot;/static/8a970cfd00facfe3b44beb649a853cc1/442cb/manifest.png&quot;
        srcset=&quot;/static/8a970cfd00facfe3b44beb649a853cc1/772e8/manifest.png 200w,
/static/8a970cfd00facfe3b44beb649a853cc1/e17e5/manifest.png 400w,
/static/8a970cfd00facfe3b44beb649a853cc1/442cb/manifest.png 752w&quot;
        sizes=&quot;(max-width: 752px) 100vw, 752px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;15&quot;&gt;
&lt;li&gt;
&lt;p&gt;Delete the main camera in the scene&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Drag OVRCameraRig Prefab from Assets &gt; Oculus &gt; VR &gt; Prefabs
Configure as per &lt;a href=&quot;https://developer.oculus.com/documentation/unity/unity-add-camera-rig/&quot;&gt;https://developer.oculus.com/documentation/unity/unity-add-camera-rig/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Drag controller prefabs onto the OVRCameraRig&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ensure to set the Controller property in the inspector to match the anchor&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACz0lEQVQ4y6WSTXfaRhSG9Xda28QIgSTEhwDJIDsBf4TYlvjGiNgQp7YxC5Ow6Sq2Wzen3bSrtufUm57smlXbTf/Y086Acdw2qyzec+8dzTx6595RXr2acHp6ytnZGacnJzIfjUaczPPx+ZjxeMzL45dSg8GQwXDI6Pyc6XTK9PVrJpMJFxcXcp+Sdz1UNUYkssKjSETq5sdfuP39L356/wc/v/+T79/9xg9zfXv7ju9uf6VzPMKykmQyGeLxOIlEAtM0UYqVfWz3CWbSwrTSUpPplJu3b7m8vubN1SVvrq4W8fKra77+5oYw7JNOpyXQMAwpAVacyj6FSg3dLhLPuMQza/jVHXr1PbrB7lzPOKjN8k6wS9jwebpZJpfP47gulpWS7gRUMed0Q08s1PSrDHpNDjt1jroNev023aYvc7E27LWoblVwXZeCu4aVSkmn4tqKgOm6ji7iXI29p/Jgv13jsB2wvfWMyuYuR906YStgcNBgq7yBYZo4jkOpVCKbzZJMJu+BC7CuU78DtgKetwN2tzdpBfscdmr0W74EVjZKaFpcQoTE+QcO7xo7A+5w2AkIW74EHHVrPG/7sg6b+wy6dSobHlo8/sCIcKwsirtemiYtv8qLsCmdiMPiqoNeg2FvtnYcNtl8fA805q0yDB1F2NU0TSoajaKuLOEWbDzHZs228NYcik6eQiZJwdJx0gaVxx75XFZecTbQ2QzEQJVcLidhItbrDXZGX5LO2hS29/AOviCXzZK2LIp+B69/jheeocZmBhbt+hAoRi8eZCqV4km5jNd5gZVKUyhvUwq62NkMdiZNqRpQavTxGn1i2kPg/VATKMViUVoXGyKRCKtLn6GqKtFHEVaXl4iKXFVZXVlmdflzKfH9/4E6yvr6unzloojFYvLvMv47j2mzWtP+A/wwSqAohEux8R7wcX3MoXyHtm3LRPTxk4H/XPlvkolVeBaIMS8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Controllers&quot;
        title=&quot;Controllers&quot;
        src=&quot;/static/c2584e633cfc563d21c56084908449f2/5a190/controllers.png&quot;
        srcset=&quot;/static/c2584e633cfc563d21c56084908449f2/772e8/controllers.png 200w,
/static/c2584e633cfc563d21c56084908449f2/e17e5/controllers.png 400w,
/static/c2584e633cfc563d21c56084908449f2/5a190/controllers.png 800w,
/static/c2584e633cfc563d21c56084908449f2/c1b63/controllers.png 1200w,
/static/c2584e633cfc563d21c56084908449f2/29007/controllers.png 1600w,
/static/c2584e633cfc563d21c56084908449f2/0f903/controllers.png 1719w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;18&quot;&gt;
&lt;li&gt;Create a plane so you don&apos;t fall into oblivion.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACr0lEQVQ4y62T3UuTcRTH9w+13LOX523P61xbG9oGc87p5vKFrBQrYjqnUesiQruqkVSIISGSRRYUdFE3XXXVhRCIkaDQRTScbyAm3zinLZMkuuiBw3mec36/L5/zfH8/h2HZCIYi8Hg8HE6nE03xOGZfv8H0wks8fP4KU09fYHLuCe4/foa7s/OYnJ3HlfFbsGwbPp8PLpcLgiDA6/XCIUkyNMOEqqrw+/2QZRnhUAhjxRGMjhQ4RobzKAzlUSwMc/3qaBED5/sRaGyEYRgsRDCiKMLhdrv5pR7UNA0dvdk0sqkEMi0x9HWmcakvh650Ah2JGLraW5FJtSAYPAFd1yFJEpMSjIOUqUBi9YZl6Ljc34vx0igW5mawtPgBq2uf8P7dW8zcK+PGWB7dmRRs22ZBQ9c5a5r2p6AoSlBlCdN3JrC3u4O9/X1sbayjsl7FxvYO6Fld/oiBnixMy+KRw+EwZxI9mtA00JNpxaMHZWxtVrG+UcW3yldsbm9hdWUZpeGLaEucwslImClJjOgOEdbD5xNhmwbO9WTR2RrH1O0JbFYr2P2+hy9rK7g2NIjujiRy7Ukmov9GhlJWFOVAkMgoyCRDU3Eml+ZoTzSjPH4dn5cWURq6gEwyhrNdHci2JRAMBn+RkZlsSiAQYGUSImHV74ei+mHqGkzT5PFVRYalKZBkmTdTjfq0j0ZurB0fJgyFQrzI7fYg3tyE3GAeicJNSG4XksUJRNOnEbRMhJtiXE+VytCjcTQ4j7GBBEFkdR8c0WgURMnI1FRUyJoBkc6VZvC3JImQFYW/FcOC1yfCLQiHzm/dVAdZbteuEI3tamjgoKvE77VrRfln7zgEwYX6hfjdUCaMRCKgsQmbKGkzLf5bCLV8cHYPhFnQsiwmJMf/SbC25kjBOh0V/4fgD3nuRg1SMe97AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Plane&quot;
        title=&quot;Plane&quot;
        src=&quot;/static/6e4b6466f3552d76a6749624735eb867/5a190/Plane.png&quot;
        srcset=&quot;/static/6e4b6466f3552d76a6749624735eb867/772e8/Plane.png 200w,
/static/6e4b6466f3552d76a6749624735eb867/e17e5/Plane.png 400w,
/static/6e4b6466f3552d76a6749624735eb867/5a190/Plane.png 800w,
/static/6e4b6466f3552d76a6749624735eb867/c1b63/Plane.png 1200w,
/static/6e4b6466f3552d76a6749624735eb867/29007/Plane.png 1600w,
/static/6e4b6466f3552d76a6749624735eb867/adef7/Plane.png 1716w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;19&quot;&gt;
&lt;li&gt;Build and Run the Game&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Make sure Oculus device is switched on.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 448px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 92.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAA7DAAAOwwHHb6hkAAADE0lEQVQ4y5WU3U4bRxiG9yI4apBIctDUxoALEmAjqspIXECqROpVtJV6D62qIFEJCKUtoZHapGkPolS5gkTtQQIHtb221z9r1rtgs/+7NkFF5KlmiKMk4IpaerSzs/reeb+fsfL0yVPsgwPMlonruhjNXVqGgeO4HMj9Fr7vY5kW7fY+vuvhuR7t/Ta2bePYNmEY0f8pRTPEapl4Qsww8HyfZkOnWtFotztyX7yLg0RgvVqlXm9QLpfZ2d6RhxfyRdqdDkEQoFj7HapalUa9jmlahGEoHXmeJ5/iXWRQLpUJfJ+trS1mMxk+vXmDT65fJ5dbYC6b5eOP5kmn0yiuY1NSVSqahq43OTw8pNvtEkWRJI5jfM+jkC/I9fLyMkNDQyQTH5BMJLhy9SqXR0a4cnmE9y5dQhE1KP6dJ58vUCwUqVQqGEZLBgt3ApGKYzucnJywtLTE8PAwqbExEokEyWSS0dFRSSqVQpGNME4LL9ai0OLZF5IuuzGmafLixREbGxtMT0+zuLjIwsLCW+RyuVOHhWKRTueAXq8nU34zbXHAbrPJzvNtgiBkfX1dupmfnyebzZ5BcRwbVVWp1Wqyy6JrekPHsvakqBAUe029yT/Hx6yurspURfDMzMwZFNd10HWdVsugXqvLuWvUG1jWaceFSyEchIGcs5WVFVm3ubk5Zmdnz6CIkSipJYlW0WQzROrvNkXU9UKCIuVKqYSmVSmpZRkoXPXd9RFzeSFB13GoVmtyuEXthDPhSDp75TD8P4KeY1PTNGpaRV43MejdOCIOA3pxRGd/T35X83l6cczttTXZlIGC63863PpD5dbjEl8/zLP2pMOdnR6bzyM2t2N+eBby3V8O3zwqsvks4suvbjORSpDJDhDcdY8oGQ4t70hihsdY0clr9uKXkqbdIwS+XfteXrnsIIc/393ipzs/8vuD+6f8eo/fzuHB/V94/OghX3z+GRMTE2QymfMF3792jXT6Q8bGx18z/sb6rb2xcaampmRgf5DPCE5OTsrTLoK4HX2x89xJQfEfNujjIP5L8F8fth9F+zucvwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Build and Run&quot;
        title=&quot;Build and Run&quot;
        src=&quot;/static/42ed9ce06e1d0df9bf9e776a4056897e/33b38/buildandrun.png&quot;
        srcset=&quot;/static/42ed9ce06e1d0df9bf9e776a4056897e/772e8/buildandrun.png 200w,
/static/42ed9ce06e1d0df9bf9e776a4056897e/e17e5/buildandrun.png 400w,
/static/42ed9ce06e1d0df9bf9e776a4056897e/33b38/buildandrun.png 448w&quot;
        sizes=&quot;(max-width: 448px) 100vw, 448px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Once the app is deployed to Oculus you should ne able to see the controllers and animations.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAwABBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIAAf/aAAwDAQACEAMQAAAB6WOiJS2X/8QAGhABAAIDAQAAAAAAAAAAAAAAARITAAMQMv/aAAgBAQABBQKskayVZj65/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHhAAAQMEAwAAAAAAAAAAAAAAAQAREgIQITFRkZL/2gAIAQEABj8CaJbmSIz6W6u0L//EABsQAQACAgMAAAAAAAAAAAAAAAEAERBRIUHx/9oACAEBAAE/IRnQGzmXIGtKe5iQjvP/2gAMAwEAAgADAAAAEGTv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qh//EAB0QAQEAAgIDAQAAAAAAAAAAAAERACExQRChwfD/2gAIAQEAAT8QawIT0hdYwUSjAjx3c/LfcUCC3rnTkLYV78f/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Running&quot;
        title=&quot;Running&quot;
        src=&quot;/static/34641681c914194befa3ae33a03678fb/4b190/RunningVRGame.jpg&quot;
        srcset=&quot;/static/34641681c914194befa3ae33a03678fb/e07e9/RunningVRGame.jpg 200w,
/static/34641681c914194befa3ae33a03678fb/066f9/RunningVRGame.jpg 400w,
/static/34641681c914194befa3ae33a03678fb/4b190/RunningVRGame.jpg 800w,
/static/34641681c914194befa3ae33a03678fb/67471/RunningVRGame.jpg 1072w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[JavaFX Scrollpane Draw At Centre]]></title><description><![CDATA[I'm creating a JavaFX application which has a ScrollPane containing a Canvas. I wanted to render an image at the centre of the viewport…]]></description><link>https://www.ginocoates.com/2021-08-31-javafx-scrollpane-draw-at-centre/</link><guid isPermaLink="false">https://www.ginocoates.com/2021-08-31-javafx-scrollpane-draw-at-centre/</guid><pubDate>Tue, 31 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;m creating a JavaFX application which has a &lt;a href=&quot;https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/ScrollPane.html&quot;&gt;ScrollPane&lt;/a&gt; containing a &lt;a href=&quot;https://docs.oracle.com/javase/8/javafx/api/javafx/scene/canvas/Canvas.html&quot;&gt;Canvas&lt;/a&gt;. I wanted to render an image at the centre of the viewport, regardless of the content scroll/panned position.&lt;/p&gt;
&lt;p&gt;I created a helper method that takes the scrollpane and the content and returns a Point2D containing the centre point of the currently visible area of the Canvas.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;package com.example.infrastructure.utilities;

import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;

public class ScrollHelper {
    public static Point2D getViewportCentre(ScrollPane scrollPane, Node content) {
        Bounds viewport = scrollPane.getViewportBounds();
        Bounds contentBounds = content.getLayoutBounds();

        // horizontal parameters
        double horizontalMin = scrollPane.getHmin();
        double horizontalMax = scrollPane.getHmax();
        double horizontalValue = scrollPane.getHvalue();
        double contentWidth = contentBounds.getWidth();
        double viewportWidth = viewport.getWidth();

        // vertical parameters
        double verticalMin = scrollPane.getVmin();
        double verticalMax = scrollPane.getVmax();
        double verticalValue = scrollPane.getVvalue();
        double contentHeight = contentBounds.getHeight();
        double viewportHeight = viewport.getHeight();

        double horizontalOffset =
                Math.max(0, contentWidth - viewportWidth) * (horizontalValue - horizontalMin) / (horizontalMax - horizontalMin);

        double verticalOffset =
                Math.max(0, contentHeight - viewportHeight) * (verticalValue - verticalMin) / (verticalMax - verticalMin);

        return new Point2D(horizontalOffset + viewportWidth / 2, verticalOffset + viewportHeight / 2);
    }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The hvalue and vvalue are percentage values representing the scroll position. The method calculates the vertical and horizontal offset of the viewport, then adds half the viewport width and height to find the centre point.&lt;/p&gt;
&lt;p&gt;When testing the method I got an error &lt;code&gt;java.lang.IllegalStateException: Toolkit not initialized&lt;/code&gt;, indicating that the JavaFx runtime was not initialized correctly, so I needed to make use of a framework for testing JavaFX applications in JUnit5.&lt;/p&gt;
&lt;p&gt;I added the dependency to by gradle.build....&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;dependencies {
    ....
    testImplementation(&amp;#39;org.testfx:testfx-junit5:4.0.16-alpha&amp;#39;)
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Then for the test, I used the JUnit5 ExtendWidth attribute to initialize the JavaFX runtime. For testing I picked some easy values to work with, a scrollpane viewport size of 100x100 with a canvas size of 200x200. The scene contains a Group as the root so that I can call applyCss and layout methods to calculate the layout before I test the result.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;package com.example.infrastructure.utilities;

import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.ScrollPane;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.framework.junit5.ApplicationExtension;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(ApplicationExtension.class)
class ScrollHelperTest {

    @Test
    void getViewportCentre_No_Scroll_ReturnsExpectedPoint() {
        Group root = new Group();
        Scene scene = new Scene(root, 320, 240);

        ScrollPane scrollPane = new ScrollPane();
        scrollPane.setPrefViewportWidth(100);
        scrollPane.setPrefViewportHeight(100);
        scrollPane.setHvalue(0);
        scrollPane.setVvalue(0);
        root.getChildren().add(scrollPane);

        Canvas content = new Canvas();
        content.setWidth(200);
        content.setHeight(200);

        scrollPane.setContent(content);

        root.applyCss();
        root.layout();

        Point2D point = ScrollHelper.getViewportCentre(scrollPane, content);

        assertThat(point.getX()).isEqualTo(50);
        assertThat(point.getY()).isEqualTo(50);
    }

    @Test
    void getViewportCentre_WhenScrolled_ReturnsExpectedPoint() {
        Group root = new Group();
        Scene scene = new Scene(root, 100, 100);

        ScrollPane scrollPane = new ScrollPane();
        scrollPane.setPrefViewportWidth(100);
        scrollPane.setPrefViewportHeight(100);

        // scroll to a certain position
        scrollPane.setHvalue(0.5);
        scrollPane.setVvalue(0.5);

        root.getChildren().add(scrollPane);

        Canvas content = new Canvas();
        content.setWidth(200);
        content.setHeight(200);

        scrollPane.setContent(content);

        root.applyCss();
        root.layout();

        Point2D point = ScrollHelper.getViewportCentre(scrollPane, content);

        assertThat(point.getX()).isEqualTo(100);
        assertThat(point.getY()).isEqualTo(100);
    }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;To Lean more about JavaFX, read the docs here &lt;a href=&quot;https://openjfx.io/&quot;&gt;https://openjfx.io/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Memoized Function with TTL]]></title><description><![CDATA[Lodash.memoize creates a function that memoizes the result of the function passed as a parameter. This is great if you want cache the result…]]></description><link>https://www.ginocoates.com/2020-09-19-memoized-function-with-ttl/</link><guid isPermaLink="false">https://www.ginocoates.com/2020-09-19-memoized-function-with-ttl/</guid><pubDate>Sat, 19 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.npmjs.com/package/lodash.memoize&quot;&gt;Lodash.memoize&lt;/a&gt; creates a function that memoizes the result of the function passed as a parameter. This is great if you want cache the result of an expensive function.&lt;/p&gt;
&lt;p&gt;However, lodash.memoize doesn&apos;t support a TTL or expiry, but it does let you customise the underlying cache it uses.&lt;/p&gt;
&lt;p&gt;So, if for example you want to cache the responses you are retrieving from an API heres a simple way to add TTL functionality to memoize:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;js&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import memoize from &amp;#39;lodash.memoize&amp;#39;;

const DEFAULT\_CACHE\_EXPIRY = 60000;

// create a custom cache based on Map
export default class CustomCache extends Map {
  constructor() {
   super();
  this.expiry = DEFAULT\_CACHE\_EXPIRY;
  }

  has(key) {
    // when checking the cache also check the expiry
    return super.has(key) &amp;amp;&amp;amp; super.get(key).expiry &amp;gt; Date.now();
  }

  set(key, value) {
    // when setting the value, add an expiry property
    value.expiry = Date.now + this.expiry;
    return super.set(key, value);
  }
}

// provide the custom cache for memoize to use
memoize.Cache = CustomCache;

// example function fetching data from the
const fetchData = async () =&amp;gt; {
  return fetch(`https://someapi-example-api`)
  .then((response) =&amp;gt; response.json())
  .then((response) =&amp;gt; response.data);
}

// create a memoized function
const cachedFetchData = memoize(fetchData);

// set the cache expiry
cachedFetchData.cache.expiry = 30000

...

// later, call the memoized function as follows
const response = await cacheFetchData();&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;A custom cache can be created that extends the Map type.&lt;/p&gt;
&lt;p&gt;An expiry property is added to the cached value in the &lt;code&gt;set&lt;/code&gt; method before saving it in the Map. The &lt;code&gt;has&lt;/code&gt; method then checks the expiry of the item if it exists in the cache.&lt;/p&gt;
&lt;p&gt;We can then set memoize.Cache to our custom cache implementation.&lt;/p&gt;
&lt;p&gt;When we wrap the function we want to cache, memoize exposes the &lt;code&gt;.cache&lt;/code&gt; property that can be used to set the desired TTL for the cached items.&lt;/p&gt;
&lt;p&gt;You can then call the cached function as needed. If the value is in the cache and not expired, it will be returned, otherwise the inner function will be called.&lt;/p&gt;
&lt;p&gt;Caveats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This approach will work if the value returned from your function is an object. You would have to extend the approach to support scalar values.&lt;/li&gt;
&lt;li&gt;By adding the expiry in this way property you are mutating the object, which is a bit yucky and could have side effects. This could be extended to be more functional, but hey, I said &quot;simple approach&quot;.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Amazon EC2 Container Service Deployment using Terraform]]></title><description><![CDATA[Overview We have a number of individual micro-services and want to have a continuous delivery system with an on demand deployment to various…]]></description><link>https://www.ginocoates.com/2017-08-31-amazon-ec2-container-service-deployment-using-terraform/</link><guid isPermaLink="false">https://www.ginocoates.com/2017-08-31-amazon-ec2-container-service-deployment-using-terraform/</guid><pubDate>Thu, 31 Aug 2017 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;We have a number of individual micro-services and want to have a continuous delivery system with an on demand deployment to various internal and external environments.&lt;/p&gt;
&lt;p&gt;After using codeship for a while we decided to use Jenkins CI to implement our pipeline to give us a little more control. Furthermore, to reduce costs we have containerized all of our services using Docker and deployed to Amazon EC2 container service, moving away from an Elastic Beanstalk deployment model with 40 instances, to three ECS clusters with just six instances.&lt;/p&gt;
&lt;p&gt;In this article I’ll go through the entire pipeline and point out some of the key patterns that have enabled us to do this.&lt;/p&gt;
&lt;p&gt;Sample source code for the article can be found here: &lt;a href=&quot;https://github.com/ginocoates/pipeline-samples&quot;&gt;https://github.com/ginocoates/pipeline-samples&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Caveats/Points to Note&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For now we have opted for continuous delivery (on-demand) over continuous deployment. This is because we need to control what features go to each environment so that we don’t impact integrators adversely.&lt;/li&gt;
&lt;li&gt;Database deployments are handled separately. We have a policy of making DB deployments backwards compatible and we rollout DB changes and smoke test them before rolling out corresponding application code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Tools Used&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Jenkins CI - Open source automation server.&lt;/li&gt;
&lt;li&gt;Jenkinsfile - All pipelines have been implemented using the Pipeline plugin with the Jenkins file pulled from source control.&lt;/li&gt;
&lt;li&gt;Terraform - Allows you to define your infrastructure as code using a json like syntax. We used terraform to define our entire ECS cluster, including cluster, cluster instances, application load balancer, target groups, security groups, ecs services and task definitions for each of our services.&lt;/li&gt;
&lt;li&gt;Custom Orchestration Tools - Some custom scripts written in bash to drive the other tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Environments&lt;/h2&gt;
&lt;p&gt;We have a number of environments in use as Doshii for different purposes. The goal of our pipeline is to promote a set of container images through these different environments on demand.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Test - This is an on-demand ECS cluster that is created as needed and destroyed by a Jenkins scheduled job each evening at 8PM to save costs.&lt;/li&gt;
&lt;li&gt;Beta - Functionality that has passed system testing in the test environment will be promoted to the Beta environment on demand. This is also the environment that most of our integration clients use for their integration development.&lt;/li&gt;
&lt;li&gt;Staging - Where we test containers with live credentials before deploying to the live cluster. This environment is hitting the live database.&lt;/li&gt;
&lt;li&gt;Live and Sandbox - Live and Live like environments. Some customers are sandboxed in a live like environment while they are onboarded.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Jenkins Setup&lt;/h2&gt;
&lt;p&gt;Jenkins is running as a docker container on a small instance. This is fronted by a load balancer to simplify SSL termination. The Jenkins servers are tucked away inside a VPC private subnet with individual security groups and network ACL’s in place.&lt;/p&gt;
&lt;p&gt;The figure below shows the basic architecture of the jenkins servers.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 578px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAABr0lEQVQoz42STa+iMBSG+f9/wIW6cGncGDQY4wojBuNXCipCjGgIYGmRSAQGaOlkbhNmMpd7c8+iOWn7nPP2PRXYF5Hnue/7juMghCiljXeEr2DDMEzTXK1WAID1es0Yq6qKrzz5Dt7v9wCA0WjUbrclSWKMUUprkicNMD9GCOm6LorieDyGENb7RVG8Xi/+EKGRLMsySRLLsrbbLcY4juOyLBljURS5rut5nu/7SZI0dw7D8Ha7PR4P3/cxxkmSVFVFKdU07XQ6zWYzVVUXi4Xw2WTbth3H8Tzvfr+naVpXTNNUVVUAwHA4bLVasiwL/wkOwxAAoCjKZDIZDAaWZRFC3u/3r48IgsA0TUmSlsvlH9lcD6WUEMIYi+NYUZTNZtPr9brdLsa4rpvneRRFjuNomoYQwhgLcRwjhKKP4GYEQXA4HGRZfj6fNclHxVVACJMkIYQIhBBd11VVhRBmWcZdvV6vl8vlX7L5h0EIRVHsdDrT6ZQrr6oqyzLucOMs/v4w13V3u918Pu/3+7ztz0MIw9AwDEVRzudzURSfy38HR1Fk2/bxePQ8r4Z/2Pk3H5ZNU9YsCe8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;jenkins-arch&quot;
        title=&quot;jenkins-arch&quot;
        src=&quot;/static/dc711aa35fac0f90d33b40d66e13ab49/508ef/jenkins-arch.png&quot;
        srcset=&quot;/static/dc711aa35fac0f90d33b40d66e13ab49/772e8/jenkins-arch.png 200w,
/static/dc711aa35fac0f90d33b40d66e13ab49/e17e5/jenkins-arch.png 400w,
/static/dc711aa35fac0f90d33b40d66e13ab49/508ef/jenkins-arch.png 578w&quot;
        sizes=&quot;(max-width: 578px) 100vw, 578px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To fire up more beefy slaves on demand we use the Amazon EC2 plugin. The slaves will created as needed and will be terminated after the idle timeout.&lt;/p&gt;
&lt;p&gt;In the AWS EC2 plugin you can configure a lot of details about your slave, including the AMI ID to use, the instance size and security group details.&lt;a href=&quot;./images/ec2-aws-1.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsUlEQVQoz53RUavDIAyG4f7/X7pV1hrbVE1MvjELhyOsG+y5EG9e1DiJqrujY2ZVBbDvu4gA8AsAmtmEf1TVzAAkImYuF2qtMcYQwhCnlIjIgRBCjJGZ9wupG2JmfixLay3nXErBN0O8bRszuzsR1W+OXIZYRM43z/N8v93iusZPaIhba+fmnIqZnbO1C0NcStH+c/fudX5f13dXeCzrEB/HkXN2d3nRP+2dWmXCr8z8CdDASSX+ZuI/AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ec2-aws-1&quot;
        title=&quot;ec2-aws-1&quot;
        src=&quot;/static/866e7b21ca8e757f9e07cc82984d3662/5a190/ec2-aws-1.png&quot;
        srcset=&quot;/static/866e7b21ca8e757f9e07cc82984d3662/772e8/ec2-aws-1.png 200w,
/static/866e7b21ca8e757f9e07cc82984d3662/e17e5/ec2-aws-1.png 400w,
/static/866e7b21ca8e757f9e07cc82984d3662/5a190/ec2-aws-1.png 800w,
/static/866e7b21ca8e757f9e07cc82984d3662/c1b63/ec2-aws-1.png 1200w,
/static/866e7b21ca8e757f9e07cc82984d3662/669eb/ec2-aws-1.png 1244w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;]&lt;/p&gt;
&lt;p&gt;We use a custom built AMI, built using Hashicorp’s Packer, which includes all the tools and utilities we need to perform our build. Pre packing the ami in this way shaves time of your build process, otherwise you’d have to install dependencies every time a slave is created.&lt;/p&gt;
&lt;p&gt;You can also set the number of jobs you would like to allow on the instance. Best approach is to limit this to the number of processors on the machine (e.g. 2 for an M4.Large instance).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 600px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9UlEQVQoz42STU7DMBBGff+7sOIKbGCDWHAFVJpETpw49je/yAkVCFo1T7Ox5KfPM+MQYyRAzQsU5BV2pHIRFg0ppfJNJWI9BouIajh/nlNKIpJzZiZ3t3u4KRGDJaiqb6zrmqaJ+X54C9iU4Bf2cCJSs9uimPv4/DS/v/3IZtb3PTP7AXD64KFrcoxxd9bW81/5es/uxFKJw5QSgHYmKqXmnJcLAF19uqkAaAMb+iHGKCLjOOacQahAraioInIruYDWQm3P87IQMwHM/PvSNiD5XyYCtD8Ruq6bpsnMRMQPw6wgCevG/sL7krUlz68vp8cHdv8CcRnB0ps/4I8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ec2-aws-2&quot;
        title=&quot;ec2-aws-2&quot;
        src=&quot;/static/27c978f9bb4143ad65dbb9039f99deac/0a47e/ec2-aws-2.png&quot;
        srcset=&quot;/static/27c978f9bb4143ad65dbb9039f99deac/772e8/ec2-aws-2.png 200w,
/static/27c978f9bb4143ad65dbb9039f99deac/e17e5/ec2-aws-2.png 400w,
/static/27c978f9bb4143ad65dbb9039f99deac/0a47e/ec2-aws-2.png 600w&quot;
        sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;]&lt;/p&gt;
&lt;p&gt;You can also configure things like the instance profile to use for your instance. Ensure that the instance profile has adequate permissions to execute the build tasks that will be run on the instance, especially if your build interacts with AWS resources in any way.&lt;/p&gt;
&lt;h3&gt;Importing Upstream Builds&lt;/h3&gt;
&lt;p&gt;In Jenkins we are using the Extensible Choice Plugin to create list of upstream builds to deploy. This plugin allows you to specify a groovy script to build the choice list. For example, to import builds from the deploy-test job, we used the following script in an Extensible Choice parameter.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import jenkins.model.Jenkins
import hudson.model.AbstractProject
import hudson.model.Result
import hudson.util.RunList

def builds = \[\];
RunList&amp;lt;?&amp;gt; runs = Jenkins.instance.getItem(&amp;quot;deploy-test&amp;quot;).getBuilds().overThresholdOnly(Result.SUCCESS).limit(100)
runs.each { it -&amp;gt;
 builds.add(&amp;quot;${it.number}-${it.displayName}&amp;quot;)
}

return builds&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;As a convenience we set the the display name of a build to be the git commit message if the build is successful. This is then visible in the Extensible Choice parameter and helps with traceability of changes deployed throughout the Jenkins pipeline. This can be achieved with a simple groovy script to get the commit message from git. e.g.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def setDisplayName(){
 def comment = sh (script: &amp;quot;git log -1 --no-merges --format=%B&amp;quot;, returnStdout: true,)
 comment = comment.trim();
 currentBuild.displayName = comment;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Pipeline Overview&lt;/h2&gt;
&lt;p&gt;In general our deployments go through a number of stages. Individual services are built and tested on check-in. We deploy the latest docker images to Test on demand, perform system testing and then promote those images through the different environments.&lt;a href=&quot;./images/pipeline-overview.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 581px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAACCklEQVQoz4WS3WoTURDHZ7MhqHcFQUTr10P4AF6p+ATaK+964TuUeq8IRYuoGIrRtmibxATTpmnSdr/Pfp5sNsludu0muzE2JaYigiJHNlBTEezAMMy5+M1/5n9A6ygEeRKRPyKiBcqXNLd8CQBg9uHMyRybjdf7tXilXgRplx3lsaEH6ifB5npiS9jXA7WZzD2/SAiBw9wyy9D6bkEJr436Y2OzmT+DB9I5dY8/X3EKp7f3Mgmjqz5QfPSOd9i3eqikWLdyuWQWoEvacUJIjHcYWgkESg/USBCsWekxsGIXoDqQwRwqsNHIQlpLTeBQ/yU6AuEaLDECnXyQ3984qnoTF6E+xPBy9QX1j8KdVoli3TIV1YMfP+GVMEcjX7gtt8V7yBem5bY4vWHlJ5RAuMI5O9czzMpNs29ck3z27Pybx3840aDUenIMLtsFyOFFyBgpwKEO1S4GHGLQAhm8fR/UjlyQPUR4mxsZKHrc/Qhy5+7UqckLk4lHc/NU7UCm/lo9AmaN1xEwZnarNA4NuvnZjve+DUDtoFnkiZhrMFvIk7DgMlOHJwCAEwAQe5YeK4ZF9HTsrLMenQJqvRrkrSVYUZOjd5Ys0IR49PbX5YTRxqAEwlUj1GzJ5U3VVxqMW/r/v7J6FuRrS7CqLYz6XP8J2KQIne4wZvYwoDZ/KzKNrTNE29UI45bIb0EPdOXZfxQEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pipeline-overview&quot;
        title=&quot;pipeline-overview&quot;
        src=&quot;/static/c43d6945a4a58068a4fffb9f4a5c5226/92d15/pipeline-overview.png&quot;
        srcset=&quot;/static/c43d6945a4a58068a4fffb9f4a5c5226/772e8/pipeline-overview.png 200w,
/static/c43d6945a4a58068a4fffb9f4a5c5226/e17e5/pipeline-overview.png 400w,
/static/c43d6945a4a58068a4fffb9f4a5c5226/92d15/pipeline-overview.png 581w&quot;
        sizes=&quot;(max-width: 581px) 100vw, 581px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;]&lt;/p&gt;
&lt;p&gt;Each of our microservices have a separate job in Jenkins, which uses ‘docker build’ to create the container image. If tests pass we tag the image with the build number and push this to EC2 container registry.&lt;/p&gt;
&lt;p&gt;If the build is successful we will also create an artifact groovy file that records the tag of the container image we published to ECR. For example, for service1 the output file for build 90 would contain:&lt;/p&gt;
&lt;p&gt;env.SERVICE1_BUILD=90&lt;/p&gt;
&lt;p&gt;The idea behind this artifact is that it’s imported by the downstream deployment project. The groovy file is loaded and the environment variable thats created is used to set the image tag to deploy for that service (more on that later).&lt;/p&gt;
&lt;p&gt;If the deployment is successful, we re-archive the env.groovy files in the deployment build so that they can be referenced in downstream deployments.&lt;/p&gt;
&lt;p&gt;The image below gives an overview of how the deployment pipeline works:&lt;a href=&quot;./images/pipeline-flow.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACsklEQVQoz42S3UtTARiHf0sHmSQaCF1YIBVBdNNlBd1EQUVBXaVEF0FXXXRd1EViWOFVFEH2BWFljbRSd/Zxds6Z29z52pwum+e46Vlnug/TttpZuvSN+Rf0/gEPz+/hhTgbpuj3CPGi88hqXnCVFz0dpYwPK6a/DiBomgaO5/Hfp6Sk3gkzeoflvhz7vSjQStr7jGgNRGS73HEagx8HYZrm/wO/abPQ52YxMOwHFYNn15eFAwC2eD1My8XOTnAxb51qyHbVkOvHkwFbytLQN/gYTGQYiiHVy3NifSQt1/dzzxFbEoFkzDyjRYwT7VcBKy+jtOAH0V+7w+HYtQmOuhHWQ5CSYQg6C31hxkZEcEVHIKXGEUyMQZ4TN+XiqRgQFzXSo+kN3sWeruR55nfG3UFkIj4RaABg5+O+K/KcdE+Zl7pDSb59OpMA/SQI02yDMi/eFGfHe1RDunHpxaG6SDIGTIVmzulq5oRn9NPBXwsCFU32ac1AVcL7akD/Vz4k6mGSUxKFvgavDQ+4Wx2fR/DgWU+rlAyvhRJBUublEiO4jyoBdTsmxAQMLYuX/Vyt4UkqBvb7vjzC2/5XW2vAkUnHtkRFbZ6qio1KeLCt69b1piFu2f7u3ZvdouVpmC6rzfM02egc6ttTS4RYqiTH0xbvVTM7ngiEmckg7nR1NzAMUzNEv38A6fVpmMvfUMkJI1aWDbscd9s+fJa3vpcD+GlFQHlnYyUnFMoZpg/yzJI8ZVQ4r487X8nx1orB3K5N3nk8sBl6TP5g07NTW6howsr5uUrOTwXdcWHFcCGX9tgp60TRHG22cmNUyQuET8MsdG0Cjte9LeXsGJWz/od/fkRRLWn2U0eaav+IaoG1VQterOWch6sF9sFa3r13NevEn8VR23opho2igmredbFa8N3/B6sCwomm0sMeAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pipeline-flow&quot;
        title=&quot;pipeline-flow&quot;
        src=&quot;/static/99f36cb2452122107b5d40872b09e6b3/5a190/pipeline-flow.png&quot;
        srcset=&quot;/static/99f36cb2452122107b5d40872b09e6b3/772e8/pipeline-flow.png 200w,
/static/99f36cb2452122107b5d40872b09e6b3/e17e5/pipeline-flow.png 400w,
/static/99f36cb2452122107b5d40872b09e6b3/5a190/pipeline-flow.png 800w,
/static/99f36cb2452122107b5d40872b09e6b3/bad1b/pipeline-flow.png 841w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;]&lt;/p&gt;
&lt;p&gt;We use the Copy Artifact plugin to copy the artifacts across to the deployment build. The code to copy the last successful build within a pipeline is quite simple. The snippet below copies the env file for the target service to the import directory of the deployment build.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt; $class: &amp;#39;CopyArtifact&amp;#39;,
 projectName: &amp;#39;service1-build&amp;#39;,
 filter: &amp;#39;output/service-1.env.groovy&amp;#39;,
 fingerprintArtifacts: true,
 target: &amp;#39;import&amp;#39;,
 flatten: true,
 selector: \[$class: &amp;#39;StatusBuildSelector&amp;#39;, stable: false\]\]);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Sample pipeline scripts can be found here:&lt;a href=&quot;https://github.com/ginocoates/pipeline-samples/tree/master/jenkins/pipeline&quot;&gt;https://github.com/ginocoates/pipeline-samples/tree/master/jenkins/pipeline&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Orchestration&lt;/h2&gt;
&lt;p&gt;We’ve created a simple tool for orchestration using terraform. This tool wraps terraform in a docker container and sets up some conventions allow terraform to be used more consistently on local and in Jenkins (ripped from an idea by an awesome DevOps I know, &lt;a href=&quot;https://www.linkedin.com/in/vin%C3%ADcius-carneiro-6719446/&quot;&gt;Vinny Carneiro&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The tool uses the following folder conventions to manage terraform source code:&lt;/p&gt;
&lt;p&gt;Orchestration Root Folder&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;config - &lt;em&gt;variable and terraform state config for deployed stacks&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;region - &lt;em&gt;A regional stack deployment - e.g. ap-southeast-2&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;stack-name
&lt;ul&gt;
&lt;li&gt;tf-config.tf - &lt;em&gt;Configure backend state for terraform&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;variables.tf-vars - &lt;em&gt;A set of variables for this stack instance&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;src
&lt;ul&gt;
&lt;li&gt;stack-name - &lt;em&gt;A set of terraform files for a stack&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;modules - &lt;em&gt;Reusable terraform modules&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example our source folders for ecs clusters are structured as follows:&lt;/p&gt;
&lt;p&gt;Orchestration Root Folder&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;config
&lt;ul&gt;
&lt;li&gt;ap-southeast-2
&lt;ul&gt;
&lt;li&gt;appcluster-test
&lt;ul&gt;
&lt;li&gt;tf-config.tf&lt;/li&gt;
&lt;li&gt;variables.tf-vars&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;appcluster-beta
&lt;ul&gt;
&lt;li&gt;tf-config.tf&lt;/li&gt;
&lt;li&gt;variables.tf-vars&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;src
&lt;ul&gt;
&lt;li&gt;appcluster-test
&lt;ul&gt;
&lt;li&gt;main.tf&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;appcluster-beta
&lt;ul&gt;
&lt;li&gt;main.tf&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;modules
&lt;ul&gt;
&lt;li&gt;app-cluster
&lt;ul&gt;
&lt;li&gt;main.tf&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These conventions allows us to independently control features of our cluster for each environment, via the variables.tfvars file in the config folder.&lt;/p&gt;
&lt;p&gt;To run the orchestration tool we pass in the source folder name and the region as follows.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;./orchestrate [tf cmd] [stack-name] [region]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;e.g.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;./orchestrate apply appcluster-test ap-southeast-2&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The tool will use a docker volume to set the source code context to the appropriate stack folder and load the configuration from the corresponding config folder.&lt;/p&gt;
&lt;p&gt;Note: To use this tool in Jenkins the slave instance profile has been given adequate permissions to manage our ECS resources using terraform.&lt;/p&gt;
&lt;p&gt;Sample source code for our ECS cluster can be found here: &lt;a href=&quot;https://github.com/ginocoates/pipeline-samples/tree/master/terraform&quot;&gt;https://github.com/ginocoates/pipeline-samples/tree/master/terraform&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Deploying to ECS using Terraform&lt;/h2&gt;
&lt;p&gt;To deploy to ECS using terraform we use a aws_ecs_taskdefinition resource which is rendered using the service.build variable, as follows:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;js&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;data &amp;quot;template\file&amp;quot; &amp;quot;service1-task-definition&amp;quot; {
 template = &amp;quot;${file(&amp;quot;${path.module}/task-definition.tpl&amp;quot;)}&amp;quot;
 vars {
 BUILD = &amp;quot;${var.service1-build}&amp;quot;
 container-image=&amp;quot;service1&amp;quot;
 container=&amp;quot;service1&amp;quot;
 }
}

resource &amp;quot;aws\ecs\task\definition&amp;quot; &amp;quot;service1&amp;quot; {
 family                = &amp;quot;ecs-service-service1-${var.environment-name}&amp;quot;
 container_definitions = &amp;quot;${data.template\file.service1-task-definition.rendered}&amp;quot;
 task_role_arn = &amp;quot;${var.task-role-arn}&amp;quot;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;To deploy a specific build of a service we can override these variables at the command line when calling the  orchestrate tool.&lt;/p&gt;
&lt;p&gt;In Jenkins, we first load the upstream groovy files to create the environment variable, then pass the environment variables to the orchestrate tool, as follows:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// load upstream build artifacts to set environment variables appropriately
load &amp;quot;import/service1.env.groovy&amp;quot;
load &amp;quot;import/service2.env.groovy&amp;quot;

// initialize the state
sh &amp;#39;./orchestrate init appcluster-test ap-southeast-2&amp;#39;

// run the deployment, specifying the tags to use in the task definition.
sh &amp;quot;./orchestrate apply appcluster-test ap-southeast-2 \\
-var service1-build=${env.SERVICE1\_BUILD} \\
-var service2-build=${env.SERVICE2\_BUILD}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Under the hood, this will generate a new task definition in ECS and issue an Update command to the service. ECS will then schedule any new tasks as required.&lt;/p&gt;
&lt;h2&gt;Staging and Blue Green deployments&lt;/h2&gt;
&lt;p&gt;For blue green deployments we considered creating a whole new staging cluster on demand, smoke testing this, then perform a DNS switch when we want to roll this new cluster out to live. However, we didn’t like this approach because it means that there may be some lost requests as the DNS switch occurs, and some possible downtime for our clients.&lt;/p&gt;
&lt;p&gt;Instead we decided to break live deployments into two steps. The first step is to stage our containers against the live environment and ensure they are operational. This creates a staging ECS cluster on demand, configured against a different DNS where we can smoke test our service containers in a live environment before releasing them to the wild.&lt;/p&gt;
&lt;p&gt;The second step is the live deployment. Since our terraform based deployment updates the service with a new task definition, we just tell ECS to deploy the images we have previously staged. ECS takes care of scheduling the new tasks,routing new requests to these tasks, while bleeding connections from the old tasks before killing them. This results in zero downtime for our clients.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A Multi-Threaded Background Processing Pipeline for .NET]]></title><description><![CDATA[In this article I'll be illustrating an architecture for a background processing pipeline in .NET. I use a similar architecture as part of…]]></description><link>https://www.ginocoates.com/2017-04-23-a-multi-threaded-background-processing-pipeline-for-net/</link><guid isPermaLink="false">https://www.ginocoates.com/2017-04-23-a-multi-threaded-background-processing-pipeline-for-net/</guid><pubDate>Sun, 23 Apr 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 80px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAACXBIWXMAAAsTAAALEwEAmpwYAAACQklEQVQ4y7VVPYsUQRCd3Vu0H2ikYKK5sYmBgqG5iYFgYuAvsB4aiZHgDxAxUFToJyJiYq6YGJkZi96peBqooOztl3RPzcftzu6dBw48ej6qXtV0V70qin+4AlUDbVjswdQHtbbgBFOD7BArxx5YOoEawLQWGIuQv8ciWGm3PVJyypFi5dRviEtkAlMmKMk0AHUI1AlQ51K6/YVfqDKzyln7QB0BdRLUeVBXQN0O1AtYfAfqByyOg8VZmWqZ7n5QR0GdAnUB1DVQj0BdBfUGFoegJqBm22CO8nmSNvg+qJeg3oP6nT6kSC2n56A2/X7qpFuOMRgn/n4Ki9OiI0oiHIP64+8iqI9VBrVzE2DazjgRjlASTEIdKa9jN3oMaqNFMFuFYsXHPRKaZzSPNqG1CG010ilPugCLW06icg/zQY2X2VdIJTPrBKtVz8D4rX7eAWkP73bCdAfUw2C6DOoWqHtLbVtIdfi5C6DWQf0EM/HbQH0H9WmZfYVip1MD9RTU113Y1WUz7YRp1JxyXPcGmCy1d/yHOvR2ChZbbRT33ilZCKw2HnnTp3Xo5Kmw10NjM/PgyzKMm2BdxHMikdcn6cRdgYYebFwLhZUq42qTf/kgTMdBnQVzzd10hXkN6pdr4pd2wLCtAer9zpK2qNI+H1zqD4A6DOoMqEugbsD0ANQrUB8q/Zzbw0xUDaCBoxea4VTPkOreFT6AOgbqdGC8mBXedL2cdO0B1Dlf0rDywcXuGRR8/uzq6gxi1ZSMzWilBn8BkgtrSSY0o5AAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;home-windows&quot;
        title=&quot;home-windows&quot;
        src=&quot;/static/83bb0007dbabb6c691cda92747c62c7b/81690/home-windows.png&quot;
        srcset=&quot;/static/83bb0007dbabb6c691cda92747c62c7b/81690/home-windows.png 80w&quot;
        sizes=&quot;(max-width: 80px) 100vw, 80px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this article I&apos;ll be illustrating an architecture for a background processing pipeline in .NET.&lt;/p&gt;
&lt;p&gt;I use a similar architecture as part of my PhD project to process large amounts of data coming from Kinect using Open CV on a background thread. In my application each frame contains approx 9MB of data and Kinect runs at a rate of 30 fps. That&apos;s about 270 MB per second flowing through the pipeline!. This pipeline pattern allows that volume of data to be processed without impacting the UI thread or the rate that Kinect frames are received.&lt;/p&gt;
&lt;p&gt;The pipeline code and a sample project can be found on GitHub at &lt;a href=&quot;https://github.com/ginocoates/BackgroundPipeline&quot;&gt;https://github.com/ginocoates/BackgroundPipeline&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Pipeline Classes&lt;/h2&gt;
&lt;p&gt;The diagram below shows the main classes in the pipeline. .NET Generics are used to allow the pipeline to process any kind of frame object. The pipeline uses &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/dd267312(v=vs.110).aspx&quot;&gt;BlockingCollection&lt;T&gt;&lt;/a&gt; which wraps a &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/dd267265(v=vs.110).aspx&quot;&gt;ConcurrentQueue&lt;T&gt;&lt;/a&gt; as the background queue. The BlockingCollection blocks the executing thread and waits for items to be added to the collection before allowing the thread to continue. The ConcurrentQueue allows thread safe access to the background queue from both the UI and background threads.&lt;/p&gt;
&lt;p&gt;The pipeline holds a collection of modules implementing the IPipelineModule interface. At run-time, frames added to the background queue are passed to the Process method of each of the registered modules.&lt;/p&gt;
&lt;p&gt;The pipeline also uses a PipelineTimer class to calculate some run-time metrics such as frames processed. Event notifications are sent to consumers periodically. E.g. this can be used to display the processing frame rate on screen.&lt;/p&gt;
&lt;p&gt;Finally the pipeline raises a number of other useful run-time events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FrameStart - The pipeline is just about to process a frame.&lt;/li&gt;
&lt;li&gt;FrameComplete - The pipeline has completed processing a frame.&lt;/li&gt;
&lt;li&gt;QueueComplete - The pipeline queue has been fully processed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABPElEQVQoz32R3W6kMAyFef/Hq7TdXbUzO5RhAgmEACF27CReMelFpf355BtLts85ckPEKWURiZH6h5pnG2OUPyjlrMq1HS83FQCaH+/97oOIDNq+vF4HbZ3bcs7ybwLSEZCZG+99ed7klHa/E5M8FfyB63bUKxjp2qpl3Us528dgJ7ullJq3XwojMxMAvrzeRuOImYhunW57ux9ARP6ASztY5+E5eu3Gj34OAZteTdvumSMgdv00aouAJad12828HAEBICA+BrM4jzES8+xWuziM1Ax6CiHUMCkxYijVKsbJGEQUkZyyMbNbT6tnIn+MWocQmkurAVmkAEalp3Xb60J7N99+fmxbqLHzk0+NnJnozNzdO2sXEdGT+/52N/Pm3CpSpsXf1exDrGp/pQGAKvXlpaV6U2o8Avxn+TdAdQfPKiMyiAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Classes&quot;
        title=&quot;Classes&quot;
        src=&quot;/static/0224e975e0615f5099bafc91bf3c80c6/5a190/Classes.png&quot;
        srcset=&quot;/static/0224e975e0615f5099bafc91bf3c80c6/772e8/Classes.png 200w,
/static/0224e975e0615f5099bafc91bf3c80c6/e17e5/Classes.png 400w,
/static/0224e975e0615f5099bafc91bf3c80c6/5a190/Classes.png 800w,
/static/0224e975e0615f5099bafc91bf3c80c6/c1b63/Classes.png 1200w,
/static/0224e975e0615f5099bafc91bf3c80c6/29007/Classes.png 1600w,
/static/0224e975e0615f5099bafc91bf3c80c6/b41cb/Classes.png 1661w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;How it Works&lt;/h2&gt;
&lt;p&gt;The sequence diagram below shows the basic operation of the pipeline.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 671px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAADc0lEQVQozz2SbUhbVxjH/zdRmxh1Gm1h1hdKtzhbxtYyGGzIiOJGaY111bqJrHWQyzpoTWBOrAjJF9vgB1mDsSA3JjczMTU3iZjEiYm55uXmxaR2UamD7pPSDjrG2IfR7YN3nMztgT/nOTw8v/95zjnY3NwUk8mkKAjC38Fg8CrP8weJREKMxWJiIBB4K5vNIpfLYXt7GzzPN0UiETEUConhcFhcX1+3ZDKZJ/F4XCQ9giCIYFn2isfjuetwOK5zHHeJYZgBhmG0Lpdr0Gq1ntrZ2dHl83nT7u7uXY/H02iz2bosFks/wzB98/Pz7ywtLaltNtvnLMteW1xc7IHf70ehsPNJ85mzJUajoRIAFY8nOke+Ha2amJhojUQi0eXl5Z/C4XDO7XY3sCyLvb09zM3NwW63Y2FhAZlMBsFgEE6nE3j+4hds5fLXnj7dr4jFYiCRzmQ+k8nk1YFAoAuAYvbhw4uiKOLo6Airq6vSsbGxcrvdLmVZVnJ4eEgZDAa5yWQqSyaTEuTzj5HLP+5OpdM1PM+3FAqFqqQgfHr2DZV8ZWWlXq1uL9vayl19+fLXilev/ioaammtbMbyFbX9ZJTq67sJmqZLaZqW6nQ6ICkIyGSz3VNTU/Ucx7WZTKbT6+FwPwAZGR9A6dra2tjMzMzrAKQASrw+3yWnKyFffHQPwAe4devrsjt3hqUajQaIRDYQTySu63T6ar/fr7LZ5l/biEZ7CbCxsZFAa91u93Bvb28zADkxiGxs9N836d9Ut5+uJvu6uhrFca0EqVQaQirdns1uyQ8ODiCKIvVjodBHRlMoyk8AqHK6XN16vb7F41mavH3beJLf5AZTqeGfnz0b+fP5i4luQJTY7d/VFIE3h4YwNPSl7IsbNyiflysCU6l0GwFWVihKAJywzM4OdnR0vD0+Pt6mVNYrA0G/gefvj+7v6/uDoa4zHPfDRcbKfFi8YJqmicrIOjAwAJWqBQ0NDaWkJqEoApRzXu+gRqNpLp4AQDTKGx888LxLXp7I5/OqHY7vPyoCDQbjvzIaYTab8cfvv8HKMFQRKJEQcKXZbH6/s/PjOplMVgWgYnp6+r0LF1QnW1tPkTHLJyfvtX4zMnK+aKjVav9XT09P0YS41tbWoqmpCcchUSprJCQ5d+48MZMc/wDq8uUr1H89gWAI/wCIvY4bYxLbewAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pipeline-basic&quot;
        title=&quot;pipeline-basic&quot;
        src=&quot;/static/66b03eaebe9091b16112d8585a252878/d0e73/pipeline-basic.png&quot;
        srcset=&quot;/static/66b03eaebe9091b16112d8585a252878/772e8/pipeline-basic.png 200w,
/static/66b03eaebe9091b16112d8585a252878/e17e5/pipeline-basic.png 400w,
/static/66b03eaebe9091b16112d8585a252878/d0e73/pipeline-basic.png 671w&quot;
        sizes=&quot;(max-width: 671px) 100vw, 671px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The BackgroundPipeline.Start method is called, which creates the BlockingCollection, starts the timer and creates the background thread.&lt;/li&gt;
&lt;li&gt;Once created the thread loops, trying to take items from the queue until BlockingCollection.IsComplete is true. Inside the loop the BlockingCollection will block the thread until there is something to process. Once TryTake returns a frame the frame is passed to the Process method of each module registered with the pipeline.&lt;/li&gt;
&lt;li&gt;The consuming application then calls BackgroundPipeline.Enqueue to add work to the pipeline. Any work added will unblock the background thread.&lt;/li&gt;
&lt;li&gt;To end process the consuming application calls BackgroundPipeline.Stop which stops the timer and calls BlockingCollection.CompleteAdding. After this no more work can be added to the pipeline. The background thread will process any remaining frames and exit the loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that the background thread is created using TaskFactory.StartNew passing TaskCreationOptions.LongRunning to ensure that the scheduler creates a separate thread. The code to create the thread and process the items in the queue is shown below. Inside the loop we use the BlockingCollection.TryTake method with a timeout of infinite. This method blocks if the queue is currently empty:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
processingTask = Task.Factory.StartNew(() =&amp;gt;
{
    while (FrameQueue != null &amp;amp;&amp;amp; !FrameQueue.IsCompleted &amp;amp;&amp;amp; !cancellationToken.IsCancellationRequested)
    {
        try
        {
            // blocks until frame is available
            T frame;

            if (FrameQueue.TryTake(out frame, -1, cancellationToken.Token))
            {
                ProcessFrame(frame);
            }
        }
        catch (OperationCanceledException)
        {
            Debug.WriteLine(&amp;quot;Queue processing cancelled.&amp;quot;);
        }

    }

    OnQueueComplete();

}, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Sample Application&lt;/h2&gt;
&lt;p&gt;In the git repo I&apos;ve provided a sample WPF application that gets data from a Kinect sensor and processes it using the background queue. The goal of the sample is to ensure that the background processing does not impact the performance of the UI or throughput of Kinect frames. The sample shows the FPS of the UI thread, the background pipeline, along with the number of frames still to be processed on screen.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABJUlEQVQoz9WSwUrDQBCG87zWl/DgqSAVPVjw5hN4FE+NVCpK01OploqHpmk2u2lDNpvsbmamJFUMWCVXf77bzjfLDOPEi9uCjfJgkAdusX7IA9ewYfx+1+8e9budq27n+ux4+3Fv2HBfkAWu8gdy8RhPbhy9eSUiQqBGAFAqK5WpsYDNR8K6WEWeo+MpEEVstRE8k2lpNWFZQfgNltYWWitjcgSLoIkoC54dvZkBkeCREEIpVZYlHcpy6Xvj8dt8rrWuO5Ji40+ZhevtNrHW/iZnSkWcJ0kCAPsZv2SkWHApJQBIKY0x9GewKWM1M8tVTkSc8zRN28pFPPu57X8gi2l9Frby24FgKzl8OXxhrX6OPGf1dBpOLkKvF3rnremxyaU/OtkBJ4virNyEd/cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;benchmark-sample&quot;
        title=&quot;benchmark-sample&quot;
        src=&quot;/static/a0289bad4f9672809a880431c06cfb8e/5a46d/benchmark-sample-300x200.png&quot;
        srcset=&quot;/static/a0289bad4f9672809a880431c06cfb8e/772e8/benchmark-sample-300x200.png 200w,
/static/a0289bad4f9672809a880431c06cfb8e/5a46d/benchmark-sample-300x200.png 300w&quot;
        sizes=&quot;(max-width: 300px) 100vw, 300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The sample provides a dummy module that simulates a background load by looping for a pre-defined period of time. i.e. the Process method simply waits for 40 ms before continuing:&lt;/p&gt;
&lt;deckgo-highlight-code   &gt;
          &lt;code slot=&quot;code&quot;&gt;
var end = DateTime.Now + TimeSpan.FromMilliseconds(40);
while (DateTime.Now &amp;lt; end) ;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This module also computes the interval between KinectFrames as they arrive.&lt;/p&gt;
&lt;p&gt;When Stop is clicked the sample will create a report.csv file containing the UI and background calculated FPS.  The diagram below shows the main classes in use in the sample.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAABiUlEQVQoz42SS2/bMBCE9f9/Uw8FigBNC8epnbZOIVuSrQdFia8llxRJWSzUXhLUCTq3PXzYnZnNEA0dqJISAGKM6T0t1+v8cs5+Hc+7/aGoLlJpRBvCynMBp7I91y0Y65xLKV2a/uHxx3NeKu2s827yiJh9fjgejqSoh0EY0AYRU0o/8/bDp/12nzeEa4PzHDf76suuKhpFhecwWRc5F1nV0I5QMBhC9N7P83qYBFe3nA5yZBLRLsvSDTIvmrpjA9dcWXTzyESGxvR9b9CsnpblLbsGNSFdURakJ1rrlBIAZB/vHu83349la6cZwEzTdBOenFNKdl2LaP5uMlpn99vT7nA5t1yC09paZ2/CXKiirIrqTCmrm07jJCVkg9CMramEEEMI1+v1JqyU6chA6Mi4clO0U1gDWyOaY3xd4L8NBx+FEEV56mlvnQUlGWeZRWScj2zkjBmDb9Hee0SjNViL/o+stdnd16fNt+fy0hPKSU+N0e/H/urDtk9lXpKmFwOHceQA8P/wb/PprtIQz+YgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sample&quot;
        title=&quot;Sample&quot;
        src=&quot;/static/a0225cb41dca91f650d3cc6fab4155d1/5a190/Sample-1.png&quot;
        srcset=&quot;/static/a0225cb41dca91f650d3cc6fab4155d1/772e8/Sample-1.png 200w,
/static/a0225cb41dca91f650d3cc6fab4155d1/e17e5/Sample-1.png 400w,
/static/a0225cb41dca91f650d3cc6fab4155d1/5a190/Sample-1.png 800w,
/static/a0225cb41dca91f650d3cc6fab4155d1/9cab2/Sample-1.png 864w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Note that the KinectFrame class stores the Kinect data using unmanaged code, and is therefore IDisposable. By doing this we can stave off expensive Gen2 garbage collection events when dealing with large amounts of data. Note that the sample targets the x64 platform so that we can allocate &lt;a href=&quot;http://stackoverflow.com/questions/200348/is-there-a-memory-limit-for-a-single-net-process&quot;&gt;more than 2GB of memory&lt;/a&gt; at run-time.&lt;/p&gt;
&lt;h2&gt;Pipeline Performance&lt;/h2&gt;
&lt;p&gt;The chart below illustrates the performance of the sample app over 60 seconds. Metrics for the fps for Kinect (RenderFPS) and pipeline are shown, along with the number of frames yet to be processed in the queue. The Interval metric is the amount of time between Kinect frames being processed by the dummy module. This metric allows us to monitor if there are large gaps in the data which might indicate a performance problem when getting frames from Kinect. Total process memory used and number of Gen1 and Gen2 collections are also shown.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABMUlEQVQoz0VRi27jMAzL/3/l0LsVbdLY9UNPS/KQZOgIgoBgECLlRZgECvcqULkXgfrh74h9YBmC7uHucWLOycxLybk2RlKkAfirB0mBjMUR6rb+673NOS/bhcPc0jagGIMzGPVTmzFMZYMM6bY/by0nH+NaOjxUdc4pIgvm1bl7zDhCWRzBptvodX2ne4dudmzzExEhUJ6PBwAQ4ULvVwjGGekKJfTO+//eyiehuQ2Teb4CtpQSMwPgAnkzAo+rBuyv75Q35KEWJEM0UGRrO4lcp6q1pJSIqLW2cNmPembba32sW0NVm8NCLYYFCGXIw0Z84ENU3P3s/N4e96/b877XIqbmqsZqPExQGmmbcXX6+6S/a6f9te1rwyZKgBWwN2iHHmyqg4lUlU5l5mv7Zf4BhKMK6DK75u0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pipeline-performace-basic&quot;
        title=&quot;pipeline-performace-basic&quot;
        src=&quot;/static/5454e00d8f66d764f66686922b97ebaa/5a190/pipeline-performace-basic.png&quot;
        srcset=&quot;/static/5454e00d8f66d764f66686922b97ebaa/772e8/pipeline-performace-basic.png 200w,
/static/5454e00d8f66d764f66686922b97ebaa/e17e5/pipeline-performace-basic.png 400w,
/static/5454e00d8f66d764f66686922b97ebaa/5a190/pipeline-performace-basic.png 800w,
/static/5454e00d8f66d764f66686922b97ebaa/ef6b9/pipeline-performace-basic.png 832w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As you can see the UI thread runs at a constant 30 fps, while the background pipeline is processing through the frames at 25 fps without impacting performanceof the UI. The interval between frames processed is also constant throughout the capture. This is important as it means we are not losing any Kinect frames due to performance bottlenecks in the background. Gen2 collections are also kept to a minimum and don&apos;t impact performance of the application.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The pipeline presented allows background processing of intensive operations while not impacting UI thread performance. The pipeline uses .NET concurrent collections to achieve thread safe access to the queue and enable processing in the background. Intervals between Kinect frames in the queue were constant, indicating that the pipeline processing does not impact the performance of the thread interacting with Kinect.&lt;/p&gt;
&lt;p&gt;For more information on the performance of .NET Concurrent Collections, see this MSDN article:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/pfxteam/2010/04/26/performance-of-concurrent-collections-in-net-4/&quot;&gt;https://blogs.msdn.microsoft.com/pfxteam/2010/04/26/performance-of-concurrent-collections-in-net-4/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The code for the pipeline and sample can be found on Github here: &lt;a href=&quot;https://github.com/ginocoates/BackgroundPipeline&quot;&gt;https://github.com/ginocoates/BackgroundPipeline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to know more about programming with the Kinect SDK, &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/dn799271.aspx&quot;&gt;Read the docs&lt;/a&gt; or &lt;a href=&quot;https://www.amazon.com/gp/product/1484223152/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;#x26;tag=ginocoatescom-20&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;linkCode=as2&amp;#x26;creativeASIN=1484223152&amp;#x26;linkId=cbeedb00236d2ab04025be98f7cbefb0&quot;&gt;pick up this book&lt;/a&gt; on Amazon.com.&lt;/p&gt;
&lt;p&gt;Kinect for Xbox One Sensor&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B00INAX3Q2/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B00INAX3Q2&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=4ae42ae2c935e22bb096e0a45ce49416&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B00INAX3Q2&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B00INAX3Q2&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Kinect for Xbox One Adapter&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B01GVE4YB4/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B01GVE4YB4&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=82081b6a15ff60f82b7f9d6e65915abe&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B01GVE4YB4&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B01GVE4YB4&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Kinect for Xbox One Colour Depth Map using Helix Toolkit and WPF]]></title><description><![CDATA[The Helix Toolkit is one of the easiest ways I've found to quickly add 3D features to a WPF Application. KinectV2 (Kinect for XBox One) is a…]]></description><link>https://www.ginocoates.com/2017-01-21-kinect-xbox-one-colour-depth-map-using-helix-toolkit-and-wpf/</link><guid isPermaLink="false">https://www.ginocoates.com/2017-01-21-kinect-xbox-one-colour-depth-map-using-helix-toolkit-and-wpf/</guid><pubDate>Sat, 21 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBBAX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAF6N6BJYD//xAAZEAACAwEAAAAAAAAAAAAAAAACAwABBBD/2gAIAQEAAQUCc3SJ0/ZEEZB3/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAQMFAAAAAAAAAAAAAAAAAQACERIgM3Gi/9oACAEBAAY/AnUMkaWPlS8QbP/EABoQAQADAQEBAAAAAAAAAAAAAAEAESFRMYH/2gAIAQEAAT8hGgFzbkU1U+p6FPKiSjkJ/9oADAMBAAIAAwAAABCwz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB0QAAICAgMBAAAAAAAAAAAAAAABESExQWFxkbH/2gAIAQEAAT8QhfXeY6b2JfYNS+mZoqJ0rTEN2kzieGB//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Kinect2&quot;
        title=&quot;Kinect2&quot;
        src=&quot;/static/81ef4450fd4eb14a24fe3942c5649021/4b190/Kinect2.jpg&quot;
        srcset=&quot;/static/81ef4450fd4eb14a24fe3942c5649021/e07e9/Kinect2.jpg 200w,
/static/81ef4450fd4eb14a24fe3942c5649021/066f9/Kinect2.jpg 400w,
/static/81ef4450fd4eb14a24fe3942c5649021/4b190/Kinect2.jpg 800w,
/static/81ef4450fd4eb14a24fe3942c5649021/6a068/Kinect2.jpg 960w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.nuget.org/packages/HelixToolkit.Wpf/&quot;&gt;Helix Toolkit&lt;/a&gt; is one of the easiest ways I&apos;ve found to quickly add 3D features to a WPF Application.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.xbox.com/en-US/xbox-one/accessories/kinect&quot;&gt;KinectV2&lt;/a&gt; (Kinect for XBox One) is a motion capture device designed for gaming. It comes with a free SDK making it super easy to build KinectV2 apps for windows. In just a few lines of code you get access to the various streams captured by the KinectV2 sensor.&lt;/p&gt;
&lt;p&gt;In this Article I&apos;ll be demonstrating a simple way of showing a 3D color depth map inside a WPF application using the HelixToolkit along with the Microsoft SDK for KinectV2.&lt;/p&gt;
&lt;p&gt;The depth map will be rendered using the X, Y, Z coordinates from the KinectV2 depth camera stream and the coloured using a texture generated from the RGB camera stream.&lt;/p&gt;
&lt;p&gt;The image below shows the output from the program capturing a fairy that lives with me and shows up from time to time.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAABdElEQVQoz3XSS0sCURTAcT+HfYGQXCa1CYIeEC40IpIQpBfWrk0mYRsTLNNMvahTzpBFg+IojotZOPfhde6MNDqfKUqUEeu3PPBfnHOvQ9f1wZQx+DJNy7JGo9FwOGy1Wul0mud5juMaUsM0TcMwBjYONAEhJKQnSQ2OKwuCIIqflFK5I0uS1Ol0kslkvV6jlEIIp4mDTGCMdV0vlcr+Xf/m9lYul7MsC0KoKIqqqolEQhRFxhjGeJrMxIyxt2o1dHR8FYm02+1SsVjI5wW+IooiAEBRFEopsZmJtX6/VqvfP6RisdvnbHZ9bfX0JFQAIBA4uIvHDcNACM3EeAIhpGla9f3jMBgMn19UXl98Gys3kcslt9u54OR5XtM0hBC2mYkppc1m8ywc9nq90ei1b2czsL/nci16PMuyLFNK/43HCCHdbhcAkHnKlDkOgMLjj5SqqniOY35ECBm/J2PMmBgfxa7XQ3/EGGM4x77d76dAsqx+A1sel4aE+qbnAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;fairy&quot;
        title=&quot;fairy&quot;
        src=&quot;/static/91fc41245dfb470fd686a9bce48fcf7e/5a190/fairy-1024x535.png&quot;
        srcset=&quot;/static/91fc41245dfb470fd686a9bce48fcf7e/772e8/fairy-1024x535.png 200w,
/static/91fc41245dfb470fd686a9bce48fcf7e/e17e5/fairy-1024x535.png 400w,
/static/91fc41245dfb470fd686a9bce48fcf7e/5a190/fairy-1024x535.png 800w,
/static/91fc41245dfb470fd686a9bce48fcf7e/2bef9/fairy-1024x535.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The code for this article is available for download on github &lt;a href=&quot;https://github.com/ginocoates/kinect2-helix&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Dependencies&lt;/h1&gt;
&lt;p&gt;We&apos;ll be using three main dependencies from Nuget for the sample:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Galasoft.MVVMLight - Used to create ViewModels for our views. All of our interactions with Kinect 2 will be within this class.&lt;/li&gt;
&lt;li&gt;HelixToolkit.WPF - Provides 3D model and rendering features. In this sample we&apos;ll be using the HelixViewport3D and PointsVisual3D classes.&lt;/li&gt;
&lt;li&gt;Microsoft.Kinect - The Kinect for Windows 2 SDK&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to run the sample on windows you will need to purchase a Kinect for XBox One sensor and adapter to allow it to be used on Windows. Click the images below to pick them up from Amazon.com&lt;/p&gt;
&lt;p&gt;Kinect for Xbox One Sensor&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B00INAX3Q2/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B00INAX3Q2&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=4ae42ae2c935e22bb096e0a45ce49416&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B00INAX3Q2&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B00INAX3Q2&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Kinect for Xbox One Adapter&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B01GVE4YB4/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B01GVE4YB4&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=82081b6a15ff60f82b7f9d6e65915abe&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B01GVE4YB4&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B01GVE4YB4&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1&gt;Application Architecture&lt;/h1&gt;
&lt;p&gt;The following diagram shows the structure of the application.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABPElEQVQoz32R3W6kMAyFef/Hq7TdXbUzO5RhAgmEACF27CReMelFpf355BtLts85ckPEKWURiZH6h5pnG2OUPyjlrMq1HS83FQCaH+/97oOIDNq+vF4HbZ3bcs7ybwLSEZCZG+99ed7klHa/E5M8FfyB63bUKxjp2qpl3Us528dgJ7ullJq3XwojMxMAvrzeRuOImYhunW57ux9ARP6ASztY5+E5eu3Gj34OAZteTdvumSMgdv00aouAJad12828HAEBICA+BrM4jzES8+xWuziM1Ax6CiHUMCkxYijVKsbJGEQUkZyyMbNbT6tnIn+MWocQmkurAVmkAEalp3Xb60J7N99+fmxbqLHzk0+NnJnozNzdO2sXEdGT+/52N/Pm3CpSpsXf1exDrGp/pQGAKvXlpaV6U2o8Avxn+TdAdQfPKiMyiAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Class structure for the sample&quot;
        title=&quot;Class structure for the sample&quot;
        src=&quot;/static/0224e975e0615f5099bafc91bf3c80c6/5a190/Classes.png&quot;
        srcset=&quot;/static/0224e975e0615f5099bafc91bf3c80c6/772e8/Classes.png 200w,
/static/0224e975e0615f5099bafc91bf3c80c6/e17e5/Classes.png 400w,
/static/0224e975e0615f5099bafc91bf3c80c6/5a190/Classes.png 800w,
/static/0224e975e0615f5099bafc91bf3c80c6/c1b63/Classes.png 1200w,
/static/0224e975e0615f5099bafc91bf3c80c6/29007/Classes.png 1600w,
/static/0224e975e0615f5099bafc91bf3c80c6/b41cb/Classes.png 1661w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt; Class structure for the sample&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The MainWindow hosts a HelixViewport3D which will display our PointCloudModel.&lt;/li&gt;
&lt;li&gt;The MainWindow&apos;s ViewModel has a reference to the Kinect 2 Sensor and handles events to update our PointCloud model.&lt;/li&gt;
&lt;li&gt;The PointCloud model is a subclass of the Helix PointsVisual3D class.&lt;/li&gt;
&lt;li&gt;To avoid expensive operations on the UI thread, the PointCloud is updated on a separate thread via a PointCloudWorker, a custom subclass of the BackgroundWorker class.&lt;/li&gt;
&lt;li&gt;Depth and Colour frame data is communicated between the PointCloudWorker and MainViewModel via the KinectData class.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;The Viewport&lt;/h1&gt;
&lt;p&gt;The MainWindows hosts a DockPanel. The HelixViewport3D is the lastchild control so will fill the window. I&apos;ve added a View menu bound to some of the viewport controls for convenience. The snippet below shows how easy it is to add a 3D viewport to the window.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;xml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
&amp;lt;helix:HelixViewport3D HorizontalAlignment=&amp;quot;Stretch&amp;quot;
   VerticalAlignment=&amp;quot;Stretch&amp;quot;
        Background=&amp;quot;Silver&amp;quot;
        x:Name=&amp;quot;mainViewport&amp;quot;
        CameraRotationMode=&amp;quot;Turnball&amp;quot;&amp;gt;

        &amp;lt;helix:HelixViewport3D.DefaultCamera&amp;gt;
            &amp;lt;PerspectiveCamera
                Position=&amp;quot;0, 0, -2&amp;quot;
                LookDirection=&amp;quot;0, 0, 1&amp;quot;
                UpDirection=&amp;quot;0, 1, 0&amp;quot; /&amp;gt;
            &amp;lt;/helix:HelixViewport3D.DefaultCamera&amp;gt;

        &amp;lt;helix:DefaultLights/&amp;gt;

&amp;lt;/helix:HelixViewport3D&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We are using a Perspective Camera positioned 2 meters behind Kinect, 0,0,0 is the Kinect sensor origin.&lt;/p&gt;
&lt;p&gt;We are also looking in the same direction that Kinect is looking, which is &lt;a href=&quot;https://msdn.microsoft.com/en-au/library/dn785530.aspx&quot;&gt;+Z in CameraSpace&lt;/a&gt;. We&apos;ve also set the up direction to be +Y in Kinect CameraSpace.&lt;/p&gt;
&lt;p&gt;Finally, we will use a default set of lights for the scene.&lt;/p&gt;
&lt;p&gt;To display our model in the viewport, MainWindow attaches to the KinectScene class exposed by its ViewModel. Note I have exposed the ViewModel and Viewport as properties of MainWindow for convenience.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public MainWindow()
{
    InitializeComponent();
    this.Loaded += MainWindow_Loaded;
}

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
   // attach the kinect scene to the viewport to display it
   this.ViewModel.KinectScene.Attach(this.Viewport);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h1&gt;Interacting With Kinect&lt;/h1&gt;
&lt;p&gt;Inside the MainViewModel we are interacting with KinectV2. We initialize Kinect to send us events for Depth and Colour frames. We then handle MultiSourceFrameArrived events and update our KinectScene with data from Kinect.&lt;/p&gt;
&lt;p&gt;An important point in the code is that we are using the CopyFrameDataToIntPtr functions to acquire the Kinect frame data for processing. These versions of the procedures outperform the versions that don&apos;t use an IntPtr since they are manipulating memory buffers directly.&lt;/p&gt;
&lt;p&gt;Also note that we are using a utility class to map the frame data to the coordinate spaces we need for rendering the depth map.&lt;/p&gt;
&lt;p&gt;First the Depth frame is mapped to Camera space so that we can position the points in 3D with respect to the Kinect sensors coordinate space.&lt;/p&gt;
&lt;p&gt;Second the Depth frame is mapped to colour space which will give us the location of RGB  pixels that correspond to the Depth frame positions, allowing us to colour the depth map appropriately.&lt;/p&gt;
&lt;p&gt;At the end of the event handler, we pass the Kinect frame data to our scene to update the point cloud.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public MainViewModel()
{
   // initialize the scene with the kinect coordinate mapper
   this.KinectScene = new Kinect2Scene();

   // create the Kinect sensor
   this.sensor = KinectSensor.GetDefault();

   // We&amp;#39;ll be creating a colour 3D depth map, so listen for color and depth frames
   this.frameReader = this.sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Depth | FrameSourceTypes.Color);

   // handle events from Kinect
   this.frameReader.MultiSourceFrameArrived += FrameReader_MultiSourceFrameArrived;

   this.sensor.Open();
}

private void FrameReader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
   var frame = e.FrameReference.AcquireFrame();

   if (frame == null) return;

   var colorFrame = frame.ColorFrameReference.AcquireFrame();
   var depthFrame = frame.DepthFrameReference.AcquireFrame();

   ...

   var pinnedDepthArray = System.Runtime.InteropServices.GCHandle.Alloc(kinectData.DepthPixels, System.Runtime.InteropServices.GCHandleType.Pinned);

   try
   {
       IntPtr depthPointer = pinnedDepthArray.AddrOfPinnedObject();
       depthFrame.CopyFrameDataToIntPtr(depthPointer, (uint)Kinect2Metrics.DepthBufferLength);
    }
    finally
    {
       pinnedDepthArray.Free();
    }

    // map the depth pixels to CameraSpace for 3D rendering
    MapperUtils.MapDepthFrameToCameraSpace(this.sensor.CoordinateMapper, kinectData);

    // map color pixels to depth space so we can color the depth map
    MapperUtils.MapDepthFrameToColorSpace(this.sensor.CoordinateMapper, kinectData);

   // update the scene using the kinect data
   this.KinectScene.Update(kinectData);

   ...
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h1&gt;The PointCloud Model&lt;/h1&gt;
&lt;p&gt;The PointCloudModel is a simple class that exposes a ModelVisual3D. It also controls the SampleSize (how many pixels from the depth map we will use) and the point size for the 3D depth map.&lt;/p&gt;
&lt;p&gt;The PointCloud class is created and added as a child Visual3D of the model.&lt;/p&gt;
&lt;p&gt;Finally the Update() function passed the KinectData to the PointCloud to that it can update itself.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public class PointCloudModel
{
        // for performance reasons we will not use every depth map point
        public static readonly int SampleSize = 10;

        // controls the size of the point cloud points
        public static readonly int PointSize = 4;

        PointCloud pointCloud;

        // expose the point cloud externally
        public ModelVisual3D Model { get; set; }


        public PointCloudModel()
        {
            // initialize the point cloud
            this.pointCloud = new PointCloud(SampleSize);
            this.pointCloud.Size = PointSize;

            // add the point cloud to the model
            this.Model = new ModelVisual3D();
            this.Model.Children.Add(this.pointCloud);
        }

        public void Update(KinectData data)
        {
            // update the point cloud using kinect data
            this.pointCloud.Update(data);
        }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The PointCloud class has two main components, the texture, which is a WriteableBitmap, and the PointCloudWorker.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public class PointCloud : PointsVisual3D
{
        // The RGB texture taken from the colour stream
        public WriteableBitmap texture;

        // a background worker to update the point cloud as data arrives
        PointCloudWorker worker;

        ...
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;In the constructor for the class we initialize the texture, setting the model material to be an ImageBrush that uses the texture.&lt;/p&gt;
&lt;p&gt;We also add a ScaleTransform3D to the model. This renders the model as the KinectV2 sees it. If we don&apos;t do this the model would be reversed on screen.&lt;/p&gt;
&lt;p&gt;Finally we initialize the PointCloudWorker class that does the grunt work.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
 public PointCloud(int sampleSize)
 {
            this.sampleSize = sampleSize;

            // initialize the texture to the size of the RGB frame
            this.texture = new WriteableBitmap(Kinect2Metrics.RGBFrameWidth, Kinect2Metrics.RGBFrameHeight, Kinect2Metrics.DPI, Kinect2Metrics.DPI, PixelFormats.Bgr32, null);

            // setup the texture brush to colour the point cloud using the RGB image
            var materialBrush = new ImageBrush(this.texture)
            {
                ViewportUnits = BrushMappingMode.Absolute,
                Viewport = new System.Windows.Rect(0, 0, this.texture.Width, this.texture.Height),
                AlignmentX = AlignmentX.Left,
                AlignmentY = AlignmentY.Top,
                Stretch = Stretch.None,
                TileMode = TileMode.Tile
            };

            this.Model.Material = new DiffuseMaterial(materialBrush);

            // reverse the model on the x axis so we see what Kinect sees
            this.Model.Transform = new ScaleTransform3D(-1, 1, 1);

            worker = new PointCloudWorker(this.sampleSize);
            worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;In the Update method, we ask the PointCloudWorker to update the PointCloud if its not busy. Depending on performance this may mean that we don&apos;t update the cloud for every Kinect frame we recieve.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public void Update(KinectData data)
{
            // if the worker is still updating, just bail
            // note: this may add some update delay depending on system performance
            if (this.worker.IsBusy) return;

            // send the data to the worker
            var workerArgs = new CloudWorkerArgs
            {
                Data = data
            };

            this.worker.RunWorkerAsync(workerArgs);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;When the PointCloudWorker completes its job, we replace the points in the PointsVisual3D with the new points generated by the Worker, and update the texture with the latest RGB pixels. We also set the TextureCoordinates in the Mesh to those created by the worker.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // get the result from the worker and update our model
            // We will just replace the texturecoordinates and points with the results from the worker
            var workerResult = e.Result as CloudWorkerResult;

            this.Mesh.TextureCoordinates = new PointCollection(workerResult.TexturePoints);
            this.Points = new Point3DCollection(workerResult.PointCloud);
            this.texture.WritePixels(
                                  new Int32Rect(0, 0, Kinect2Metrics.RGBFrameWidth, Kinect2Metrics.RGBFrameHeight),
                                  workerResult.Data.ColorPixels,
                                  Kinect2Metrics.ColorStride,
                                  0);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h1&gt;The PointCloudWorker&lt;/h1&gt;
&lt;p&gt;This is where we do the grunt work.&lt;/p&gt;
&lt;p&gt;In the constructor we create a set of indexes that are used to sample the depth map and update the pointcloud and texture vertices arrays.&lt;/p&gt;
&lt;p&gt;The KinectData is passed as part of the event args to OnDoWork. We use a Parallel.For loop to update the point cloud asynchronously and efficiently.&lt;/p&gt;
&lt;p&gt;Note that for the texture vertices we need four points (two triangles). We want each cloud point to take on the colour of the single pixel in the RGB texture, so each vertex points to the same point in the texture.&lt;/p&gt;
&lt;p&gt;Finally, we update the point cloud X,Y and Z coordinates, taking care that of -Infinities (in fact if we include -Infinities in the point positions, the model will not render at all).&lt;/p&gt;
&lt;p&gt;When we are done we set the resulting point cloud in the DoWorkEventArgs.Result property.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;csharp&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public class PointCloudWorker : BackgroundWorker
{
        Point[] texturePoints;
        Point3D[] pointCloud;
        private int[] pointIndexes;

        public PointCloudWorker(int sampleSize)
        {
            // select a set of indexes based on the sample size
            this.pointIndexes = Enumerable.Range(0, Kinect2Metrics.DepthFrameWidth * Kinect2Metrics.DepthFrameHeight)
                .Where(i =&amp;gt; i % sampleSize == 0).ToArray();

            // initialize the arrays of points with empty data
            this.texturePoints = this.pointIndexes.SelectMany(i =&amp;gt; Enumerable.Repeat(new Point(), 4)).ToArray();
            this.pointCloud = this.pointIndexes.Select(i =&amp;gt; new Point3D()).ToArray();
        }

        protected override void OnDoWork(DoWorkEventArgs e)
        {
            // if we have no depth data, just bail here
            var args = e.Argument as CloudWorkerArgs;
            var data = args.Data;

            // update the texture coordinates and points in parallel
            Parallel.For(0, this.pointIndexes.Count(), (i) =&amp;gt;
            {
                var j = this.pointIndexes[i];

                // update texture coordinates, we need four vertices per point in the cloud
                // however, all texture coordiates can point to the same RGB pixel so the
                // point assumes that colour

                var colorPoint = data.MappedDepthToColorPixels[j];

                var k = i * 4; //there are 4 texture vertices per cloud point

                this.texturePoints[k].X = float.IsNegativeInfinity(colorPoint.X) ? 0 : colorPoint.X;
                this.texturePoints[k].Y = float.IsNegativeInfinity(colorPoint.Y) ? 0 : colorPoint.Y;
                this.texturePoints[k + 1].X = float.IsNegativeInfinity(colorPoint.X) ? 0 : colorPoint.X;
                this.texturePoints[k + 1].Y = float.IsNegativeInfinity(colorPoint.Y) ? 0 : colorPoint.Y;
                this.texturePoints[k + 2].X = float.IsNegativeInfinity(colorPoint.X) ? 0 : colorPoint.X;
                this.texturePoints[k + 2].Y = float.IsNegativeInfinity(colorPoint.Y) ? 0 : colorPoint.Y;
                this.texturePoints[k + 3].X = float.IsNegativeInfinity(colorPoint.X) ? 0 : colorPoint.X;
                this.texturePoints[k + 3].Y = float.IsNegativeInfinity(colorPoint.Y) ? 0 : colorPoint.Y;

                // update the 3D positions in the point cloud
                var cloudPoint = data.MappedDepthToCameraSpacePixels[j];

                if (!float.IsNegativeInfinity(cloudPoint.X))
                {
                    this.pointCloud[i].X = cloudPoint.X;
                    this.pointCloud[i].Y = cloudPoint.Y;
                    this.pointCloud[i].Z = cloudPoint.Z;
                }
            });

            // return the result
            var workerResult = new CloudWorkerResult
            {
                Data = data,
                TexturePoints = this.texturePoints,
                PointCloud = this.pointCloud
            };

            e.Result = workerResult;

            base.OnDoWork(e);
        }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h1&gt;Program Output&lt;/h1&gt;
&lt;p&gt;Here is a set of images of the back of my office. The first picture is front on, the second is an isometric view. The HelixViewport3D allows you to rotate the view by right-clicking and moving the mouse.&lt;/p&gt;
&lt;p&gt;For performance reasons in the code I have sampled every 10 pixels from the depth map. The sampling amount can be adjusted in the PointCloudModel class.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 596px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAADbElEQVQoz32P209TdwDHTxwYKQ8KwSiXNrXnQKFXSi8UGIViy1XZFn1wkAGVYq/bYjTLIqKlbq7u+mCRUynTZcmCSQN7KKUXOD09Pef8Tk9LC9QYjUwXzcw0ccPJH2C8zGQvSz75vn3y/X6he2Byi7H9DSxPqbGnpHmLsW5nHM9Yx28R82ZkbDNsfoBZtjPOf1j7dsb5F239gxi9+8vQunVg7dwx6G42cO9W4vHvzNaj3PMn+Ueb1J1c+FZm8WZ6MZ9e3GCDeTZ4O7d0/yZ2O7v08A7x7M/1x/fBkwe5hxtzEEuGCCozODjU//4R03Fz76H3RszWCdcXZ866Ph8/f2bCdeqz8VGLo6uvv7GlVa1tmf3x5yQJMCLNxucgCguQFNPb21VaVlLJLUdqkEuer/L5jRQD0mwKAHp9LRcIBI4NDDaolQL4wCdOO5GIr2AEFbkGUfF5imZNo6Y6iUipVuna26w2m9fr9U5NuS+4Bwc+9Hovf3ryZFff4dY2fZ1Y5PFcBIBeiZN07Kc38ohpWK6Qv6vTdff0NDY1yerrJXL5AVjQpG7Q6/Uarbb/g6PGnkN79+13uSbTbGoZ+1cm6ZTdYVOqVR0GQ2d3t0KlVGo0DSqVVCY16nWTbrfdbh8ZGv5oaLin77Dn0tfM22Y6Pk9SKZvdqtFqjF1GQ2enWCoTS6W1Eom0Xi4S1coVij0lJRxOEQRBDqdzORbD8TiGU/+RG5u1hk5je0dHtVBYLRQKEKS1TafWqCurKos4nF1FuwoKC5wfO1GfL5kkVl7K11/KSZIZMQ3r2tsOGg3alhYun1/F4/H4fLFErGhQcHncgsLCHe/sKC0rRVF0/OwEoKlXs6+//syOnRiFEb5MJlbUy+qENQgigGEBj8ctL99fVVkBwwIuj1snqrVYLadOn06z7DL2ZvYCnqBCoUWP52Jzc2NFxT4E4ddUwzDM5/KqyvaW7d6zm1PMKS7m7NxZYDAcDIfD0WhwKRIlI9cggM9j8QRBxLOrmRs35s67zk1Nef3+Gf/sK/xXp9Er3373zQX3pNNu+3VhgUhEp6+AaTSLB70QnQiuJFgMp2JYMpFkQCpLM6sUyFDgba7SzGtyOMGEo1H/bBqdXcNDM1Am9gOIzTAxlIn5QASlw9P/AxP1gagvGfo+uXSZDX35Ao1Zx5tHTNqNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;depthmap-front&quot;
        title=&quot;depthmap-front&quot;
        src=&quot;/static/74b159782e61479ec2777144c9ff2998/699b7/depthmap-front.png&quot;
        srcset=&quot;/static/74b159782e61479ec2777144c9ff2998/772e8/depthmap-front.png 200w,
/static/74b159782e61479ec2777144c9ff2998/e17e5/depthmap-front.png 400w,
/static/74b159782e61479ec2777144c9ff2998/699b7/depthmap-front.png 596w&quot;
        sizes=&quot;(max-width: 596px) 100vw, 596px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 596px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAADkElEQVQozzXR/U8aBxjA8ctenEUWnWW2tHZFjYrUpq6tAu0KiIJycpxgW9RpoYvaNlrdmiwM0babriKl3YR0NSxrYJP+YBdJ7DYQcYBwd7C764BkXaDVFN9iFdy/sMCyz+/fPE+eB1hZW9v7Zy+dTqdSqXQ6vZe1u7v7d9bzv54nEolUKrX7v+3t7Xg8jqIoSZJA4kUiFoutr62vr28kk8nV1dVkMvly5eWLrEQisbmxsbm5ubOz8/r1zsrqajweT756lU6nt7a2AJIkNZrP1ZfVvX29F5TnVZfV1weHZK2w4nwbBMMfd3c5nU6DYdLn8xE4Pnr7K169oAUCn/7yNBKJAM+e/Wm8Z6yuqT7x4fGakyekcGtnVzeby2FzOadqT0MyaG5ubmR0xOVyIQgyZTKXM5kiccNj+0xmbYIgZmdnBUIB5wyHXy+AFW2f9PTUcWprTtacZtdVVpQNDQ4olRfv3zNardYp09Sx49UqVbfT+RuO40AoFFpcXFS0KdhcdlNzk0yugOVyJquqpKyEyaoqZRTfNeghmczhcPh8PhRFpRCk0WgwLBQKhQAURZEg0n99gHuWC0pbZHIFXyjU6yfMZrPVZgMl4kePfrDb7Xr9pNvtxjDssxs3hj4dwrI1gGRN6PXn+LwWSNoMgheUSgzDUASNRiIdHe0PH36nUquPFhdDINgoFtlsVovFsry8HA6HM3EwGLRYLPUNQokUrG8Q3v/2G5Ik/X5/NBq7dKlLLBYp29tLy0oAAOCe4bpcrvn5eb/fn4lRFA0EAk9+fiIQ8s/xPwJbJMPD2sHBgWvXrvT19rBYTPohOiyXCwQCJquyqbnJZDZNT0+jKJpZG8Mwj8eDIEh//1VO3amO9ouSZnEVs6KU8QH9YBGNVlhYWEChUvKolHdyc6Qyad+VqzP2x8Egkpn8XxwIBBYWFmBYyuOdlUFSUaNQwOcxK8sPH6bTaO9R8nLfynlzHyW3orK8QdTo9S243b+Gw39kXuX1+rze33Ecv3NnHJZBarVKrVJ1dnZUH6tiHD1SVEQryKfmUXPfznnjwMEi+08/Ttkcxu9n0GAgczCPx7OU5XA4tNovWuWwBATZ7FoG4wj90AHa+4X5+dT9+wuo7+7T6XQY5jeZsAcPgsGgF4hGowRBkCRJEEQkEllaWjIYDbe+vD02Pjb+9djIzdFh3bBWp715a3RyciIcDpMZKI4j0WjsX8UH0lDFWwuyAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;depthmap-iso&quot;
        title=&quot;depthmap-iso&quot;
        src=&quot;/static/2d7a4d1e234c31bc8a512e2571cc867b/699b7/depthmap-iso.png&quot;
        srcset=&quot;/static/2d7a4d1e234c31bc8a512e2571cc867b/772e8/depthmap-iso.png 200w,
/static/2d7a4d1e234c31bc8a512e2571cc867b/e17e5/depthmap-iso.png 400w,
/static/2d7a4d1e234c31bc8a512e2571cc867b/699b7/depthmap-iso.png 596w&quot;
        sizes=&quot;(max-width: 596px) 100vw, 596px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And that&apos;s it!, we have a nice and simple 3D rendering of what Kinect can see.&lt;/p&gt;
&lt;p&gt;If you want to know more about programming with the Kinect SDK, &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/dn799271.aspx&quot;&gt;Read the docs&lt;/a&gt; or &lt;a href=&quot;https://www.amazon.com/gp/product/1484223152/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;#x26;tag=ginocoatescom-20&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;linkCode=as2&amp;#x26;creativeASIN=1484223152&amp;#x26;linkId=cbeedb00236d2ab04025be98f7cbefb0&quot;&gt;pick up this book&lt;/a&gt; on Amazon.com.&lt;/p&gt;
&lt;p&gt;Kinect for Xbox One Sensor&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B00INAX3Q2/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B00INAX3Q2&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=4ae42ae2c935e22bb096e0a45ce49416&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B00INAX3Q2&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B00INAX3Q2&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Kinect for Xbox One Adapter&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B01GVE4YB4/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B01GVE4YB4&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=82081b6a15ff60f82b7f9d6e65915abe&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B01GVE4YB4&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B01GVE4YB4&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Backing up your Multi-Site Wordpress Blog to AWS S3]]></title><description><![CDATA[Today I'll be demonstrating how to back up a wordpress blog to Amazon S3 storage using the free version of the BackWPup plugin. If you are…]]></description><link>https://www.ginocoates.com/2017-01-06-backing-up-your-multi-site-wordpress-blog-to-amazon-s3/</link><guid isPermaLink="false">https://www.ginocoates.com/2017-01-06-backing-up-your-multi-site-wordpress-blog-to-amazon-s3/</guid><pubDate>Fri, 06 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAABnklEQVQY01WOW0/aYACG+/f0ziyBHijWQ7+6uYQtkkXvvPHSxEVBzohb1MUD9gAmFnS2pRaYiAEZniCaLWO0FOIhLjH2W9Zd+eS5ePNcvYgvg74Us7WHiPpEdEF0ruS8Cfmt/1/B/Fk8sOf2ZbBQhkGiOfK/MdUdVcmQgoYVPKxgQdkZVrCQjMZU90VbKv9gd2uz2frHvbN59SqRqc+JlUUkkiMiChFRXEHJEVGI9aOJtW+eVHVGrM3WW/uXbXVZAxulD9zJ9JI69Dk/tqzRicPRmOr+cjiJsNWp9bJntfRGbgaMxwsLWk/w3oLPEEILWhDC3p9rqeGPaXg8T8TzRFB2hOxrcWkMaf6snV8fn98ctfXWXe/RNLsdwzTNrq7rhqHrut7r3HaMbuGMX5GmAuLQWvHdJ43xHwxEv9IIjrleM+MT772ABv39fSRJ0jQNAENRFIETDAAUNTg8PEINjgIAHM5XldPyg/Wr/jt73EghW8ktgec5jhNSQtJG4AWO43ieZ1mWt0mn08nkdnontboZ/355UsyXVKVQKGp/ATN+FD9opCvqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;backwpup&quot;
        title=&quot;backwpup&quot;
        src=&quot;/static/4421d63d2829dcd0e0405ba2b91451df/5a190/backwpup-1.png&quot;
        srcset=&quot;/static/4421d63d2829dcd0e0405ba2b91451df/772e8/backwpup-1.png 200w,
/static/4421d63d2829dcd0e0405ba2b91451df/e17e5/backwpup-1.png 400w,
/static/4421d63d2829dcd0e0405ba2b91451df/5a190/backwpup-1.png 800w,
/static/4421d63d2829dcd0e0405ba2b91451df/8802b/backwpup-1.png 1135w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Today I&apos;ll be demonstrating how to back up a wordpress blog to &lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;Amazon S3 storage&lt;/a&gt; using the free version of the &lt;a href=&quot;https://backwpup.com/&quot;&gt;BackWPup&lt;/a&gt; plugin. If you are already hosting your wordpress site on AWS, then backing up to S3 makes sense. S3 is a robust, secure and easy to use cloud storage solution. &lt;a href=&quot;https://aws.amazon.com/s3/pricing/&quot;&gt;S3 is not free&lt;/a&gt; but pricing is fairly reasonable.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://backwpup.com/&quot;&gt;BackWPup&lt;/a&gt; is a nice little plugin that supports backup of wordpress to a number of destinations, including Dropbox, FTP and Amazon S3. The S3 integration works very well once the configuration is complete. They have both a free version with limited features and a supported version with many more. BackWPup also supports Amazon Glacier which may be more cost effective, however this feature is only available on the PRO version.&lt;/p&gt;
&lt;p&gt;In order for the plugin to work properly and for the S3 setup to be secure you need to use a combination of S3 bucket policies, IAM policies, groups and users.&lt;/p&gt;
&lt;p&gt;In this article we&apos;ll be configuring a complete backup of a WordPress multi-site installation including database, files and plugins. I will assume that you already have signed up for an AWS account. Lets go….&lt;/p&gt;
&lt;h1&gt;Step by Step&lt;/h1&gt;
&lt;p&gt;Click on MySites &gt; Network Admin &gt; Plugins &gt; Add New. Search for BackWPup and click &quot;Install Now&quot;.&lt;/p&gt;
&lt;p&gt;Once the plugin is installed, click on &quot;Network Activate&quot;&lt;/p&gt;
&lt;p&gt;Click on My Sites &gt; Network Admin &gt; Dashboard. When activated you should see a BackWPup menu in the sidebar.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABwElEQVQoz22Ry46bMBSGeYJ2laZVM0pISMBcDMEGG8ydAEPIUFKps+0rdN/VTHOppi9dJaTkVunTkTef/t/ncO+ff7z7/vODmfS8uhd86dGiLyifBnx/wA8FYLEoWzZ51aRlHaSljp0OiBxuoFqfIfk4lvoTpT+aDUT4IICHsTjgZ6OpjJ0gSB+9OGdxTv3EJJ5JfERPcE/rb1mxcry0br6WT81MhhNJnQBtImkCgJYb5tWaerFmEh27c5sd/ROcZhJZxwBiRD0DU15ShlMwFtWxpAoAYidsk91w4YapzaJLn6vWz4uylnUbOYGGiIFdRH1RNXhREQC0WZSWdVJURdXky4ZFmWG5Z9liESKeBPGceDqiimFDRGeK3sqIBnFeES/RsTO32W3toQCGU8CLh7b8TBkdkMfSqTai/qKs3XBxGXiW290c6B6S1soTSXPCLKsOp0rypUnYnXwndPOQfDyVnzz+u9P/5NbstO5ts7hYNflyHeermw9fJd8jAEi8OMoqPylsFiEaIBrg42y5la/yRRVix09LFuWKYQPdkq/hLkt2mxMAHE4BIt6v7f51u3/Z7Da7t+3+zGb/tvv95y8vJKTDE9cPHAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;backup-menu&quot;
        title=&quot;backup-menu&quot;
        src=&quot;/static/1beb620060563aeb43c90ebb96cb5a26/5a46d/backup-menu-300x200.png&quot;
        srcset=&quot;/static/1beb620060563aeb43c90ebb96cb5a26/772e8/backup-menu-300x200.png 200w,
/static/1beb620060563aeb43c90ebb96cb5a26/5a46d/backup-menu-300x200.png 300w&quot;
        sizes=&quot;(max-width: 300px) 100vw, 300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;AWS IAM Setup&lt;/h1&gt;
&lt;p&gt;First we&apos;ll create an IAM policy that lets the BackWPup create, list and upload to s3 buckets in our account.&lt;/p&gt;
&lt;p&gt;Navigate to the AWS IAM system and click on Policies &gt; Create Policy&lt;/p&gt;
&lt;p&gt;Click on Create your own policy&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 7.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsTAAALEwEAmpwYAAAALElEQVQI143LwREAIAgDMPZfF9qP0Ho6Afknqqq7z6e1kWxFZgIgQdJr79oXNaF2nU3rb6YAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;createownpolicy&quot;
        title=&quot;createownpolicy&quot;
        src=&quot;/static/9c4586560e6aeb4ffcd0d68cc082748a/5a190/createownpolicy-1-1024x77.png&quot;
        srcset=&quot;/static/9c4586560e6aeb4ffcd0d68cc082748a/772e8/createownpolicy-1-1024x77.png 200w,
/static/9c4586560e6aeb4ffcd0d68cc082748a/e17e5/createownpolicy-1-1024x77.png 400w,
/static/9c4586560e6aeb4ffcd0d68cc082748a/5a190/createownpolicy-1-1024x77.png 800w,
/static/9c4586560e6aeb4ffcd0d68cc082748a/2bef9/createownpolicy-1-1024x77.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Create a policy that allows s3 bucket management called s3-manage. Fill in the fields as below and click Validate Policy then Create Policy.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAABKklEQVQoz52STU7EMAyFc/8LsOMmLDgGjITEjBjSJmn+mzp2HNQWDRIbOnx6shwrz5EjCztZNao5z0SEFREbIjVqrTVuN5g2rRX6rjOzsMENehzMLiWn6WqdjT7kEHLwOfjkfY5uLq6U/RhLcsmXWoR2RjuzEABXaBUaQIOFYNnjT7JsgpuggRjlEGPovTMxt/skAKDWStsM3PkuREopxrj+ANGqvekxhFLKWjuOY42Zc2kVmVo/wPqyMYaIcs6DlKgducSIR81aa9puF6nh5b2+fVAuR81KKUTk3kFZOF3q63k5XSjmo+Z1txC9nqpPjNQbHxl7NUspSynceS/8aVhj7+u2UhPTNBFR/xcizSWlmDZqrXv7X3TmGdC68Hm9GmPGUT0+Xx6ezl8HSjR+eguoHQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;reviewpolicy&quot;
        title=&quot;reviewpolicy&quot;
        src=&quot;/static/621c162e8f7bd0549ded63dd8c890915/5a190/reviewpolicy-1024x707.png&quot;
        srcset=&quot;/static/621c162e8f7bd0549ded63dd8c890915/772e8/reviewpolicy-1024x707.png 200w,
/static/621c162e8f7bd0549ded63dd8c890915/e17e5/reviewpolicy-1024x707.png 400w,
/static/621c162e8f7bd0549ded63dd8c890915/5a190/reviewpolicy-1024x707.png 800w,
/static/621c162e8f7bd0549ded63dd8c890915/2bef9/reviewpolicy-1024x707.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we&apos;ll create a user group that has this policy attached. Go to AWS IAM and click on Groups &gt; Create New Group. Call the group wordpress and click next&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAu0lEQVQY02WQ247DMAhE8///2TqpL3GwAWMbs2qqrVZaNA88cNDMbMSMRMRMzLVWJNJlZkZEuztiSj4EH4JzeynV/swy217eQ4EzZwAYY/QxbtYq4vPpYoyvw4cQd7d/DkSktSYiqroBFCICACT6vlxm0jsiXtflvY8pQcEj5scRQi63KsrciBgRmXgt+0p16e2+936eGS6QMZ1PjyNEqMCSkYH7NqfOqarrP/zefxOqKjNLk3c/rd2I/gBz9iEzm3U8XwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;groupname&quot;
        title=&quot;groupname&quot;
        src=&quot;/static/38f3b41f791e1d864e3fd309f9736d19/5a190/groupname-1024x233.png&quot;
        srcset=&quot;/static/38f3b41f791e1d864e3fd309f9736d19/772e8/groupname-1024x233.png 200w,
/static/38f3b41f791e1d864e3fd309f9736d19/e17e5/groupname-1024x233.png 400w,
/static/38f3b41f791e1d864e3fd309f9736d19/5a190/groupname-1024x233.png 800w,
/static/38f3b41f791e1d864e3fd309f9736d19/2bef9/groupname-1024x233.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Search for the s3-manage policy that we created earlier and select it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAxUlEQVQY03WPQZKEIAxFvf8ZXeg4Wug0BhCSSIR0oTWrrn71F1kkv146ZuazBYnkulQVEQEAnBMR/UK900FIEDBEkktKKaqaUgIA7/1xHEiUEGOMCRttTjHG+Gx2dgfnQyJ6+lQ1n2dMCflEYiJCxCZ3k0VyzsyccxaRrpTrqan/St67n2kafs04zcuybNsWfPDeA8DfjbX2ddN9/hORXjtsq+n7fp4X55y1FgCMMeM4DsOwris4twN8HNcqpUVrJeJH6htvmwNbI6urRz4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;attachpolicy&quot;
        title=&quot;attachpolicy&quot;
        src=&quot;/static/93a12a114366eb66671d76f61f8693ec/5a190/attachpolicy-1024x287.png&quot;
        srcset=&quot;/static/93a12a114366eb66671d76f61f8693ec/772e8/attachpolicy-1024x287.png 200w,
/static/93a12a114366eb66671d76f61f8693ec/e17e5/attachpolicy-1024x287.png 400w,
/static/93a12a114366eb66671d76f61f8693ec/5a190/attachpolicy-1024x287.png 800w,
/static/93a12a114366eb66671d76f61f8693ec/2bef9/attachpolicy-1024x287.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Click next, review the changes and create the group.&lt;/p&gt;
&lt;p&gt;Next we&apos;ll create a new user that belongs to the wordpress group and can access s3 in our account. In IAM click on Users &gt; Add User. Call the user wordpress and give the user programmatic access.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA6UlEQVQoz4WRW4rDMAxFvf+VzDLmbxbQHeRBmsckfkm2Yske4kKZJoWeD4FBR75CChEBQETKJ3I+6l3T189QSgEAlSrM/FmudfP0fTtk771a13WapnEciSjGSJUYY4gxUNqT5AtHiJxFRHElpbRXvAdEoBgSEe28s7xqL6jTeyfCQP1sZxNO5hv5mTal9NxNSpFcLs3lNEtt27ZWtNaAaIyx1jnnvHMAEEI49g8RiJNcfv4/iZmdtQ5wnH+bpmnbdl4WrbWxxgMC4iPm47pEpN5exRjTdV3f98MwLMtyBPHOWmOtBQBmlsofXvkOQ3co+hoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;adduser&quot;
        title=&quot;adduser&quot;
        src=&quot;/static/4e87c8f1bb94c35dce83d19994d1f348/5a190/adduser-1024x456.png&quot;
        srcset=&quot;/static/4e87c8f1bb94c35dce83d19994d1f348/772e8/adduser-1024x456.png 200w,
/static/4e87c8f1bb94c35dce83d19994d1f348/e17e5/adduser-1024x456.png 400w,
/static/4e87c8f1bb94c35dce83d19994d1f348/5a190/adduser-1024x456.png 800w,
/static/4e87c8f1bb94c35dce83d19994d1f348/2bef9/adduser-1024x456.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;On the next step, search for the group we created earlier and add the user to group...&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABj0lEQVQoz42SYW/jIAyG8/9/Wb/f1m3ZLk27AKElEDCYBEiYUu5612o63SPLsmS/MrappmlC59Ch9+i9R0TnXAgx/wcVgBViGKSUUo1aK6WklIi4/EXOuQTrPZWeVo55M5cFrmvO0zTFuHUuFcuyeO/XLZOL/9NZ+fxOdd2S17anOuWchRCMsRhjKfXe13XNef+NeJzyT6YpvxyoIGNYrulhGDjnpcIYs9vtmqb5Rixx/bhMb9TUvaNmjSkZY5RS4zjGK0ZrzrmUMqX0uDAMq8KkXFQuwVxm9kqpbc5rq5QSAJQtPIpTSh5dmGdEh+i899bacjCttbUWAKy11m3OGAMAWm8eADaxMWYcNQCIYVBKxRjTb8q2b3da7qlijJTSY9sKIc7nM+v7EEJRxhjDP6lCCOJyEeICYLb/hWivT7IWhkEQQighhHSk6ygl9BekxNUcwvHUPe1fD8dTR9knoR1hNyOs/2gOP573z/uXpj0S1neU3aya49Kp+Y25VkzcpP7euEmfcn6h7iTnHh6zX+l26J/dpd+gAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;setpermissions&quot;
        title=&quot;setpermissions&quot;
        src=&quot;/static/29b8edf99c41ac89cedaddb3ca82e350/5a190/setpermissions-1024x660.png&quot;
        srcset=&quot;/static/29b8edf99c41ac89cedaddb3ca82e350/772e8/setpermissions-1024x660.png 200w,
/static/29b8edf99c41ac89cedaddb3ca82e350/e17e5/setpermissions-1024x660.png 400w,
/static/29b8edf99c41ac89cedaddb3ca82e350/5a190/setpermissions-1024x660.png 800w,
/static/29b8edf99c41ac89cedaddb3ca82e350/2bef9/setpermissions-1024x660.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;On the last step AWS will display the new users Access Key and Secret Key. Take note of these for configuring the BackWPup plugin later. Also take note of the users arn (e.g. arn:aws:iam::[youraccountid]:user/wordpress). We will use this for creating the S3 bucket policy later.&lt;/p&gt;
&lt;h1&gt;S3 Bucket Setup&lt;/h1&gt;
&lt;p&gt;Next we&apos;ll create the S3 bucket and add a bucket policy to control access to it. In the AWS Console navigate to the S3 and create a bucket called com.yoursite.wordpressbackup&lt;/p&gt;
&lt;p&gt;Click on Properties &gt; Permissions &gt; Add Bucket Policy&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAwklEQVQY03XOO3LEIAwGYO5/pNwiXbqkyBrW4FkjEJbAQhnwZtJsvvkLFXqZt3f7dVu/Xfi8rY+EzEREiMi/iCjnhEPets37FQBKKcxspI1W7/1iFzs556y1NN0n79cQPEC8tvTeVTXGaPSp6yshBOccIhLTNTNau6gqAJj+R2aetWoXkZwTADDTQUdKo05Y8KDW2r7vZt58HelnLhkAiApiRsQ0QGXucl5v/z8sZ61ca221MlM7GzNLq3fgjwXiI/wAVgRbVISqClUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;s3bucket&quot;
        title=&quot;s3bucket&quot;
        src=&quot;/static/57ecb124a1e56c47038f455b0290110b/5a190/s3bucket-1024x318.png&quot;
        srcset=&quot;/static/57ecb124a1e56c47038f455b0290110b/772e8/s3bucket-1024x318.png 200w,
/static/57ecb124a1e56c47038f455b0290110b/e17e5/s3bucket-1024x318.png 400w,
/static/57ecb124a1e56c47038f455b0290110b/5a190/s3bucket-1024x318.png 800w,
/static/57ecb124a1e56c47038f455b0290110b/2bef9/s3bucket-1024x318.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Enter the following bucket policy into the dialog. This policy will allow the wordpress user to list the contents of the bucket and put, get and delete objects. Note: The resource property should contain the arn of the bucket you created, and the Principal should contain the ARN:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{
  &amp;quot;Version&amp;quot;: &amp;quot;2012-10-17&amp;quot;,
  &amp;quot;Id&amp;quot;: &amp;quot;Policy1483571552699&amp;quot;,
  &amp;quot;Statement&amp;quot;: [
    {
      &amp;quot;Sid&amp;quot;: &amp;quot;Stmt1483571510816&amp;quot;,
      &amp;quot;Effect&amp;quot;: &amp;quot;Allow&amp;quot;,
      &amp;quot;Principal&amp;quot;: {
        &amp;quot;AWS&amp;quot;: &amp;quot;arn:aws:iam::youraccountid:user/wordpress&amp;quot;
      },
      &amp;quot;Action&amp;quot;: &amp;quot;s3:ListBucket&amp;quot;,
      &amp;quot;Resource&amp;quot;: &amp;quot;arn:aws:s3:::com-ginocoates-wordpressbackup&amp;quot;
    },
    {
      &amp;quot;Sid&amp;quot;: &amp;quot;Stmt1483571549812&amp;quot;,
      &amp;quot;Effect&amp;quot;: &amp;quot;Allow&amp;quot;,
      &amp;quot;Principal&amp;quot;: {
        &amp;quot;AWS&amp;quot;: &amp;quot;arn:aws:iam::youraccountid:user/wordpress&amp;quot;
      },
      &amp;quot;Action&amp;quot;: [&amp;quot;s3:DeleteObject&amp;quot;, &amp;quot;s3:GetObject&amp;quot;, &amp;quot;s3:PutObject&amp;quot;],
      &amp;quot;Resource&amp;quot;: &amp;quot;arn:aws:s3:::com-ginocoates-wordpressbackup/*&amp;quot;
    }
  ]
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAABzElEQVQoz4WSDW/bIBCG+f8/a5u0TtPUpnW3blo3z0kMBuwA5vMAM2FHaTup3avHpwPxcocBfd59//Dp5v3V9ccvzbur691Dexjtn0F1dG6JPI6WiNBPjoiw4okIeHIt0XcPvxEfR0JIjzFfRYaBMTZQuk1KpWJKECNABEgBsoMcUy6ljOOIYoRwkff1c+5MHQZjtNEr1kgTTrN3Ia7mCUHMuZSNZSUtJeaS8jlu5C0ulZiWXMpAOZqNE0KeOOcY4/2edHstZd3lrEuy5SXnpZQCAF+//UDWA8aYUarnWWvtnVvLv6q6Ryneu5vbBkFaQgjjNCkhBGOSc8EYhLAtLJeKy/LS7Hd39wjiElOaGJsoFYylGMubejLfNiimmmFCjDEj54Jz8FVOazvPNSplVnL9s8/N97VtAOjaFmOs19VhvSS7mr21TuuNzXyp/6vtUIA6ZZSSjKlpMlrPWs9KSc69tW/0fzhiFGIGgGVZXjvh00W91P6Aa9u1SWedMXo6aSGMEBGg/E/doUfOJ2M9JnQaT1IoKWclZ+chPXteF3yIPkTrAsS8PxJ07HtCBkIGxvlAGSZDjwmlzPnwD9b5rtt3Xffz8bHvcdM0fwHXNF8APOhLeQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;bucketpolicy&quot;
        title=&quot;bucketpolicy&quot;
        src=&quot;/static/141ed1e076acd9611690d1b449ba55a5/5a190/bucketpolicy.png&quot;
        srcset=&quot;/static/141ed1e076acd9611690d1b449ba55a5/772e8/bucketpolicy.png 200w,
/static/141ed1e076acd9611690d1b449ba55a5/e17e5/bucketpolicy.png 400w,
/static/141ed1e076acd9611690d1b449ba55a5/5a190/bucketpolicy.png 800w,
/static/141ed1e076acd9611690d1b449ba55a5/6295b/bucketpolicy.png 919w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Click save and S3 has now been secured for access by the wordpress user.&lt;/p&gt;
&lt;h1&gt;Configure BackWPup Plugin&lt;/h1&gt;
&lt;p&gt;Now we are ready to configure the backup plugin to talk to S3. Let&apos;s create a job and configure it. Back in your Wordpress installation, click on BackWPup &gt; Add Job.&lt;/p&gt;
&lt;p&gt;Enter the desired job name&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAdklEQVQI132OwQ6CQBBD9/8/UC7Asgox0Z3Zmc5GAS+oAYVEbN6lPbR1h6LwvgEgol+J6Mb/lmMiYjYzAJ8UlmGmir+o802oa19WVdt2eBdcYgrH8yl0V5I8POw22H1ck/tRcs9JXCSK8QUzL5dlTlIS1T2eyxPPLeBr62cw/gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin1&quot;
        title=&quot;plugin1&quot;
        src=&quot;/static/79b08a65d3bfba83ca8ddf84e28e355e/5a190/plugin1.png&quot;
        srcset=&quot;/static/79b08a65d3bfba83ca8ddf84e28e355e/772e8/plugin1.png 200w,
/static/79b08a65d3bfba83ca8ddf84e28e355e/e17e5/plugin1.png 400w,
/static/79b08a65d3bfba83ca8ddf84e28e355e/5a190/plugin1.png 800w,
/static/79b08a65d3bfba83ca8ddf84e28e355e/69476/plugin1.png 926w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Select the destination as &quot;Backup to an S3 Service&quot;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAyElEQVQoz5WSS27DMAxEc/+7tV0XqV2IX4lSQlH2AYokcDe10/YtB3iYAcFTKeVjmuYbn73Huq5jjIgYG3HMycxyzqoqojmriLR2iYhaa2ut1vpMnub55fXt/XwWESJKAFZr7z0lYGZENLNDeYyx3Fk3lmW5Xp2Z71tyMeu978v9BxHh7oQkIk9qb/Ju2q5ORDlnIiqlHJ1tR+4RZBdAFJGHX1v7HvV7s7sDIG0wc0rJ3f8uAzMBABEJC7P8oxkRVZWIVPXoW74AjVEsxnq+6BcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin2&quot;
        title=&quot;plugin2&quot;
        src=&quot;/static/c10b517d01dcd4d88293aa12fcec5b3f/5a190/plugin2.png&quot;
        srcset=&quot;/static/c10b517d01dcd4d88293aa12fcec5b3f/772e8/plugin2.png 200w,
/static/c10b517d01dcd4d88293aa12fcec5b3f/e17e5/plugin2.png 400w,
/static/c10b517d01dcd4d88293aa12fcec5b3f/5a190/plugin2.png 800w,
/static/c10b517d01dcd4d88293aa12fcec5b3f/58bb7/plugin2.png 985w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For now set the schedule to manually so that we can test.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAUklEQVQI133LQQ6AIBBDUe5/R4FgmCkRcDonMGFh3MjbNekPtdZSziOl3ru7c3HSF24FEQHwbifHzXYNAK01M9vFqgrgezK6KGKMOScAc86/+AHWH6d0u3UuYQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin3&quot;
        title=&quot;plugin3&quot;
        src=&quot;/static/f451e6b83a00ea04b8c2ae11442e8608/5a190/plugin3.png&quot;
        srcset=&quot;/static/f451e6b83a00ea04b8c2ae11442e8608/772e8/plugin3.png 200w,
/static/f451e6b83a00ea04b8c2ae11442e8608/e17e5/plugin3.png 400w,
/static/f451e6b83a00ea04b8c2ae11442e8608/5a190/plugin3.png 800w,
/static/f451e6b83a00ea04b8c2ae11442e8608/5b587/plugin3.png 1010w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;On the S3 tab select the region where you created the S3 bucket and enter your S3 bucket url&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABBUlEQVQY01WP226EMAxE+f/va6sFEgg4Fwg4zg1p4amC7LbsUWRZkceeqYQQj0f99f3Tdb3Wum1bxnjdtHXT9GKIMeac40W+CDcqay1jTJwMnHMA4JzXTTMMgxBimqZS53lWSimtiehfjIh930kplVKMsTLddZ3WehxHrTVrW62NMWYcRhjBOeffVMYYznnfixDCdjlMKeWcUzqbP9shhHTxYds5t5ysy7IWS2UrkaezknMuxrht27XxUzxNEwBIpQDgngcRvQ9EtCxryvk4jn3fn889xRTvma2183w+51z5pYL37iyv+4XSv8SztUopKRWARMQSj7zHaxIRi4CI8K1eVyziX0NTuaNQLFuUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin4&quot;
        title=&quot;plugin4&quot;
        src=&quot;/static/fa88f04a08792428632af3e4e8ed4985/5a190/plugin4.png&quot;
        srcset=&quot;/static/fa88f04a08792428632af3e4e8ed4985/772e8/plugin4.png 200w,
/static/fa88f04a08792428632af3e4e8ed4985/e17e5/plugin4.png 400w,
/static/fa88f04a08792428632af3e4e8ed4985/5a190/plugin4.png 800w,
/static/fa88f04a08792428632af3e4e8ed4985/6bff2/plugin4.png 957w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now enter the Access Key and Secret for the wordpress user created earlier&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAb0lEQVQY05XPzQqDQAwE4H3/N9SNUGHxZwUzGdmDOZVqW9SD4EcuCYRJwqtt6ygiTVVHAPZEwI+q/qckl1skP8spJZGm6/q9382zTnkaczbS3dczdy+lAAi6uRz8IDmKDMN4TDYzBRT6/WgrIy/JbyyMGbBVIjkcAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin5&quot;
        title=&quot;plugin5&quot;
        src=&quot;/static/06d7199bbda5b2a383728258e5352d63/5a190/plugin5.png&quot;
        srcset=&quot;/static/06d7199bbda5b2a383728258e5352d63/772e8/plugin5.png 200w,
/static/06d7199bbda5b2a383728258e5352d63/e17e5/plugin5.png 400w,
/static/06d7199bbda5b2a383728258e5352d63/5a190/plugin5.png 800w,
/static/06d7199bbda5b2a383728258e5352d63/dc333/plugin5.png 938w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If your settings are correct you should see a list of available buckets in the Bucket selection area. Select the one we created earlier.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.500000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAn0lEQVQY05VQSQ7CMAzs/5/YLKAm0CiJHS/qBS4oOZRWwAFrZI1tjcfyFEIMMQKAiPCfMXnvnXMAqN/E8oGTuFYAhJxza23vIguyNpI2SC9JkLgN/hYba53z1tpaq4gQ9WFFjqmGNYc1x1TimpdbWu4pplLawZmJiHtCxN1cmFVVRDuVA86HT8ZYf7kaY+d5zqUMc1LV5+OxbdvY/PNhL7QCTzDEeIPwAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin6&quot;
        title=&quot;plugin6&quot;
        src=&quot;/static/59b4fd4dfde2abe48217dd1037b9f241/5a190/plugin6.png&quot;
        srcset=&quot;/static/59b4fd4dfde2abe48217dd1037b9f241/772e8/plugin6.png 200w,
/static/59b4fd4dfde2abe48217dd1037b9f241/e17e5/plugin6.png 400w,
/static/59b4fd4dfde2abe48217dd1037b9f241/5a190/plugin6.png 800w,
/static/59b4fd4dfde2abe48217dd1037b9f241/4b446/plugin6.png 877w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Save settings. Now switch to the jobs screen and run the job.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0ElEQVQY043PwW7DIBBFUf7/69oqTVaNVAljsA0MGRjPgBuq1F3Was7qbt7iqdPp/eX17eN61cNwPl/cvHw6Py4hPUEZM1rrnHPTNGmtFx/sEuYQ4UACSCntrQBSiJCJYggAQERUHvIBxHxD3FvV7SuUitx67/1+7//htsFa91bphloPZhwLIq/rysIsR2qtpWTnXIyRmRVmMrO3PiZrwYcJCTLV4zEiGjMUIhFRtbbCQlzbj1rbb/1la21lmX0QkdaaWmWbbwTE/TmPzyT7528/Q8s5FCQ+UgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin7&quot;
        title=&quot;plugin7&quot;
        src=&quot;/static/a073fb903dd18c4ec24271970f682725/5a190/plugin7.png&quot;
        srcset=&quot;/static/a073fb903dd18c4ec24271970f682725/772e8/plugin7.png 200w,
/static/a073fb903dd18c4ec24271970f682725/e17e5/plugin7.png 400w,
/static/a073fb903dd18c4ec24271970f682725/5a190/plugin7.png 800w,
/static/a073fb903dd18c4ec24271970f682725/69476/plugin7.png 926w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The job progress will be displayed. If everything is setup correctly the progress bar should reach 100%&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAAApklEQVQY05XKywqCQBQG4Hn/F2hdC23jJX0RddQJocXU3BUnxaYgqUAhSm0RfBzO/58DTvgoBReq1Ofm0rbdP8CBcMwVKWtS1vQbqzSrNJ31I6JqsIm4BaWdKgtObaGwofhs7MG4rGMJ/JwGiAX5DGJugt0YL17DnDkpBX7OAzQV7rmXEi/BXoL9jC48IO5kDDxv5pfedL3p3vExzPvV7Aq9iqpCNi962zkOYBWROgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin8&quot;
        title=&quot;plugin8&quot;
        src=&quot;/static/68f0cb15c5bdd5466633f595a7c3798b/5a190/plugin8-1024x287.png&quot;
        srcset=&quot;/static/68f0cb15c5bdd5466633f595a7c3798b/772e8/plugin8-1024x287.png 200w,
/static/68f0cb15c5bdd5466633f595a7c3798b/e17e5/plugin8-1024x287.png 400w,
/static/68f0cb15c5bdd5466633f595a7c3798b/5a190/plugin8-1024x287.png 800w,
/static/68f0cb15c5bdd5466633f595a7c3798b/2bef9/plugin8-1024x287.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now switch back over the AWS console and navigate to your S3 bucket. Your newly created backup should be available.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAfklEQVQI103H2wrDIBBFUf//+/pUc7HBS4hlnKkdJRFNwUDJejjsI2YHG8Rp/YyO5r5PDQ/ljadyHCnllNLeMXOMMed83VKKkMNk182/AUMgDESotZ7GYVleiOicM8YQEQAopaSU1lpEBABmFt+9YSrIpbXz0v5169adN7XWHwB6qlE/acRSAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;plugin9&quot;
        title=&quot;plugin9&quot;
        src=&quot;/static/c43753e472471ba5ca3ceec24e2dd627/5a190/plugin9.png&quot;
        srcset=&quot;/static/c43753e472471ba5ca3ceec24e2dd627/772e8/plugin9.png 200w,
/static/c43753e472471ba5ca3ceec24e2dd627/e17e5/plugin9.png 400w,
/static/c43753e472471ba5ca3ceec24e2dd627/5a190/plugin9.png 800w,
/static/c43753e472471ba5ca3ceec24e2dd627/73caa/plugin9.png 1110w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now you can go back to the settings of the job and set the backup to run on a schedule (e.g. using WPCron). Finally, you should take the time to test restoration of your backup to, say, another test site to ensure that if works as expected ;)&lt;/p&gt;
&lt;p&gt;Learn more about Wordpress...&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/1118987241/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=1118987241&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=bb3494ec333ba2c675e81e5ab268111e&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=1118987241&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=1118987241&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Creating an RStudio Server using Terraform on AWS]]></title><description><![CDATA[I've created a set of Terraform files to quickly provision an RStudio server on AWS using one of the great ami's from Louis Aslett http…]]></description><link>https://www.ginocoates.com/2017-01-04-creating-an-rstudio-server-using-terraform/</link><guid isPermaLink="false">https://www.ginocoates.com/2017-01-04-creating-an-rstudio-server-using-terraform/</guid><pubDate>Wed, 04 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABdElEQVQY012PO0sDURCF7/vu3bn7yGYT3VeSzb5CGiGItYigoGAr2NgI2mqRfxBExMbGTlD8Ef4BCwtbwdrSxk6wUJMYCZ5mZuB8czgII4LGwgRTSjgjQjGHE8C4IVhXskzxhuI1QU1KBJqZf5cpTDBlRHCitPAVDnvVShQOEIoZXrLpkuKewV1BzYkZoznhSSwTFEA0A7uPULS3e/L0+Dw8vhgeXa3IUQTLpvAM5jAi/5Dpl/GghEtmu0YrcQYxrFlo83R49/H+dX3wdpK+9r2tBeiB8BkxpvCs7Hwyb/rQzfXGKrodbb/cn3+elV/7vZsmVIvQN7lHMPuXPL0oxZITMGjd5H5Hra5bl4fFw046aqieI2JFXYLEfGGMMAIAQyopDG3a2nQc3XB14Fh+zVoM3MKzw5oVOLpuKtCgpZTGWArjyZeyLJOkFQZhp5PGSZxlRbudVmW/3cryvMrzKu2kWTeL46TIiyiKWkm7KEoN+gf+BsPBLl7YzpiZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;terraform&quot;
        title=&quot;terraform&quot;
        src=&quot;/static/f604c86351d2cce894e399a09f9d6303/5a190/terraform-1.png&quot;
        srcset=&quot;/static/f604c86351d2cce894e399a09f9d6303/772e8/terraform-1.png 200w,
/static/f604c86351d2cce894e399a09f9d6303/e17e5/terraform-1.png 400w,
/static/f604c86351d2cce894e399a09f9d6303/5a190/terraform-1.png 800w,
/static/f604c86351d2cce894e399a09f9d6303/fbf08/terraform-1.png 962w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve created a set of Terraform files to quickly provision an RStudio server on AWS using one of the great ami&apos;s from Louis Aslett &lt;a href=&quot;http://www.louisaslett.com/RStudio_AMI/&quot;&gt;http://www.louisaslett.com/RStudio_AMI/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The files contain the following variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;local_ip - the local IP address to be given SSH access&lt;/li&gt;
&lt;li&gt;region - the aws region for the deployment&lt;/li&gt;
&lt;li&gt;instance_type - the type of instance to create. see: &lt;a href=&quot;https://aws.amazon.com/ec2/instance-types/&quot;&gt;https://aws.amazon.com/ec2/instance-types/&lt;/a&gt; zone - the availability zone the server should be created in&lt;/li&gt;
&lt;li&gt;disk_size - the size of the EBS volume to provision&lt;/li&gt;
&lt;li&gt;keep_data - whether the EBS volume should be kept on server termination&lt;/li&gt;
&lt;li&gt;keypair_name - the pre-created key pair name to use&lt;/li&gt;
&lt;li&gt;private_key - the private key to use to connect via ssh, i.e. the key from the key pair&lt;/li&gt;
&lt;li&gt;rstudio_password - the password to set for RStudio server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is also a privision.sh and libraries.R scripts that can be modified to setup the server and install addtional R libraries once its created.&lt;/p&gt;
&lt;p&gt;The code is available on github here: &lt;a href=&quot;https://github.com/ginocoates/rstudio-server-terraform&quot;&gt;https://github.com/ginocoates/rstudio-server-terraform&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To provision a server, create a variables.tfvars file, and use terraform to do the grunt work. e.g. create a tfvars file as follows:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;js&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;quot;local_ip&amp;quot; = &amp;quot;[your local ip address]&amp;quot;

&amp;quot;region&amp;quot; = &amp;quot;ap-southeast-2&amp;quot;

&amp;quot;instance_type&amp;quot; = &amp;quot;t2.micro&amp;quot;

&amp;quot;zone&amp;quot; = &amp;quot;ap-southeast-2a&amp;quot;

&amp;quot;keypair_name&amp;quot; = &amp;quot;datascience&amp;quot;

&amp;quot;private_key&amp;quot; = &amp;quot;~/.ssh/datascience.pem&amp;quot;

&amp;quot;rstudio_password&amp;quot; = &amp;quot;verysecurepassword&amp;quot;

&amp;quot;disk_size&amp;quot; = &amp;quot;100&amp;quot;

&amp;quot;keep_data&amp;quot; = &amp;quot;false&amp;quot;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Then provision the server as follows&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;terraform plan -out=terraform.plan -var-file=variables.tfvars

...

terraform apply terraform.plan
&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Terraform should report as output the public IP and DNS of the newly created server. Navigate to the IP and the RStudio prompt should be displayed.&lt;/p&gt;
&lt;p&gt;Learn Terraform and RStudio Server...&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B01MZYE7OY/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B01MZYE7OY&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=ffe4d887a7a4fafd3e292f5f00d30175&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B01MZYE7OY&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B01MZYE7OY&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/1782160604/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=1782160604&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=70d8e3092c367b79a1237bb9f86b8dbb&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=1782160604&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=1782160604&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Creating and Destroying AWS S3 Buckets in two Regions with Terraform]]></title><description><![CDATA[I ran across an issue with Terraform where I couldn't destroy an Amazon S3 bucket created in a region other than the one provided at the…]]></description><link>https://www.ginocoates.com/2017-01-03-s3-multi-region-issue-in-terraform/</link><guid isPermaLink="false">https://www.ginocoates.com/2017-01-03-s3-multi-region-issue-in-terraform/</guid><pubDate>Tue, 03 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABdElEQVQY012PO0sDURCF7/vu3bn7yGYT3VeSzb5CGiGItYigoGAr2NgI2mqRfxBExMbGTlD8Ef4BCwtbwdrSxk6wUJMYCZ5mZuB8czgII4LGwgRTSjgjQjGHE8C4IVhXskzxhuI1QU1KBJqZf5cpTDBlRHCitPAVDnvVShQOEIoZXrLpkuKewV1BzYkZoznhSSwTFEA0A7uPULS3e/L0+Dw8vhgeXa3IUQTLpvAM5jAi/5Dpl/GghEtmu0YrcQYxrFlo83R49/H+dX3wdpK+9r2tBeiB8BkxpvCs7Hwyb/rQzfXGKrodbb/cn3+elV/7vZsmVIvQN7lHMPuXPL0oxZITMGjd5H5Hra5bl4fFw046aqieI2JFXYLEfGGMMAIAQyopDG3a2nQc3XB14Fh+zVoM3MKzw5oVOLpuKtCgpZTGWArjyZeyLJOkFQZhp5PGSZxlRbudVmW/3cryvMrzKu2kWTeL46TIiyiKWkm7KEoN+gf+BsPBLl7YzpiZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;terraform&quot;
        title=&quot;terraform&quot;
        src=&quot;/static/f604c86351d2cce894e399a09f9d6303/5a190/terraform-1.png&quot;
        srcset=&quot;/static/f604c86351d2cce894e399a09f9d6303/772e8/terraform-1.png 200w,
/static/f604c86351d2cce894e399a09f9d6303/e17e5/terraform-1.png 400w,
/static/f604c86351d2cce894e399a09f9d6303/5a190/terraform-1.png 800w,
/static/f604c86351d2cce894e399a09f9d6303/fbf08/terraform-1.png 962w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I ran across an issue with Terraform where I couldn&apos;t destroy an Amazon S3 bucket created in a region other than the one provided at the prompt.&lt;/p&gt;
&lt;p&gt;To get around this I had to configure an AWS provider for each region I wanted to add a bucket to, and use the alias property to reference the provider from the s3 resource.&lt;/p&gt;
&lt;p&gt;Without the region based providers security errors were showing up when trying to manipulate the bucket in different regions.&lt;/p&gt;
&lt;p&gt;For example, Create two buckets in different regions:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;js&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;resource &amp;quot;aws_s3_bucket&amp;quot; &amp;quot;s3_bucket_1&amp;quot; {
 bucket = &amp;quot;com-ginocoates-testbucket1&amp;quot;
 region = &amp;quot;us-east-1&amp;quot;
 }

resource &amp;quot;aws_s3_bucket&amp;quot; &amp;quot;s3_bucket_2&amp;quot; {
 bucket = &amp;quot;com-ginocoates-testbucket2&amp;quot;
 region = &amp;quot;us-east-2&amp;quot;
 }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Plan and apply them...&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ terraform plan
 provider.aws.region
 The region where AWS operations will take place. Examples
 are us-east-1, us-west-2, etc.

Default: us-east-1
 Enter a value:

+ aws_s3_bucket.s3_bucket_1
 acceleration_status: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 acl: &amp;quot;private&amp;quot;
 arn: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 bucket: &amp;quot;com-ginocoates-testbucket1&amp;quot;
 force_destroy: &amp;quot;false&amp;quot;
 hosted_zone_id: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 region: &amp;quot;us-east-1&amp;quot;
 request_payer: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 versioning.#: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_domain: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_endpoint: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;

+ aws_s3_bucket.s3_bucket_2
 acceleration_status: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 acl: &amp;quot;private&amp;quot;
 arn: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 bucket: &amp;quot;com-ginocoates-testbucket2&amp;quot;
 force_destroy: &amp;quot;false&amp;quot;
 hosted_zone_id: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 region: &amp;quot;us-east-2&amp;quot;
 request_payer: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 versioning.#: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_domain: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_endpoint: &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;

Plan: 2 to add, 0 to change, 0 to destroy.

gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ terraform apply
 provider.aws.region

The region where AWS operations will take place. Examples
 are us-east-1, us-west-2, etc.

Default: us-east-1
 Enter a value:

aws_s3_bucket.s3_bucket_2: Creating...
 acceleration_status: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 acl: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;private&amp;quot;
 arn: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 bucket: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;com-ginocoates-testbucket2&amp;quot;
 force_destroy: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;
 hosted_zone_id: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 region: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;us-east-2&amp;quot;
 request_payer: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 versioning.#: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_domain: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_endpoint: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 aws_s3_bucket.s3_bucket_1: Creating...
 acceleration_status: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 acl: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;private&amp;quot;
 arn: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 bucket: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;com-ginocoates-testbucket1&amp;quot;
 force_destroy: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;
 hosted_zone_id: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 region: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;us-east-1&amp;quot;
 request_payer: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 versioning.#: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_domain: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_endpoint: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 aws_s3_bucket.s3_bucket_1: Still creating... (10s elapsed)
 aws_s3_bucket.s3_bucket_1: Creation complete
 Error applying plan:

1 error(s) occurred:

* aws_s3_bucket.s3_bucket_2: Error putting S3 ACL: TemporaryRedirect: Please re-send this request to the specified temporary endpoint. Continue to use the original request endpoint for future requests.
 status code: 307, request id: 9D4A2839B33A45F4&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We get an error, however, the two buckets were created successfully. We can query s3 to verify...&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ aws s3 ls | grep bucket
 2017-01-03 14:16:01 com-ginocoates-testbucket1
 2017-01-03 14:15:59 com-ginocoates-testbucket2&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Lets try to destroy the buckets...&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ terraform destroy
 Do you really want to destroy?
 Terraform will delete all your managed infrastructure.
 There is no undo. Only &amp;#39;yes&amp;#39; will be accepted to confirm.

Enter a value: yes

provider.aws.region
 The region where AWS operations will take place. Examples
 are us-east-1, us-west-2, etc.

Default: us-east-1
 Enter a value:

aws_s3_bucket.s3_bucket_2: Refreshing state... (ID: com-ginocoates-testbucket2)
 aws_s3_bucket.s3_bucket_1: Refreshing state... (ID: com-ginocoates-testbucket1)
 Error refreshing state: 1 error(s) occurred:

* aws_s3_bucket.s3_bucket_2: error reading S3 bucket &amp;quot;com-ginocoates-testbucket2&amp;quot;: Forbidden: Forbidden
 status code: 403, request id: E081D8D9A009E943&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Forbidden error, since the AWS provider is initialized with us-east-1, we can&apos;t delete the s3 bucket that was created in us-east-2. What I think this means is that the default provider is locked in to the region it was initialized with and can&apos;t call the s3 API in another region to refresh the state of bucket 2.&lt;/p&gt;
&lt;p&gt;Lets try to fix it. Configure the AWS provider so we can create buckets in different regions. We can do this with alias properties.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;js&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;provider &amp;quot;aws&amp;quot; {
 alias = &amp;quot;us-east-1&amp;quot;
 region = &amp;quot;us-east-1&amp;quot;
 }

provider &amp;quot;aws&amp;quot; {
 alias = &amp;quot;us-east-2&amp;quot;
 region = &amp;quot;us-east-2&amp;quot;
 }

resource &amp;quot;aws_s3_bucket&amp;quot; &amp;quot;s3_bucket_1&amp;quot; {
 bucket = &amp;quot;com-ginocoates-testbucket1&amp;quot;
 region = &amp;quot;us-east-1&amp;quot;
 provider = &amp;quot;aws.us-east-1&amp;quot;
 }

resource &amp;quot;aws_s3_bucket&amp;quot; &amp;quot;s3_bucket_2&amp;quot; {
 bucket = &amp;quot;com-ginocoates-testbucket2&amp;quot;
 region = &amp;quot;us-east-2&amp;quot;
 provider = &amp;quot;aws.us-east-2&amp;quot;
 }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Lets plan these changes...&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ terraform plan

aws_s3_bucket.s3_bucket_1: Refreshing state... (ID: com-ginocoates-testbucket1)
 aws_s3_bucket.s3_bucket_2: Refreshing state... (ID: com-ginocoates-testbucket2)

No changes. Infrastructure is up-to-date. This means that Terraform
 could not detect any differences between your configuration and
 the real physical resources that exist. As a result, Terraform
 doesn&amp;#39;t need to do anything.&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So TF now sees the state as correct. Can we now destroy the s3 buckets successfully in each region?&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ terraform destroy

gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ terraform destroy
 Do you really want to destroy?
 Terraform will delete all your managed infrastructure.
 There is no undo. Only &amp;#39;yes&amp;#39; will be accepted to confirm.

Enter a value: yes

aws_s3_bucket.s3_bucket_2: Refreshing state... (ID: com-ginocoates-testbucket2)
 aws_s3_bucket.s3_bucket_1: Refreshing state... (ID: com-ginocoates-testbucket1)
 aws_s3_bucket.s3_bucket_1: Destroying...
 aws_s3_bucket.s3_bucket_2: Destroying...
 aws_s3_bucket.s3_bucket_2: Destruction complete
 aws_s3_bucket.s3_bucket_1: Destruction complete

Destroy complete! Resources: 2 destroyed.&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Yes! Now is it possible to move bucket2 to us-east-1? Lets first recreate them.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ terraform apply

aws_s3_bucket.s3_bucket_1: Creating...
 acceleration_status: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 acl: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;private&amp;quot;
 arn: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 bucket: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;com-ginocoates-testbucket1&amp;quot;
 force_destroy: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;
 hosted_zone_id: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 region: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;us-east-1&amp;quot;
 request_payer: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 versioning.#: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_domain: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_endpoint: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 aws_s3_bucket.s3_bucket_2: Creating...
 acceleration_status: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 acl: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;private&amp;quot;
 arn: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 bucket: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;com-ginocoates-testbucket2&amp;quot;
 force_destroy: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;
 hosted_zone_id: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 region: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;us-east-2&amp;quot;
 request_payer: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 versioning.#: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_domain: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 website_endpoint: &amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;lt;computed&amp;gt;&amp;quot;
 aws_s3_bucket.s3_bucket_1: Still creating... (10s elapsed)
 aws_s3_bucket.s3_bucket_2: Still creating... (10s elapsed)
 aws_s3_bucket.s3_bucket_1: Creation complete
 aws_s3_bucket.s3_bucket_2: Creation complete

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Lets use change testbucket to to use the provider us-east-1. In theory this should destroy the bucket and create a new one in the desired region. Fingers crossed....&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;provider &amp;quot;aws&amp;quot; {
 alias = &amp;quot;us-east-1&amp;quot;
 region = &amp;quot;us-east-1&amp;quot;
 }

provider &amp;quot;aws&amp;quot; {
 alias = &amp;quot;us-east-2&amp;quot;
 region = &amp;quot;us-east-2&amp;quot;
 }

resource &amp;quot;aws_s3_bucket&amp;quot; &amp;quot;s3_bucket_1&amp;quot; {
 bucket = &amp;quot;com-ginocoates-testbucket1&amp;quot;
 region = &amp;quot;us-east-1&amp;quot;
 provider = &amp;quot;aws.us-east-1&amp;quot;
 }

resource &amp;quot;aws_s3_bucket&amp;quot; &amp;quot;s3_bucket_2&amp;quot; {
 bucket = &amp;quot;com-ginocoates-testbucket2&amp;quot;
 region = &amp;quot;us-east-1&amp;quot;
 provider = &amp;quot;aws.us-east-1&amp;quot;
 }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Plan the changes...&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gino@gino-MacBookPro:~/sourcecode/s3-multiregion$ terraform plan

aws_s3_bucket.s3_bucket_1: Refreshing state... (ID: com-ginocoates-testbucket1)
 aws_s3_bucket.s3_bucket_2: Refreshing state... (ID: com-ginocoates-testbucket2)
 Error refreshing state: 1 error(s) occurred:

* aws_s3_bucket.s3_bucket_2: error reading S3 bucket &amp;quot;com-ginocoates-testbucket2&amp;quot;: Forbidden: Forbidden
 status code: 403, request id: 83855B6D6780B04D&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Oops, error. The state has the bucket in us-east-2, but the tf files reference us-east-1. So the provider is initialized with the us-east-1 endpoint when we run terraform plan. But it can&apos;t refresh the state of the bucket in us-east-2 as its configured to use a different AWS endpoint. Kinda makes sense.&lt;/p&gt;
&lt;p&gt;So, with TF we can&apos;t destroy a bucket in one region and recreate it in another with this setup. Something to watch out for!&lt;/p&gt;
&lt;p&gt;To achieve the move we&apos;d have to remove bucket2 from our tf files, apply to allow TF to destroy it, then add it back with the desired region, and apply these changes.&lt;/p&gt;
&lt;p&gt;Learn Terraform Here....&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B01MZYE7OY/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B01MZYE7OY&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=ffe4d887a7a4fafd3e292f5f00d30175&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B01MZYE7OY&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B01MZYE7OY&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Kanban - Feel the flow]]></title><description><![CDATA[In this article I will be talking about my favorite way to manage flow of work through a software team, Kanban. Kanban is a great way to…]]></description><link>https://www.ginocoates.com/2017-01-02-feel-the-flow/</link><guid isPermaLink="false">https://www.ginocoates.com/2017-01-02-feel-the-flow/</guid><pubDate>Mon, 02 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAADBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAATJ5bg1pH//EABoQAQEAAgMAAAAAAAAAAAAAAAIBERIAAzP/2gAIAQEAAQUCKLm5wnmz37wdZOf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAdEAABAwUBAAAAAAAAAAAAAAAAAREhAhAxMkFh/9oACAEBAAY/AtmUmpIOjemO2//EABsQAQEBAAMBAQAAAAAAAAAAAAERACExQVFh/9oACAEBAAE/IREVpZ3kEBj0jqtR+BunwzMSjlGDk+O//9oADAMBAAIAAwAAABCoL//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAQEAAgIDAAAAAAAAAAAAAAERACFBcYGxwf/aAAgBAQABPxAHkBjRx5yXiBFX5csmTWye8hQEijUuAgEFrzcIAaQOs//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;work-1713103_640&quot;
        title=&quot;work-1713103_640&quot;
        src=&quot;/static/f632eb796cf072c5b17809b917978e33/c08c5/work-1713103_640.jpg&quot;
        srcset=&quot;/static/f632eb796cf072c5b17809b917978e33/e07e9/work-1713103_640.jpg 200w,
/static/f632eb796cf072c5b17809b917978e33/066f9/work-1713103_640.jpg 400w,
/static/f632eb796cf072c5b17809b917978e33/c08c5/work-1713103_640.jpg 640w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this article I will be talking about my favorite way to manage flow of work through a software team, Kanban.&lt;/p&gt;
&lt;p&gt;Kanban is a great way to gain team agility without requiring changes to existing processes. Kanban allows these processes to be improved over time with minimal impact to how a team works, while giving full visibility on the process and bottlenecks within the delivery system. 25% of respondents on the version one state of agile survey said they were using Kanban . In a nutshell, Kanban…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Helps visualize the development process and bottlenecks within the delivery system&lt;/li&gt;
&lt;li&gt;Enables agility within existing development processes&lt;/li&gt;
&lt;li&gt;Improves the speed of delivery of software to the business users requesting it&lt;/li&gt;
&lt;li&gt;Enables a continuous improvement culture within a team&lt;/li&gt;
&lt;li&gt;Uses an SLA based scheduling approach&lt;/li&gt;
&lt;li&gt;Uses frequent delivery of usable software on an agreed schedule with business users&lt;/li&gt;
&lt;li&gt;Improves team morale&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;What is Kanban?&lt;/h1&gt;
&lt;p&gt;Kanban is a technique for managing software development in an efficient way. It is not a development process in itself and can be used to improve the performance existing development processes (e.g. Scrum). As it doesn&apos;t mandate drastic changes in the development process and it can be used as a change agent that gradually improves delivery performance over time with minimal impact to staff and processes.&lt;/p&gt;
&lt;p&gt;At its core it is a visual system designed to help:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visualize the development process as a pipeline of stages&lt;/li&gt;
&lt;li&gt;Visualize the current state of items being produced within the process.&lt;/li&gt;
&lt;li&gt;Visualize current resource assignments for items within the process.&lt;/li&gt;
&lt;li&gt;Visualize blockages to items being produced within the process.&lt;/li&gt;
&lt;li&gt;Visualize Work in Progress Limits within the system (see below).&lt;/li&gt;
&lt;li&gt;Visualize lead times for classes of work items within the process.&lt;/li&gt;
&lt;li&gt;Maximize the throughput of bottlenecks within the system in order to increase the flow of items through the system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In manufacturing a Kanban card is a visual signal that more inventory needs to be produced and is used to control waste in the system. Kanban was developed by Taiichi Ohno, at Toyota, to find a system to improve and maintain a high level of production.&lt;/p&gt;
&lt;p&gt;In Kanban for software development we use a physical board and system cards to display work items within the development process. Typically the board is divided into the various columns representing the phases of the development process. As items are developed the cards are pulled through the different columns in the board by development resources that become free. At a glance, the Kanban board gives a clear picture of where things are within a current release. Each card on the board gives a clear status for items being developed.&lt;/p&gt;
&lt;h1&gt;Example Kanban Board&lt;/h1&gt;
&lt;p&gt;Below is a sample similar to board formats that I&apos;ve used in the past:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAACTElEQVQozy2Sy2sUQRDG55/w4M2bf41XD549CHoRwaOiaPASEZJLRAlIEILPjSJCQhSzQiJIzLJKls1m3Me8p6d7unt6+l2yG2+/ouqj6iu+oMQEIdQIqYxx4FVL2PFd3rsq8B9rwShprbXOWmvOwMzJOGeYaANlLAA4D4QqTJRQBieHWfidCcYcNN43ylZYUK5b7RBqWu1bZWumqVCBNha0ttbV5aCJu4RMeBVq3CPZdPJ+a9r5iOJQ5Ack61MybuIurYakClnZy4so0FJBHBmhxXQdhtfJ7IMdPzntLw13O9/OXfh6/mK2/wqmt/hohccdOLnBxht4sGyOLuWjzcVm8NYBm2yq4R0S7bTx22jwooiPeO8x7a+S9ECNV+j0Hcu6Klym8TYZrorDy+nJ6zMxWAd89kaPHpJoV0/Wej+fJ/GRnj1tJuss6/76sTbZfSaKL/JkiUSf2GhF96+kp1v/xcYBG2/o45s02fUAFkDwVI0eyNFSyzMLIA3IsuvD+03UUS0COcV1FWitQQirLYs6avSI5vvez98vBVbZZ5nvtKIGcMZCk+3J4T067RgHAEAbGehWQpoYoWT0EsLbTb4314I/uwgAlDYA3ngQ2bYbXON/142ZG625CBY9MM5lZZWXqKgoJjXCpKp5iSpMSEVYgXBFOEJpEf8uipk03jpf0Taw1i1C4qVsrVZKSquV1UprbZR0RiuljGy1mifQGm+M8wtjTKggQRTVPMc8RawgTYJojnlBmnhR5pjPoW4SxFLESsISROPFTJjgf76DjCsg6w9MAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;board&quot;
        title=&quot;board&quot;
        src=&quot;/static/592c89592f9b7829a93b80cf7cefde03/5a190/board-1024x619.png&quot;
        srcset=&quot;/static/592c89592f9b7829a93b80cf7cefde03/772e8/board-1024x619.png 200w,
/static/592c89592f9b7829a93b80cf7cefde03/e17e5/board-1024x619.png 400w,
/static/592c89592f9b7829a93b80cf7cefde03/5a190/board-1024x619.png 800w,
/static/592c89592f9b7829a93b80cf7cefde03/2bef9/board-1024x619.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each column is a step in the value chain. The ready for dev column is a set of groomed items in priority order, most important at the top.&lt;/li&gt;
&lt;li&gt;The lead time for delivery is written clearly on the board beside the ready for dev column.&lt;/li&gt;
&lt;li&gt;Each column has a work in progress limit which represents the capacity of the resources working in that column, or the limits of the bottleneck of these resources (see below for details)&lt;/li&gt;
&lt;li&gt;The definition of ready/done for each column is written in a section at the bottom of the board. This makes it easy for team resources to check whether they have done all important tasks before the item can move.&lt;/li&gt;
&lt;li&gt;Different coloured cards are used for different types of item. e.g. Red for bugs, yellow for development tasks, making classes of item clearly visible.&lt;/li&gt;
&lt;li&gt;Some columns are Queue columns. I usually put a queue in front of system test column up to a limit. Testing is usually a bottleneck in the system due to the developer/tester ratio. By adding a queue developers can start on the next development task while testers are completing its current batch. However, once the ready for test queue reaches its WIP limit, developers may switch to Kaizen tasks.&lt;/li&gt;
&lt;li&gt;Paper tabs are used to denote who is working on a task (blue) and which tasks are blocked (red). Using tabs in this way is a great way to impose a physical limit that a certain name can only be assigned to a single task at a time. Red tabs are used to visualize items that are blocked and can be initialled to denote who raised the blocked issue.&lt;/li&gt;
&lt;li&gt;An expedite queue is included at the top and is for emergency items the team may be working on. The policy is usually that an expedite item freezes all other work on the board until it is complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Electronic vs Physical Boards&lt;/h1&gt;
&lt;p&gt;Some of the electronic work tracking tools available today, such as Atlassian Jira or Visual Studio Team Services are really feature rich and the Kanban boards they provide do a great job. Benefits include automated reporting of team metrics (e.g. for calculating lead times) and integration with other systems such as email. However, there are also limitations. For example, Kanban works best when work is always visible to the team. With electronic boards, this is not always the case. Not all teams have large screen displays in every meeting room and having to pull up the board on laptops or projectors before progress can be discussed can be a real time sink. Also, some of these electronic tools can be overcomplicated for smaller teams.&lt;/p&gt;
&lt;p&gt;On the other hand, the benefits of a physical board are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Work is visible always to everyone in the office.&lt;/li&gt;
&lt;li&gt;Mobile whiteboards can be easily carted around the office and in to meetings.&lt;/li&gt;
&lt;li&gt;The board imposes physical limits on the number of cards that can be added to the columns. This acts as a physical WIP limit for the team.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The downsides of a physical board are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The board is only visible locally to the team. This can make it hard to share information with a wider audience, say, international teams.&lt;/li&gt;
&lt;li&gt;No automated reporting of lead times is available and it must be done manually.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One strategy when considering a physical board is to run a backup electronic board for calculating statistics and reports, which the scrum master keeps in sync with the physical board.&lt;/p&gt;
&lt;h1&gt;Anatomy of a Kanban Card&lt;/h1&gt;
&lt;p&gt;For Kanban cards a standard index card can be used. Printing the index cards is a great way to make them legible and tidy. The card should make visible any pertinent process related info that helps discussing and helps it flow through the process.&lt;/p&gt;
&lt;p&gt;Here&apos;s a sample similar to a card layout I&apos;ve used before:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3klEQVQoz3WQbW/aMBSF+YnjA0OENiEltpPsr7GP61ZNW6CMUgqMkDiv0GqQrlMZLTSQEMiLJ0OnStMqHR1d23qudU7O/cLjU+a2xV9/ZX0Vbk0pMl6V1+RvFO6+I/y64KctmFv0q4suGw5PVgM+MQFx4GvKbOCeFSdK6YdS8uqlpwHIxY5MrkXiIjIW/8O4iLgvx0m9/NBhf19ys/ZRgMXcznpmUgtkNsxsmFggtZ6H2BBSC6Q2SPavZITouhEiY7gxpAMMyAjN2sfdWt76WOi9z5unBefT2/HnontW7Nby0zozUUpXtTfGh0JqHaIBCm9NMbHF1KEeaGCjQ18FEYahDjYYRgZcDYVQpze+KviqsDNRbKHEgiGWcgGWH/tgdsnPO9xDt7Ls8YktzzuVUBPISMwcRFwxc1Fqw/2MMgcd0vmaTOGlKnlN1mswE4XxGszWEIPhiddgfp6XvQbVtM7cXxzffTuaNphZm6WxHUDhtS77mnTX4m7PyxOF2eJqbEnEQbEp7IwXJSagzdHyDrXtf15jCntNdtHllj0usYTYljIHktFe7l8fowhXH6+4p35l3mEDlV/hd7kQS2tdWnwHkSEmthQZYoip/6OtIa41uOgLKxUsByDQ4FqX/wAxMNqDL5hGVAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;card&quot;
        title=&quot;card&quot;
        src=&quot;/static/9f989371bb32517cfc032fbaf7f6baff/5a190/card-1024x560.png&quot;
        srcset=&quot;/static/9f989371bb32517cfc032fbaf7f6baff/772e8/card-1024x560.png 200w,
/static/9f989371bb32517cfc032fbaf7f6baff/e17e5/card-1024x560.png 400w,
/static/9f989371bb32517cfc032fbaf7f6baff/5a190/card-1024x560.png 800w,
/static/9f989371bb32517cfc032fbaf7f6baff/2bef9/card-1024x560.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The number records the electronic boards number if one is being used as a backup.&lt;/li&gt;
&lt;li&gt;To generate metrics, key dates are recorded in the top right of the card. It&apos;s important to record at a minimum the card creation date, when the team started working on it, and when they completed it.&lt;/li&gt;
&lt;li&gt;A clear description of the item appears in the centre of the card.&lt;/li&gt;
&lt;li&gt;A definition of done is included in the bottom left of the card. This gives a quick visual indicator to the team of the &quot;done-ness&quot; of the item and when it can pulled downstream. If certain activities are not required they can be crossed out. Having a definition of done on the card is an alternative to adding a done column for each phase on the board and is a good space saving technique.&lt;/li&gt;
&lt;li&gt;Notes are maintained on the bottom right. For example, the team might note the due date for the item as requested by the stakeholder, or if collaboration with an external team is required before the item is &quot;done done&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This card layout is an example only. Kanban encourages that a team tailors the visual system (board and cards) to suit the process of the team. This is an evolutionary process, where the team tries something and keeps it if it improves the process, and discards it if it doesn’t. Changes are evaluated in the regularly held retrospective meetings.&lt;/p&gt;
&lt;h1&gt;WIP Limits and Flow&lt;/h1&gt;
&lt;p&gt;Batch size (or the number of items in a release) has a direct impact on lead time and quality. What this means is that the more items a team is working on at the same time, the longer it will take for items to make their way through the development process and be delivered to the business. At the same time the rate of defects will increase. This is due to the following factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The time for each type of activity (analysis, development, testing) that needs to be completed within the release increases, and this impacts the lead time for items in that release.&lt;/li&gt;
&lt;li&gt;The potential for complex dependencies between items increases if the scope of a release increases.&lt;/li&gt;
&lt;li&gt;An increase in complexity requires an increase in the amount of management and planning required for the release.&lt;/li&gt;
&lt;li&gt;Defect rates increase as the scope increases and the time between analysis, development and testing increases. This can simply be due to the fact that there is a greater scope to discuss, implement, test and more potential for things to get lost in the mix.&lt;/li&gt;
&lt;li&gt;The time required to sign off a release increases as there is more functionality for business users to consider.&lt;/li&gt;
&lt;li&gt;The risk associated with a release increases as more code has changed in a large release, also meaning that it’s harder to find issues when something goes wrong.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So keeping a small number of items per release and releasing more regularly promotes team agility by reducing lead time, reducing management overhead and reducing defect rates.&lt;/p&gt;
&lt;p&gt;Kanban maximizes flow through the development pipeline by keeping the number of items in a release at an optimum level, known as the Work In Progress limit (WIP). Small batch sizes are achieved by setting work in process limits for each phase of development within the development process. These WIP limits are usually set based on the capacity of resources within that development phase.&lt;/p&gt;
&lt;p&gt;This WIP limit ensures that bottlenecks within the system are exploited at 100% capacity. For example a development team may have 4 developers available. The WIP limit for the development phase of the pipeline may be a maximum of 4 items being worked on at any time (or 2 if pair programming is being used). Typically WIP limits are displayed on the Kanban board at the top of each column.&lt;/p&gt;
&lt;p&gt;There is a great Yow conference video by Don Reinertsen on the effect of batch size on throughput here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://yow.eventer.com/yow-2012-1012/the-practical-science-of-batch-size-by-don-reinertsen-1269&quot;&gt;https://yow.eventer.com/yow-2012-1012/the-practical-science-of-batch-size-by-don-reinertsen-1269&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Pull System &amp;#x26; Kaizen Culture&lt;/h1&gt;
&lt;p&gt;Kanban implements a pull system, which means that cards pulled from left to right on a board as resources become available. e.g. Developers will pull tasks from the &quot;Ready For Dev&quot; column when they have finished development on their current item. Testers will pull development tasks into the System Test column when they are out of items to test and there are development items ready to test.&lt;/p&gt;
&lt;p&gt;Since it is only the bottlenecks in the development process that are working at 100%, the pull system can create slack within the development process. For example, a developer cannot pull a card unless their current card has been pulled into system test by the testers. This means the developer must wait before starting new work.&lt;/p&gt;
&lt;p&gt;With Kanban this slack is considered to be a good thing and gives time to develop a self-improvement culture within the team, referred to as a &quot;Kaizen&quot; culture. Waiting resources focus on &quot;Kaizen&quot; tasks that are aimed at improving the development process or team resources in some way. This might be optimizing the builds, training, blogging etc, etc. Developing the Kaizen culture is the core of how Kanban enables team agility.&lt;/p&gt;
&lt;p&gt;In terms of managing the Kaizen tasks, by their nature they should be tasked that can paused easily if the bottleneck has cleared in the process. I&apos;ve seen Kaizen work best when the team maintains a much simpler &quot;To Do, Doing, Done&quot; style Kaizen board separate to the main delivery board.&lt;/p&gt;
&lt;p&gt;More on Kaizen can be found here: &lt;a href=&quot;http://en.wikipedia.org/wiki/Kaizen&quot;&gt;http://en.wikipedia.org/wiki/Kaizen&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Outcomes and challenges using Kanban&lt;/h1&gt;
&lt;p&gt;Using Kanban I have seen the following positive outcomes for a team:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Everyone on the team gets a clear understanding of the development process and the state of items in the team as all work is visible.&lt;/li&gt;
&lt;li&gt;Delivery becomes more efficient as Kaizen tasks are being completed to improve the development process (e.g. automating manual tasks)&lt;/li&gt;
&lt;li&gt;Team members work at a sustainable rate of development while delivering continuously.&lt;/li&gt;
&lt;li&gt;Meetings (scrums and retrospectives) become very focused on delivery and take the minimal amount of time necessary. Scrums become focused on flow of work and discussions are about coordination and blocked issues on the board.&lt;/li&gt;
&lt;li&gt;Team morale increases due to the team working effectively together.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kanban works well if items being requested are of a consistent size. If they are, then meeting SLA&apos;s for upstream requesters is easy. Care needs to be taken at the input queue (our Sandbox) to slice things up in a consistent way (e.g. a small number of acceptance criteria per story). This may not always be possible though, and Kanban requires that any variability in size is managed accordingly.&lt;/p&gt;
&lt;p&gt;Kanban also works on an SLA basis for delivery instead of upfront planning. The rationale for this is that if items being delivered are of a fairly consistent size, and a small batch size is used per release, then over time the delivery frequency of items averages out.&lt;/p&gt;
&lt;h1&gt;Team Metrics&lt;/h1&gt;
&lt;p&gt;The key to successfully using Kanban to improve flow is to monitor team throughput metrics. You can go as complex as you like with this, but at a minimum I like to track the following items:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Number of Items in Flight&lt;/li&gt;
&lt;li&gt;Number of Items &quot;Done Done&quot;&lt;/li&gt;
&lt;li&gt;Average Lead Time (From requested to done done) +/- Std Deviation&lt;/li&gt;
&lt;li&gt;Average Dev Time (From dev start to done done) +/- Std Deviation&lt;/li&gt;
&lt;li&gt;Max Lead Time&lt;/li&gt;
&lt;li&gt;Max Dev Time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A simple monthly report on these figures might look like the following, and is very simple to create in Excel. This kind of report can be used in retrospective and for high level planning with stakeholders.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB0UlEQVQoz21SbdOcIAz0///Kzun5ggoSEkhA4BmkvWmnt8MHdLLZ7Ibh9Xoty2y0OY5jfr/XdTXGLMs8vxuUUuM4TeO4bds8v6dpWtd1WZZpmtS+D8dxxBhD8MyMiABgryuEwMwhhOu6mFkkGnMRUUrpuqxDTCkZowetdSklxphSEhHvPSLmnGutzGytrQ/AQYyx1uqcY+ZaK6JryjlnEXn0AxE551JKtVbvfW9dSrHWdg4AeO97l0Ep1ZVF5L7vGKMDuO+71ioiH2VrrYg0QaLehYiG9/uNiMzNJIcAAOd5dvJn7FIKOCcipZSeUc75PM8BAHo80rSl5yTy+y4i6UEUKU8QPY5+GXoAKUXC0I+wxBR7hM3uU5pS+tA+aOTHpycX2Av7aDT20pzvmku+71LK8/kfGQnRoVnPNu3dtuUObS8SbgNXZrrahrvPL8o+sHF4anscWm37pk7YL2OQY2K89mV8vcbjOP84+JfMLKXWnBJtSk69z6telb4gsASOatfHcYjIF2UiCt6DWmCbyekQURh/TZOaxiwsuVrynui+7y/k0OABdiDtBVm888aRBthF+FP3nczMzjkkj0jOoQMHtv1wSNZC2/ADZu4v52/yD3MNY6ok43A7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;report&quot;
        title=&quot;report&quot;
        src=&quot;/static/b2b6fb1c6faa38ac3f934d437a76ee1f/5a190/report-1024x759.png&quot;
        srcset=&quot;/static/b2b6fb1c6faa38ac3f934d437a76ee1f/772e8/report-1024x759.png 200w,
/static/b2b6fb1c6faa38ac3f934d437a76ee1f/e17e5/report-1024x759.png 400w,
/static/b2b6fb1c6faa38ac3f934d437a76ee1f/5a190/report-1024x759.png 800w,
/static/b2b6fb1c6faa38ac3f934d437a76ee1f/2bef9/report-1024x759.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;Resources&lt;/h1&gt;
&lt;p&gt;The &quot;Kanban&quot; book by David J. Anderson is a really great resource on the topic, highly recommended if you are interested in using Kanban. You can &lt;a href=&quot;https://www.amazon.com/gp/product/B0057H2M70/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B0057H2M70&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=83b7d6124bf55c5ff8b63ba4aceb9aed&quot;&gt;get a copy on Amazon.com&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B0057H2M70&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B0057H2M70/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B0057H2M70&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=c1a986f8e79c5b4c83bd7305547f49c2&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B0057H2M70&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B0057H2M70&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Terraforming a Wordpress blog on AWS]]></title><description><![CDATA[Terraform by Hashicorp is an awesome tool that allows you to define your system architecture as code using a json language variant. With…]]></description><link>https://www.ginocoates.com/2017-01-01-terraforming-wordpress-on-aws/</link><guid isPermaLink="false">https://www.ginocoates.com/2017-01-01-terraforming-wordpress-on-aws/</guid><pubDate>Sun, 01 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAMEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHCmKrCP//EABgQAAIDAAAAAAAAAAAAAAAAAAABAxES/9oACAEBAAEFAkS4s//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABYRAAMAAAAAAAAAAAAAAAAAABARIf/aAAgBAgEBPwFwf//EABcQAQADAAAAAAAAAAAAAAAAAAEAECL/2gAIAQEABj8CmBK//8QAGBAAAwEBAAAAAAAAAAAAAAAAAAERITH/2gAIAQEAAT8hTdRRJk1HD//aAAwDAQACAAMAAAAQf8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAWEQEBAQAAAAAAAAAAAAAAAAARARD/2gAIAQIBAT8QgBn/xAAYEAEBAQEBAAAAAAAAAAAAAAABEQBBIf/aAAgBAQABPxBwVfemrHAoqXsuRUSO/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;terraform&quot;
        title=&quot;terraform&quot;
        src=&quot;/static/7ded78829672418213220a53760bd12e/4b190/terraform.jpg&quot;
        srcset=&quot;/static/7ded78829672418213220a53760bd12e/e07e9/terraform.jpg 200w,
/static/7ded78829672418213220a53760bd12e/066f9/terraform.jpg 400w,
/static/7ded78829672418213220a53760bd12e/4b190/terraform.jpg 800w,
/static/7ded78829672418213220a53760bd12e/82d6d/terraform.jpg 962w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Terraform by Hashicorp is an awesome tool that allows you to define your system architecture as code using a json language variant.&lt;/p&gt;
&lt;p&gt;With Terraform you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define your architecture as code in simple text files rather than manually tweaking things in your cloud provider&lt;/li&gt;
&lt;li&gt;Store your architecture definition in a source control repo and have a complete revision history over changes made&lt;/li&gt;
&lt;li&gt;Deploy resources to multiple cloud providers&lt;/li&gt;
&lt;li&gt;Preview changes to your architecture before deploying&lt;/li&gt;
&lt;li&gt;Work in an iterative way, deploying and tearing down complex cloud architectures in minutes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this blog post we&apos;ll use terraform to define a WordPress website and deploy it to AWS. We are going to create:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A standalone EC2 instance with Bitnami WordPress Multisite installed&lt;/li&gt;
&lt;li&gt;A security group to restrict access to the server and allow SSH from our local machine&lt;/li&gt;
&lt;li&gt;An elastic IP so that DNS mappings work if the server is rebooted&lt;/li&gt;
&lt;li&gt;A root device that won&apos;t get deleted with the instance, so that if the server dies, we still have the website data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&apos;ll be using Atom to edit terraform files and doing all of this on windows. I usually use Linux these days, but I&apos;m using a surface tablet to write this post so I&apos;ll use git bash prompt for the examples.&lt;/p&gt;
&lt;p&gt;This post assumes that you already have already signed up for an AWS account, but will cover configuring your local system for use with Terraform.&lt;/p&gt;
&lt;h1&gt;AWS SDK Install&lt;/h1&gt;
&lt;p&gt;Install the AWS SDK for your local system. For windows that can be installed from &lt;a href=&quot;http://docs.aws.amazon.com/cli/latest/userguide/installing.html#install-msi-on-windows&quot;&gt;http://docs.aws.amazon.com/cli/latest/userguide/installing.html#install-msi-on-windows&lt;/a&gt;. Once its installed open a prompt and check that the aws cli is accessible by typing aws --version.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAkklEQVQoz+3SQQ7CIBAF0D+gFgZE9OBcoQS10cZFQQ/EUUysJurGGre+5SQ//y8G2y51x2532Pen/nxTSsk5l4fxcnmScx6GodaKxjd6rXnDNMNERAQghAD2zCtjvFl6a41RSllrnXP2zjCz1jxmRlJKAG3bghRhDiwAObVZCAEgpfQy523YV+Gfmv/hT08SY7wCpdY/EBQ8oPEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-1&quot;
        title=&quot;tf-wordpress-1&quot;
        src=&quot;/static/2eac35061b79875d76633630850ad7f8/5a190/tf-wordpress-1-1024x543.png&quot;
        srcset=&quot;/static/2eac35061b79875d76633630850ad7f8/772e8/tf-wordpress-1-1024x543.png 200w,
/static/2eac35061b79875d76633630850ad7f8/e17e5/tf-wordpress-1-1024x543.png 400w,
/static/2eac35061b79875d76633630850ad7f8/5a190/tf-wordpress-1-1024x543.png 800w,
/static/2eac35061b79875d76633630850ad7f8/2bef9/tf-wordpress-1-1024x543.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now execute &quot;aws configure&quot; to configure your local environment to access aws. You will be promoted for the access key, secret, region and output format. You can generate a user account in AWS IAM and generate the access key and secret. The user should have adequate permissions to create EC2 resources. For this article I created a user with full EC2 access.&lt;/p&gt;
&lt;p&gt;I usually use text output which is easier for bash scripting.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAwUlEQVQoz9WQyQ7BUBiFz+2gKkXbexUJNyqlC+GNuu/wQhbGtIIOT8JDiSESsUB3vvy7ky//ycFuNV9sl5t4s47Xh+O+KIr8RpZlaZrmeV68ck+TJDmdz5CalaqpqIYq1AR8hyRJADzPw2wyHQ9H7sh1bMce2Jxz3ueMMUppp9Otaxp5Q5ZlAEEQwOpZGtVYl+ltnbZMwzAoNUVR/Pg5iiIoDYUoBCIgA8IPtcMwBAF5HvAo9q1cgr+Wr2uXln3fvwAAQzrW12LZ8AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-2&quot;
        title=&quot;tf-wordpress-2&quot;
        src=&quot;/static/fdb01a103baebcf43488548e521eedc8/5a190/tf-wordpress-2-1024x575.png&quot;
        srcset=&quot;/static/fdb01a103baebcf43488548e521eedc8/772e8/tf-wordpress-2-1024x575.png 200w,
/static/fdb01a103baebcf43488548e521eedc8/e17e5/tf-wordpress-2-1024x575.png 400w,
/static/fdb01a103baebcf43488548e521eedc8/5a190/tf-wordpress-2-1024x575.png 800w,
/static/fdb01a103baebcf43488548e521eedc8/2bef9/tf-wordpress-2-1024x575.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To check configuration was successful, ask aws to kindly list running ec2 instances in your default region. If this is a brand new account, or you have no instances in that region, you should see output similar to the following...&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAp0lEQVQoz+WQSw6CQBBEa3q6w1cicEPWI9zKFfiJglFmOIXcxwBLNoStL5VObV4qaXT1ubnV98f1cm9e725w1treOetsP5XBDqtY+2nb5zh+wYlQRDpkOmjytCatmZVSeoaIsIKZARRFAd/302Oa53mWZnEUB0GwaCLCzESkVogIAGMMJBFEgA+EUIwtLMtVVcELPWhAATTfzXJZltjB/8rTt3fLJ2N+zcg7AKegPSQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-3&quot;
        title=&quot;tf-wordpress-3&quot;
        src=&quot;/static/2c4bacda1ce10b38f33da9014d27e31e/5a190/tf-wordpress-3-1024x585.png&quot;
        srcset=&quot;/static/2c4bacda1ce10b38f33da9014d27e31e/772e8/tf-wordpress-3-1024x585.png 200w,
/static/2c4bacda1ce10b38f33da9014d27e31e/e17e5/tf-wordpress-3-1024x585.png 400w,
/static/2c4bacda1ce10b38f33da9014d27e31e/5a190/tf-wordpress-3-1024x585.png 800w,
/static/2c4bacda1ce10b38f33da9014d27e31e/2bef9/tf-wordpress-3-1024x585.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Okay, now AWS is good to go. Lets install terraform...&lt;/p&gt;
&lt;h1&gt;Install terraform&lt;/h1&gt;
&lt;p&gt;We are going to install to a directory on the C drive. So let&apos;s create a directory…&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkdir c:\terraform&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Download terraform from &lt;a href=&quot;https://www.terraform.io/downloads.html&quot;&gt;https://www.terraform.io/downloads.html&lt;/a&gt; and extract terraform.exe into c:\terraform&lt;/p&gt;
&lt;p&gt;To make it easy to use terraform from any prompt. Let&apos;s add c:\terraform to your path. Go to System &gt; Advanced System Settings &gt; Environment Variables and add c:\terraform; to the start of your path.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAABe0lEQVQoz1WS247cIAxA8/9/1vahqlbqQ2c7m52BhABJuNs4Aaphur0cWTxgHSxsDz7EBJmIQggAkHMGAETMOZdSaq2lU/+ntUcM1/G22xBTYowLIZSUQggppe2EELCTngBkBGnTq4bbDkPO+TiOZzp/4Jzbt32auPd+341zDjoxpRCC8yHg6eAYWoeIvPcAQESttRjjum5a67OcYlnGt1FKaYwJIcQYEbFLdegfaM9qzwQihhCstTlTrTWl5Jyz1gFgKRUxJ4CHWrtcayWiZ7eICBGNMV2wxhjvvXduU8uqhJazWibCWEo5jvO3nHM+z7NfHdYYzrmYZ8aYMWZb9TjpL9f9682/TPDtnl44bC4aa/9WVkrpDueT1noWYlkW74Oz9n3Sn37Izxf9fcbXtV50uXFxv9+Hj7k9muScQzqBCpW2G6ekijFKqThjbPx5u17UzFqh1uo88evbOPyZ+7/L8HgrhGVZlJTrqpVet31/HMYCIB2nlPLO2C8NTzoyHsRvAAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-4&quot;
        title=&quot;tf-wordpress-4&quot;
        src=&quot;/static/ace2405feb74ae0af521bbd003b399b2/5a190/tf-wordpress-4-1-1024x531.png&quot;
        srcset=&quot;/static/ace2405feb74ae0af521bbd003b399b2/772e8/tf-wordpress-4-1-1024x531.png 200w,
/static/ace2405feb74ae0af521bbd003b399b2/e17e5/tf-wordpress-4-1-1024x531.png 400w,
/static/ace2405feb74ae0af521bbd003b399b2/5a190/tf-wordpress-4-1-1024x531.png 800w,
/static/ace2405feb74ae0af521bbd003b399b2/2bef9/tf-wordpress-4-1-1024x531.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Verify the installation by typing terraform --version at the prompt.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAh0lEQVQoz2OYPb1n8qzJ0+dMnT532sJF8xcvXogfLQKTs2bNPHx0HwOnAAe3IDczBwsLJwsjIyMDEYCFhYWBgSEhMZqBV5CLm5+bg4eDjYuNmZmJGMDGxsrAwJBfkM3Ayc/GwMoAQmwMDERZDLU5Lz+LgZmJOB1YNTMxM4wozaDQJltzVlYqAF/RMwVsgpzZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-5&quot;
        title=&quot;tf-wordpress-5&quot;
        src=&quot;/static/158e2b8b2f1d97549b631cd563020e78/5a190/tf-wordpress-5-1024x529.png&quot;
        srcset=&quot;/static/158e2b8b2f1d97549b631cd563020e78/772e8/tf-wordpress-5-1024x529.png 200w,
/static/158e2b8b2f1d97549b631cd563020e78/e17e5/tf-wordpress-5-1024x529.png 400w,
/static/158e2b8b2f1d97549b631cd563020e78/5a190/tf-wordpress-5-1024x529.png 800w,
/static/158e2b8b2f1d97549b631cd563020e78/2bef9/tf-wordpress-5-1024x529.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;Atom&lt;/h1&gt;
&lt;p&gt;Install Atom from &lt;a href=&quot;https://atom.io&quot;&gt;https://atom.io&lt;/a&gt;. You may also want to install the terraform language which adds syntax highlighting for terraform files. &lt;a href=&quot;https://atom.io/packages/language-terraform&quot;&gt;https://atom.io/packages/language-terraform&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apm install language-terraform&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Also, to support reformatting terraform files on save we can use the terraform executable and a plugin called terraform-fmt. First install it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apm install terraform-fmt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then configure it by editing the atom config file and add the following lines (File &gt; Config):&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 687px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbElEQVQI143NOxKDMAxFUXYjfwTYsYRsZBkosv8tpUhFQzJzitu8eVNYBCKtXHl0OTp1lcswi0fyMzl8MmFqgBSQLrb3NjjvRbWetg1L0iA+juNaHZJH0tduRXvRlKVJCzMD/noOi3wL7twfPuY1LP4shZYMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-6&quot;
        title=&quot;tf-wordpress-6&quot;
        src=&quot;/static/f5492253ad16b47ad22f1baa7f445bbe/aec65/tf-wordpress-6.png&quot;
        srcset=&quot;/static/f5492253ad16b47ad22f1baa7f445bbe/772e8/tf-wordpress-6.png 200w,
/static/f5492253ad16b47ad22f1baa7f445bbe/e17e5/tf-wordpress-6.png 400w,
/static/f5492253ad16b47ad22f1baa7f445bbe/aec65/tf-wordpress-6.png 687w&quot;
        sizes=&quot;(max-width: 687px) 100vw, 687px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we are ready to terraform our architecture. At the prompt change to your source code directory and open atom (usually just type atom .)&lt;/p&gt;
&lt;h1&gt;Lets Terraform&lt;/h1&gt;
&lt;p&gt;Add the following files to your terraform project.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;config.tf - This is where we will configure our aws provider&lt;/li&gt;
&lt;li&gt;main.tf - This is the file where we will define the main resources used in the architecture, including the EC2 instances, elastic IP&apos;s etc.&lt;/li&gt;
&lt;li&gt;security.tf - This is where we will define security groups to allow access to our server over http and to restrict SSH access to our local IP&lt;/li&gt;
&lt;li&gt;outputs.tf - This is where we will define the outputs of our terraform architecture once its complete. For example this is handy if you output the public DNS of any server created.&lt;/li&gt;
&lt;li&gt;variables.tf - Here we list any variables used in our architecture definition. For example, this can be used to provide region specific ami ids.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note, I&apos;m using multiple files for code organization. It&apos;s perfectly fine to define everything in a single terraform file. Terraform is smart enough to process all terraform files it finds in the current directory and consider them as part of your solution when planning or deploying the architecture.&lt;/p&gt;
&lt;p&gt;Let&apos;s first setup our variables...&lt;/p&gt;
&lt;h1&gt;Variables.tf&lt;/h1&gt;
&lt;p&gt;Here we define the variables that are in use in our architecture. This will cause Terraform to expect these variables when it is executed, either to be passed at the command line with the -var option, or in a .tfvars file using the -var-file option.&lt;/p&gt;
&lt;p&gt;We will define the following variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;local_ip - Later on we will create a security group to allow SSH access to the server. We will define a variable for the local ip address so that it should be provided when running terraform at the command line&lt;/li&gt;
&lt;li&gt;region - This variable will allow us to pass the region as a variable. This allows us to conveniently pass it in the tfvars file.&lt;/li&gt;
&lt;li&gt;wordpress-images - This is a map or lookup variable allowing us to use specific images for a certain region. We will only be deploying to N.Virginia in this article, but I&apos;ll include this kind of variable for illustration purposes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The image id for bitnami wordpress multi-site can be found here: &lt;a href=&quot;https://aws.amazon.com/marketplace/fulfillment?productId=2f1d4d67-324b-41d7-8af9-b7860d269c6d&quot;&gt;https://aws.amazon.com/marketplace/fulfillment?productId=2f1d4d67-324b-41d7-8af9-b7860d269c6d&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To launch in Virginia well be using the image ami-a42d75b3. You must subscribe to this ami in the amazon market place before using it. To do this, go to the Bitnami WP multisite page in the marketplace, select the Manual Launch tab and then hit the &quot;Accept Terms&quot; button on the right of the page.&lt;/p&gt;
&lt;p&gt;Here is our completed variables file...&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 94%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAIAAAAf7rriAAAACXBIWXMAAAsTAAALEwEAmpwYAAABkklEQVQ4y5WT3Y7aMBCFeZVWahLb43E8/hsnToCwy8X2/d+nNYGCYAVUOhdW5C8+53i8kX1pFUnwPm7jsEiMHfhWuZ8N/PilnmsjMAEVDDvjJ4ozuhF6hp6FDhLjKoFB6NAqutOmU96kxZUv4jnvFhuL0KEDL3S4Uwf+Thupo8Iote/ACXQduFbdbXKtco9khUU1lqRhdZHEBH1WPUuTVFVNodb1ZXGGO/AmHWj6It6V/RHdIODq89H8bYSNxKht1nZQfTSBBfpGVp+3+tbzCqfrL+FUr0lyzVIV/5n8Fo6GDzT9dnmf50/P+zwfXdqe8r+COwjQM9KoHYONjaRWUlNVm39SdYWhz2A5TQdKE/QJbLJpIp7QDw939gAjjdIkTWwCg03oBt5+DsvR5fnJmdfCJMZG2JNVapVrBFXJF+RlwqhAXNCNfSjnerR/SZ4mTEf0s+UPyoW4vMPcwkFioDQj5fVTfUMY3jm8ZlYmuWF2YzEh17Y5Wx6kCZ16D15Hct3dKv/ykq6v6tyQ9v8V+C/8BxfE0Yn/0Y/CAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-7&quot;
        title=&quot;tf-wordpress-7&quot;
        src=&quot;/static/23b102bfc4abe32812fdf2f6f0eef2f5/5a190/tf-wordpress-7.png&quot;
        srcset=&quot;/static/23b102bfc4abe32812fdf2f6f0eef2f5/772e8/tf-wordpress-7.png 200w,
/static/23b102bfc4abe32812fdf2f6f0eef2f5/e17e5/tf-wordpress-7.png 400w,
/static/23b102bfc4abe32812fdf2f6f0eef2f5/5a190/tf-wordpress-7.png 800w,
/static/23b102bfc4abe32812fdf2f6f0eef2f5/58bb7/tf-wordpress-7.png 985w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I will use a local tfvars file to provide these values so that I don&apos;t have to pass them at the command line, or check them into source control. The format of this file is shown below….&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAfklEQVQY043OQQ6DIBBAUY7iwjh0hoIwM4CtYiz3v5Npurcmb/uTbwAjICdZdTlSbtaJJbFOhvExjHjNzLrJq0v9lL0vR5dt17Z7fU/E4OSa8bzG3ChWx8VrfXJ1qWAo6P8zs2xRG805aA15Qa8T8nf+BgPEEyYgAWK4nf3iE4IiNpK8dYcdAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-8&quot;
        title=&quot;tf-wordpress-8&quot;
        src=&quot;/static/bb8010eea06d51c416ac9bddba4ef98e/5a190/tf-wordpress-8.png&quot;
        srcset=&quot;/static/bb8010eea06d51c416ac9bddba4ef98e/772e8/tf-wordpress-8.png 200w,
/static/bb8010eea06d51c416ac9bddba4ef98e/e17e5/tf-wordpress-8.png 400w,
/static/bb8010eea06d51c416ac9bddba4ef98e/5a190/tf-wordpress-8.png 800w,
/static/bb8010eea06d51c416ac9bddba4ef98e/91b29/tf-wordpress-8.png 983w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;Config.tf&lt;/h1&gt;
&lt;p&gt;Here we can configure some defaults for our provider. Here we could put our access key and secret, however, it&apos;s better to let terraform use the locally configured aws profile and prompt for the region during deployment. For now we will tell terraform to use the default aws profile configured on the local system and deploy to the region specified in the region parameter. Note the user of the interpolation ${} syntax here to reference the variable we defined earlier.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhUlEQVQY043LSw7CIBAAUG6iiYYCM3yHESi2knajC+9/HT2BNXnbJxQEaQJaptvqqU+QjGMM5XTR56v5TSAP31+x7vPjGcvQvkBoGPuE+ZDA1OftTW2nem/rmuqCvmnLxpVDAkPNdWBszjGHQmUQL9pmhceEtixNUkATZAlZmiSB/pnf/AF5FDbG9ERQ9gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-9&quot;
        title=&quot;tf-wordpress-9&quot;
        src=&quot;/static/34404928cb64e409ef7b1352ae478be2/5a190/tf-wordpress-9.png&quot;
        srcset=&quot;/static/34404928cb64e409ef7b1352ae478be2/772e8/tf-wordpress-9.png 200w,
/static/34404928cb64e409ef7b1352ae478be2/e17e5/tf-wordpress-9.png 400w,
/static/34404928cb64e409ef7b1352ae478be2/5a190/tf-wordpress-9.png 800w,
/static/34404928cb64e409ef7b1352ae478be2/adc48/tf-wordpress-9.png 979w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Okay, lets first make sure we&apos;ve considered security...&lt;/p&gt;
&lt;h1&gt;Security.tf&lt;/h1&gt;
&lt;p&gt;Add a security group resource to the security.tf file and give it an id, name and description. Our security group will contain a number of rules, so we will &quot;outline&quot; rather than &quot;inline&quot; its rules.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAnElEQVQI113KWwqDMBQE0KxF2yYab2K4eWgemmjVv+5/OQVLqRQOwwwM4WpmoBuw2mYXn6A8A8OluzFZPaC6Q01FzcSZsqJwRRowwhadX70rxq/DvIXl8GU3sYTlGPOGY8GhmLBKnRrhrgjrEJQf045DlhjRe5eTmYKeoknRzhGDl3ZQbuS9oVyz7oewTrfCUY6fTTnS9vQt1/efN1TuLnFDS3Y/AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-10&quot;
        title=&quot;tf-wordpress-10&quot;
        src=&quot;/static/c78a747e29d6dc9b33bbcd144baf96c4/5a190/tf-wordpress-10.png&quot;
        srcset=&quot;/static/c78a747e29d6dc9b33bbcd144baf96c4/772e8/tf-wordpress-10.png 200w,
/static/c78a747e29d6dc9b33bbcd144baf96c4/e17e5/tf-wordpress-10.png 400w,
/static/c78a747e29d6dc9b33bbcd144baf96c4/5a190/tf-wordpress-10.png 800w,
/static/c78a747e29d6dc9b33bbcd144baf96c4/7a3d6/tf-wordpress-10.png 990w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we will add rules to the security group. We will use the Terraform interpolation syntax to specify the security group the rule belongs to e.g. ${aws_security_group.wordpress_security_group.id}&lt;/p&gt;
&lt;p&gt;We&apos;ll allow ingress on http port 80 and 443 to allow web traffic to hit the server. (I won&apos;t set up SSL just yet, but it&apos;s good to have the port open for when I do)&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcklEQVQoz41R7XKcMAzkZVLA2Ei2JX8bOC4cSfv+75PhyKXtTNpkZn9IGq21u26knSTV0WSwBegAcgUq90lGqkhVmdQr1w72A52kTlID/mrLzvk5zXuom4tLzpsvN5fWsrymac/zi8/PYIvmSXM1btaU+4PMDdocyxWpSB2FcmBDWieuVZkowPWKe8UD+AG8AC8xDBgEuMdlm0PdOFx6xe1A4DLVAuyV8R/y/hT8l2ykUtdfFC+tMJ1yMeRR81P/e+M/aJSOSFWM7r7NoL1Q3H5FeycLxd1gu7u3U/mjPoydQzE6MbpPyJJmxRfkybhJ33HkyRNytX45Wj8jVQnhE7IOVz//5Limafdlc6Eu656WncKSl5dQt1hvLl/PVygsFC6GSy8Pjw2Y5OIKNh+ahQFKbikmRWX8D2FO/Cu8Brnmy2somzhMsvWFSpbaD+i/kbZJcbohlXYwnWTvEiI/Cdt956skBomhV3z2Epx41F+S3wAC2ZfnHV1R3AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-11&quot;
        title=&quot;tf-wordpress-11&quot;
        src=&quot;/static/030c7c2c9db6a948e1a42adedaa21c9c/5a190/tf-wordpress-11.png&quot;
        srcset=&quot;/static/030c7c2c9db6a948e1a42adedaa21c9c/772e8/tf-wordpress-11.png 200w,
/static/030c7c2c9db6a948e1a42adedaa21c9c/e17e5/tf-wordpress-11.png 400w,
/static/030c7c2c9db6a948e1a42adedaa21c9c/5a190/tf-wordpress-11.png 800w,
/static/030c7c2c9db6a948e1a42adedaa21c9c/f79fa/tf-wordpress-11.png 940w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We&apos;ll also allow ingress reply traffic from the internet. This will allow replies on ephemeral ports to requests made by the server out to the internet.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5UlEQVQY04WOW26EMAxFWc0ASUhCHnbsAAnQmVbd/4KqlIq/ttL5OLJ073UnDUqTvkFl0+2XiCbNhQahUVm6LqOGQYVugsPyu6MD8wn5jGlHPiOfIdXIBy5vwCcuT+1YmqQsKZvUTNJgCxufuXzMcQ1UHRTreS01ny8Hm8cSaPdYIh0OioNthi2k6lM1jnrpO+2YtpdPVRp8iHmaKdbVL2yBBhkewt300vfS396WteN1/0zLU2joVbCe05KFgVHHUcWhEX6jU5Zye3trxSo4T3vmUYWr+29aWBoUGsapITRI8+P/8gWPuFKKIlLAhQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-12&quot;
        title=&quot;tf-wordpress-12&quot;
        src=&quot;/static/3235fa78c6bf0c5316086948d1e62445/5a190/tf-wordpress-12.png&quot;
        srcset=&quot;/static/3235fa78c6bf0c5316086948d1e62445/772e8/tf-wordpress-12.png 200w,
/static/3235fa78c6bf0c5316086948d1e62445/e17e5/tf-wordpress-12.png 400w,
/static/3235fa78c6bf0c5316086948d1e62445/5a190/tf-wordpress-12.png 800w,
/static/3235fa78c6bf0c5316086948d1e62445/f6f78/tf-wordpress-12.png 932w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We&apos;ll allow egress traffic from the server to the internet. This will allow the server to connect to the internet on all ports.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2UlEQVQY042M226EMAxE+ZqFLLkQO1c7JYEC2/3/H6qgfajUF6SRNTPymW60rCwpmw0WjazgikBXST9e2ix0kFOSUxI6jCYJ5QfpOihfjjaaj1R2nxcuWyp75M9AK7d3Klv+OCBUDayBDBaDbICeOpww1TeEhmm1sdkwc2207JAaxN8S4oJpgbhcZnV5hdA08HPKXZ5fgTcN/BhRI4dWkQlLkTY9Rhyk76XrR3fePxqUFyp0VF+xbEL5fnQGkg1JKDfIW+ryfGCsQp3zFkmZc+UuLKS/+fof/gbahFL6NJfsLgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-13&quot;
        title=&quot;tf-wordpress-13&quot;
        src=&quot;/static/f9cfb4b34671294ce62e97e746fbb6df/5a190/tf-wordpress-13.png&quot;
        srcset=&quot;/static/f9cfb4b34671294ce62e97e746fbb6df/772e8/tf-wordpress-13.png 200w,
/static/f9cfb4b34671294ce62e97e746fbb6df/e17e5/tf-wordpress-13.png 400w,
/static/f9cfb4b34671294ce62e97e746fbb6df/5a190/tf-wordpress-13.png 800w,
/static/f9cfb4b34671294ce62e97e746fbb6df/23266/tf-wordpress-13.png 923w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We&apos;ll also allow SSH access from our local IP to the server. Note that we are using the local_ip variable here to define which IP has access.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA20lEQVQY042OXY6DMAyEOQ0/JcFJ7DjESYACbff+J1oFHvdhK42sseRvxo2mVfsNsBi65BfwC2ABKoAZqFi/TJh75buROkV1jtRrP2hutN9c+mDc5/LidHB8Sj44nRSfLIeUD8vuZQdMkxPABJSrN6FX1ExOZHkbzJYXZWZwsWyrbLvl+giGu78Ct4wvgEnBBWsrc3lzOpWZ28EpF2PM1gTji609YTShfbhuxFuXr+sFO0nrT8jnoH03osGIIXaK+i/UaBtZDkO5r8EEdgbD7RX8P/yYuPvu9C/8Cxk0UjZzzMRNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-14&quot;
        title=&quot;tf-wordpress-14&quot;
        src=&quot;/static/b9dc8b798181c62d929e8d9c0d85843e/5a190/tf-wordpress-14.png&quot;
        srcset=&quot;/static/b9dc8b798181c62d929e8d9c0d85843e/772e8/tf-wordpress-14.png 200w,
/static/b9dc8b798181c62d929e8d9c0d85843e/e17e5/tf-wordpress-14.png 400w,
/static/b9dc8b798181c62d929e8d9c0d85843e/5a190/tf-wordpress-14.png 800w,
/static/b9dc8b798181c62d929e8d9c0d85843e/c4b7c/tf-wordpress-14.png 903w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The cool thing about terraform is that now we can go ahead and create those security groups in AWS, i.e. work in an iterative fashion. In a prompt, change to the source code directory and execute terraform plan.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAoElEQVQoz+WNQQ6CMBBFf2emBAroDQkJe7iQK7BQoiaF1nPIkQxxbyJbX17+7uXjNlz6sbfO2tna8eomO8+jc3Yah/tjjnEJwYfgY9x3DT4+l3X1zrnXtkGftZyEDUkuIpylXBRSlmyMThLKUjIZFTkbw8aQ1gRARADUdQ0trECkdtUOfZFZACWiAVRVBWaFH/k8N01zPO667njctu0/xm/4tzXza2/prgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-14-5&quot;
        title=&quot;tf-wordpress-14-5&quot;
        src=&quot;/static/6984b1ac5228541bca4e2d43e9c51b88/5a190/tf-wordpress-14.5-1-1024x525.png&quot;
        srcset=&quot;/static/6984b1ac5228541bca4e2d43e9c51b88/772e8/tf-wordpress-14.5-1-1024x525.png 200w,
/static/6984b1ac5228541bca4e2d43e9c51b88/e17e5/tf-wordpress-14.5-1-1024x525.png 400w,
/static/6984b1ac5228541bca4e2d43e9c51b88/5a190/tf-wordpress-14.5-1-1024x525.png 800w,
/static/6984b1ac5228541bca4e2d43e9c51b88/2bef9/tf-wordpress-14.5-1-1024x525.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Note the out parameter is used to save the plan file. This is important because you want to apply exactly what was listed in the plan phase. If you don&apos;t save your plan, when you apply your changes the underlying architecture may have changed. Be careful however, as the saved plan has some security issues and may store some sensitive information so don&apos;t check that into source control.&lt;/p&gt;
&lt;p&gt;Now we can apply the plan to create our security groups. Execute &quot;terraform apply terraform.plan&quot; at the prompt. You should eventually get output similar to the following.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABSklEQVQoz5WOy3LqMBBEx7Zk662Ro7eFwXBvPo0qVqwo+PwUoZLKJoucmmWf7oH6CSIG7x06a22MMcVUSqm15pStxZyzEKLrur7vCSGUUgB4PB5QSonPeGqtpRi993WpKaUQgrHGOZdzjjEqpbx/y/kZJoQAwPV6haUuNdccc6sthRRD2LVdTk/Bv/mSS0mlLTujDSWUMUYIGYYBAM7nMwjLJ80mMwkjmGGjpFxxjpxySiWd9DSKUaLsWQ9fvJYvlwvYaPWsddAKpXTSBjTOaK/Ro/EGo0WPetY22HGiP+Xb7Qbv//4f9odtvx2349rW7bCdjqfT8ZRT3rW1La0tbV33JRW0+Hr4Jd/vd7DWWLTKKDc7NztEO88zFwx+oet+yCIIOtNBDb3pR0mUGowhUgDnnZQd550Qz5OyEwIYA6WA8y/5VfMnvpc/AD3PJMDfP32/AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-15&quot;
        title=&quot;tf-wordpress-15&quot;
        src=&quot;/static/62c57d8c3ce9bbba2100d9ae63acdbcd/5a190/tf-wordpress-15-1024x466.png&quot;
        srcset=&quot;/static/62c57d8c3ce9bbba2100d9ae63acdbcd/772e8/tf-wordpress-15-1024x466.png 200w,
/static/62c57d8c3ce9bbba2100d9ae63acdbcd/e17e5/tf-wordpress-15-1024x466.png 400w,
/static/62c57d8c3ce9bbba2100d9ae63acdbcd/5a190/tf-wordpress-15-1024x466.png 800w,
/static/62c57d8c3ce9bbba2100d9ae63acdbcd/2bef9/tf-wordpress-15-1024x466.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s go to the AWS console and verify that terraform is awesome. In the EC2 console for your selected region, verify that the SG is created and has the rules defined.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABO0lEQVQoz43Q25KDIAwGYN//tfoI3c6eelhrlZYKaAgGJcCOuu30cr/hImH4M0ChVau1Nlp3RhujAXprAdE6RAvgENFatHauHTqHqm2voqmqsxBNcbzcGqn2p+rj+yRVfxH33cf+8HOpr+3nodyfKqn6sr69vX8dyvpY1lUjxd0IqYXUBceUcyY/oaOUc2AeyE8hBk5u8GBdTHmcAlgEiwONgRPHzDEHTgVA33ddZ9Q0jmGamEPOaZWXlVJMKeY/aaljCFN7vxe3m+w77UD3yxAiyv+QUmLmom1b55wfx4FmuBge7OLZ+sVaE9EchhdCCGPMmgEAKaVSylqLiF3X7Xa78/mMiACAiAUieu/nF+YcY/TeP+8WYySiEMLaEtFms9lut88DxTAM0zjGBTN77+MDM6/h1535xx5+AdHJA//PsSvrAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-16&quot;
        title=&quot;tf-wordpress-16&quot;
        src=&quot;/static/cec069aba14367fb642def2f366ed0ee/5a190/tf-wordpress-16-1024x451.png&quot;
        srcset=&quot;/static/cec069aba14367fb642def2f366ed0ee/772e8/tf-wordpress-16-1024x451.png 200w,
/static/cec069aba14367fb642def2f366ed0ee/e17e5/tf-wordpress-16-1024x451.png 400w,
/static/cec069aba14367fb642def2f366ed0ee/5a190/tf-wordpress-16-1024x451.png 800w,
/static/cec069aba14367fb642def2f366ed0ee/2bef9/tf-wordpress-16-1024x451.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Confirmed, it&apos;s awesome. Now let&apos;s create our wordpress instance.&lt;/p&gt;
&lt;h1&gt;Main.tf&lt;/h1&gt;
&lt;p&gt;We&apos;ll create a t2 micro instance that’s free tier eligible, with a 40GB harddrive. This means it should cost about $1 per month on the free tier (&lt;a href=&quot;https://calculator.s3.amazonaws.com/index.html&quot;&gt;https://calculator.s3.amazonaws.com/index.html&lt;/a&gt;). We&apos;ll also assign our security group we created earlier to the server. We&apos;ll also assign a public IP and configure the root_block_device to not delete the volume when the instance is deleted.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 114.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAIAAACEf/j0AAAACXBIWXMAAAsTAAALEwEAmpwYAAACUElEQVQ4y4VUCW7bMBDUV4IiEcX7Wu6StE67KVCg//9OQcmOHbROgIFAcTV7zqrTcVQWlSPrK5bN4yj3V+2ISffG7RfoBp25Aq5AanQwaV+YjFxjL8LLq3x5lT96vUPdDnd0TAYZJ3f6bdISaYF6oXoOOENesV48ztJS866RaxwUPKJjMgqbQ7loS4EWM74jjBFnGn9CXvP0y6fZ6dT80qJM6mVkN3S7j3h4Yi1C4hqZhJ6HXgQmm4k9ENhncuQ6xbxC2aBsPk02FV+KBty/CDf8j8z2sEdAbnA/gLAoDA4q3UzpWeRmDrQEXCCvkVYTTsIQ38mNaVBYYjL24lrIndy8KvA+l7LR+I71DGWLefU4Qd4CLh7ngHMq50BLyqsNpX+I3Ny3rohw3Lbm7TcfYO2Z/m1edxTmYHJp8jDaOGpfjQKVJoWTt+jSZFzxBltpaeLqnnknLAnTtOVxtjBaPFms2hN3RbgiDUmXpc3CkHJF2tySuqHTvghDHpdAq/LVYAnlpKEMh6Ra5654PB+4pi0t7ftwDaJ9FYb2Iv+d0MfkQzfsYwx7SyOtARcLY6pnyJtyOUET9lOFHXK3cXQwtrbBaELlOt1HquIX5MQk2FRpWWhZcF6OhJmMOpKv1ecyqOfkXgQVsVxm2kaYThrIJhKeaqlwVfhzMtdJ2qQ8NgTUEQ0Qt0kaMC59Q24NN7kX8W0Ir4N/G0JDW8nYi/gNmSmA5Q/kswroczGJTMs8f7HJn9JWu864AR1QepQOxZcJ37TdCr5uHBPx+IHsiN+S/wLXNgXiHqp5CAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-17&quot;
        title=&quot;tf-wordpress-17&quot;
        src=&quot;/static/f8f423402841ea4c479c5235d3b29ecb/5a190/tf-wordpress-17.png&quot;
        srcset=&quot;/static/f8f423402841ea4c479c5235d3b29ecb/772e8/tf-wordpress-17.png 200w,
/static/f8f423402841ea4c479c5235d3b29ecb/e17e5/tf-wordpress-17.png 400w,
/static/f8f423402841ea4c479c5235d3b29ecb/5a190/tf-wordpress-17.png 800w,
/static/f8f423402841ea4c479c5235d3b29ecb/65654/tf-wordpress-17.png 872w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Finally, we&apos;ll create an elastic IP so that in the event the server is rebooted, the server will maintain its public IP address.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.499999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAoklEQVQY03WO6w6CMAxGfRoH6y7s0pUxNiZRiTHh/R/HgP9Ak5OmX9qm5wJmEDhzV8Eki1n7LLvYYbY0aZe0G6WJjXBXbhvhD1xaRSY+4rxSXnw/5TTT+Ixlqfd1KEuZ32N9+f7mqYBCdjhm4FwoFjNXQZm+DgOGxDWBJq4CaBImbrWjVp4+g6ZdzDNwrUShke/9kZPzri2xEdv4mxn8WPrHB55WOa0GPDpvAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-18&quot;
        title=&quot;tf-wordpress-18&quot;
        src=&quot;/static/e9ffeaaf5a9df4d08c4511295de8182a/5a190/tf-wordpress-18.png&quot;
        srcset=&quot;/static/e9ffeaaf5a9df4d08c4511295de8182a/772e8/tf-wordpress-18.png 200w,
/static/e9ffeaaf5a9df4d08c4511295de8182a/e17e5/tf-wordpress-18.png 400w,
/static/e9ffeaaf5a9df4d08c4511295de8182a/5a190/tf-wordpress-18.png 800w,
/static/e9ffeaaf5a9df4d08c4511295de8182a/64d87/tf-wordpress-18.png 818w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Before creating the server, lets create some outputs so Terraform will tell let us know what our new server DNS and public IP is. In outputs.tf, define the following output values.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 774px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABKUlEQVQoz32Qa27CMBCEcxWqJvj9tndthzgQoKh/ev/bVAlUoqiq9Gk1tne04+0It4Q7IrxUoFxRLlPhB+aICES4Xc//oaMyMpOpTC4c6nxLdREGuUaugen0NoifVrHrxdsgn+mYra59uXLFepraFeq5tI/abqXdAs7aV2FQ+yptVhaJDFuiBx1XyfjqoTEZvUWPs42TjVPA2aXmcXbQNj05X4nwr+Y9swO1A3Pv1PXU9sTs+frte72zav7LuZqZrWr8FHGGesqHS8BjPly1H+/PVMaHEGHN/BKbquTzksaLcaXiBOMFxzOMZw8NygJ1CTCnsnhoMR+Nhf3T/E7asi0WiIxUJqaBqrVyg0wjM7BtHtnag1SlZ7r11mRmMjeFrWS+Hf+Em/LMN+YOZxm2eNBUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-19&quot;
        title=&quot;tf-wordpress-19&quot;
        src=&quot;/static/350fe3d613f46340cdae61fcfc247905/41d3b/tf-wordpress-19.png&quot;
        srcset=&quot;/static/350fe3d613f46340cdae61fcfc247905/772e8/tf-wordpress-19.png 200w,
/static/350fe3d613f46340cdae61fcfc247905/e17e5/tf-wordpress-19.png 400w,
/static/350fe3d613f46340cdae61fcfc247905/41d3b/tf-wordpress-19.png 774w&quot;
        sizes=&quot;(max-width: 774px) 100vw, 774px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As before, we can now iteratively update our architecture. First execute terraform plan.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuUlEQVQoz+WQPQ6CQBCFZ2ZnwWVBEwstPB+lBaJWXsVCYGGJFPwdhCMZ0RBbaH35ikleXr5k4JncHyYxNjOlyfK0KIy1eVFkeZ5WVdn3TdfVE21b933TNLW1dhgGIF/IjSSP2GdmUoq0Fuu11Fp6HrkOrlxUioKAtSbHIQBgZgAIwxAISdA7ggQi/UTQt5luMc5QSgkAURTBgnzMcRz/5xhxLjx++7TMLEfz5RyD2u7nEuwOgM7xensBXi47u0i3JqYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-20&quot;
        title=&quot;tf-wordpress-20&quot;
        src=&quot;/static/bebce04f95d3770a228374f2a7ba89df/5a190/tf-wordpress-20-1024x568.png&quot;
        srcset=&quot;/static/bebce04f95d3770a228374f2a7ba89df/772e8/tf-wordpress-20-1024x568.png 200w,
/static/bebce04f95d3770a228374f2a7ba89df/e17e5/tf-wordpress-20-1024x568.png 400w,
/static/bebce04f95d3770a228374f2a7ba89df/5a190/tf-wordpress-20-1024x568.png 800w,
/static/bebce04f95d3770a228374f2a7ba89df/2bef9/tf-wordpress-20-1024x568.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Terraform should report that it will create 2 new resources. Now apply the plan to create the server.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAq0lEQVQoz8WRPQ6CQBCF3+7MuCg/Ft6RmsCZNMAuCDYsHEM9kKLRzkIavrxiivkyyTycy+OpLuvWVa2rXG0b27SNa6x1tr/04zSOk//Gj95PfhiGtuuutzvMIeQ9ccSSCBsdhxxFlMSsFH7BzADSNEWw3WkoImJi0qSJtCZhIaJ5Uc0Ar3xmEQGQZRl4Y/An78tFUUCWynmeQ0ywkkyyWS4r4hXk+duL5WfPD9YnOU62rQC/AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-21&quot;
        title=&quot;tf-wordpress-21&quot;
        src=&quot;/static/188ed92397218451d8dc2d82e7801c66/5a190/tf-wordpress-21-1024x568.png&quot;
        srcset=&quot;/static/188ed92397218451d8dc2d82e7801c66/772e8/tf-wordpress-21-1024x568.png 200w,
/static/188ed92397218451d8dc2d82e7801c66/e17e5/tf-wordpress-21-1024x568.png 400w,
/static/188ed92397218451d8dc2d82e7801c66/5a190/tf-wordpress-21-1024x568.png 800w,
/static/188ed92397218451d8dc2d82e7801c66/2bef9/tf-wordpress-21-1024x568.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It will take a number of minutes to create the server, but you can check in AWS console if the server creation is in progress, again confirming Terraforms awesomeness. Once TF has finished creating the instances you should see something like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Outputs: Wordpres_DNS = ec2-[your elastic ip address].compute-1.amazonaws.com Wordpress_Public_IP = [your elastic IP address]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s confirm that TF has created the instance successfully in the AWS console.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 8.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAdElEQVQI1wXBSw7CIBAA0N62nkYTT2Pixhu4UJTOT+jANFBk03WN7w2WU61FhEsp+/7rvRMiAiTVj8jkvQilWRmJmTTOhChMlhMiDE9P0b6T5OPpPI6Hy/UmWiku1jaHwUGwdYMl3v07aKPFHsAOQrT2Qv0DuohmqpJ2Oa0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-22&quot;
        title=&quot;tf-wordpress-22&quot;
        src=&quot;/static/02d80f2a14eee07e2dc892f94ff6bda3/5a190/tf-wordpress-22-1024x87.png&quot;
        srcset=&quot;/static/02d80f2a14eee07e2dc892f94ff6bda3/772e8/tf-wordpress-22-1024x87.png 200w,
/static/02d80f2a14eee07e2dc892f94ff6bda3/e17e5/tf-wordpress-22-1024x87.png 400w,
/static/02d80f2a14eee07e2dc892f94ff6bda3/5a190/tf-wordpress-22-1024x87.png 800w,
/static/02d80f2a14eee07e2dc892f94ff6bda3/2bef9/tf-wordpress-22-1024x87.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can see here that the instance is created and running. We can also see that our security group is mapped to the instance and that its using our elastic IP address.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 636px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 6.999999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAIAAABR8BlyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAQElEQVQI1wXBCw5AQAwFQPe/pPi1qG67EV7ZSM10mWlmRCQizFzd0b5+v4cDq8dSQAYqsdVHz3dUzAq2mBR+tR+uVTgYYSEq2QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-23&quot;
        title=&quot;tf-wordpress-23&quot;
        src=&quot;/static/97e551bcea763ba20a4e314f89f42e79/9be90/tf-wordpress-23.png&quot;
        srcset=&quot;/static/97e551bcea763ba20a4e314f89f42e79/772e8/tf-wordpress-23.png 200w,
/static/97e551bcea763ba20a4e314f89f42e79/e17e5/tf-wordpress-23.png 400w,
/static/97e551bcea763ba20a4e314f89f42e79/9be90/tf-wordpress-23.png 636w&quot;
        sizes=&quot;(max-width: 636px) 100vw, 636px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 290px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 10.999999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAfklEQVQI1yXLwQrCIBgA4L2NN8HqVbKD4HqKolcMAg/Fj40JomP70UsxixFqUN/9a0opzrnpBxFjjFrrlFKtdfnkbkrhudzHuQ/vLeeEEMYYpXS13pzVtck5W2sBQCkFAN57Ywwi/vPFPGx4wTDfhnQ4nnacSymFEG2773rzBdqAWo9XyD8xAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-24&quot;
        title=&quot;tf-wordpress-24&quot;
        src=&quot;/static/92ad5e7608705805d51ed78df05277d3/139a5/tf-wordpress-24.png&quot;
        srcset=&quot;/static/92ad5e7608705805d51ed78df05277d3/772e8/tf-wordpress-24.png 200w,
/static/92ad5e7608705805d51ed78df05277d3/139a5/tf-wordpress-24.png 290w&quot;
        sizes=&quot;(max-width: 290px) 100vw, 290px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And now we can successfully navigate to the blog using the DNS output by terraform, sweet!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAABVElEQVQoz42QzW6CQBSFeXH3Jhrtqu07NE2TRiQs+gotMXFLa+UndmCAAeZemL8GsNaaGv1yFyeTe+bMHCuO4+02CMPw82p8/70oitFoZBljABAAzdUIIYwx0+nUqmu+2Xxst4EQUindj/oRf7TWe3PbtsaYyWTSJR+OtdZK66PtjkFIKQGAc6xr4Bz25jSlnuf5vr9erxHPPr5p2jwvGCsRm9Vq1TTtfH5jKaUYY32gYiVLivqrqEnGKKVZluV5TikVQiileF1xzhExCAJjzGw2sw4FSCmjKIp3ZEfSiNCY0ISQhCQ7kjAQiLgJopRmiAgAp3/WWg+3/IuUkpVDMpRVuW/7uLBOD9NzbAaAIIo/gzClecGqX7MQQl6iFaLiWHJkNdCiSx6Px6fJ59BKcwDVbyopjTG3d/eW67q2bS8vsVjYD0/P9tJxHMd13dc373H58g3b+5qLRNAKDgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tf-wordpress-25&quot;
        title=&quot;tf-wordpress-25&quot;
        src=&quot;/static/de4db8ae4e4b2be3c662bca929364a71/5a190/tf-wordpress-25-1024x593.png&quot;
        srcset=&quot;/static/de4db8ae4e4b2be3c662bca929364a71/772e8/tf-wordpress-25-1024x593.png 200w,
/static/de4db8ae4e4b2be3c662bca929364a71/e17e5/tf-wordpress-25-1024x593.png 400w,
/static/de4db8ae4e4b2be3c662bca929364a71/5a190/tf-wordpress-25-1024x593.png 800w,
/static/de4db8ae4e4b2be3c662bca929364a71/2bef9/tf-wordpress-25-1024x593.png 1024w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Terraform rocks. You can download the source code for this terraform solution here: &lt;a href=&quot;https://github.com/ginocoates/terraform-aws-hosted-wordpress&quot;&gt;https://github.com/ginocoates/terraform-aws-hosted-wordpress&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Learn Terraform... &lt;a href=&quot;https://www.amazon.com/gp/product/B01MZYE7OY/ref=as_li_tl?ie=UTF8&amp;#x26;camp=1789&amp;#x26;creative=9325&amp;#x26;creativeASIN=B01MZYE7OY&amp;#x26;linkCode=as2&amp;#x26;tag=ginocoatescom-20&amp;#x26;linkId=ffe4d887a7a4fafd3e292f5f00d30175&quot;&gt;&lt;img src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#x26;MarketPlace=US&amp;#x26;ASIN=B01MZYE7OY&amp;#x26;ServiceVersion=20070822&amp;#x26;ID=AsinImage&amp;#x26;WS=1&amp;#x26;Format=_SL250_&amp;#x26;tag=ginocoatescom-20&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;img src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=ginocoatescom-20&amp;#x26;l=am2&amp;#x26;o=1&amp;#x26;a=B01MZYE7OY&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Image PanView - Objective-C]]></title><description><![CDATA[In my current IOS project I want to display an image on screen, but make it a little more interesting. The FlipBoard iPad application has a…]]></description><link>https://www.ginocoates.com/2012-08-18-image-panview-objective-c/</link><guid isPermaLink="false">https://www.ginocoates.com/2012-08-18-image-panview-objective-c/</guid><pubDate>Sat, 18 Aug 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my current IOS project I want to display an image on screen, but make it a little more interesting.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://www.Flipboard.com&quot;&gt;FlipBoard&lt;/a&gt; iPad application has a nice start screen which pans the images it displays, quite an eye catching effect. I wanted to do something similar in my application.&lt;/p&gt;
&lt;p&gt;To achieve this I created a UIView subclass with the following structure:&lt;/p&gt;
&lt;p&gt;UIView&lt;br&gt;
--UIImageView&lt;/p&gt;
&lt;p&gt;I then used [UIView animateWithDuration] to move the UIImageView whichever way I want.&lt;br&gt;
In the sample code the view selects a random animation from the following set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pan Left&lt;/li&gt;
&lt;li&gt;Pan Right&lt;/li&gt;
&lt;li&gt;Zoom In&lt;/li&gt;
&lt;li&gt;Zoom Out&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The end result is quite nice!&lt;/p&gt;
&lt;p&gt;Sample code can be downloaded here: &lt;a href=&quot;https://code.google.com/p/objective-c-panview&quot;&gt;https://code.google.com/p/objective-c-panview&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Screenshot from the sample is below. Clicking &quot;Animate&quot; will select a random animation.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 396px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 188%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAmCAYAAADEO7urAAAACXBIWXMAAAsTAAALEwEAmpwYAAAIY0lEQVRIx5WWe1BTVx7HLyAKbZ061tUijwQxEB7hkSqCYgghQLhBCJCQ8IiQCJiEJKCgQhQVHzxswTdW6UAFa6vWmbW13d3Z3c5s/9h/dha36rqzXTvTbbedbavV8iqYw/nunIui2Dq7e2Y+k989v9/vc8/v5k4mHAAOgP/O1laL0+XudLnr2zfbHe3WTdXtTnd9e53TJeCYocNmdxxo2LK1d+++fe/uaWu7tG3HjqFmj6eKOZhLWNbqmourktciOiYBMXGJkMbEIypahqjoeERK47AyMkZgxUopxCsiHyJBmDgCwaEirIiQoLS8/KIga+9sT1EoMyFeISXRsYleaUyCN1Iq80qi4gQiJDFeUbjEGyaWCJ+hoghvSNgK7/IQsTcoOMy7dNnyB/7zA4ksPgFdrx5K4Wx2mzZNoYIkKo4JqTQmgUqkMhoRJaOSqDgaKY2lsbIEGhMXTyMkUhoqihAICQtHUHAYfWnJUvrCwhdJdEwc7A6Hlmvv7LCvT89EpFRGYmVyGh2biOjYJMTGy4WR4+LleGX1GoHo2Hhh3PCIKIjCJQgKDsXSZUF04YuLSExsHOqcTp5r3d1qT12nwLKgMBIqWkmDQ8KxPFiMl5eLBJa9zJpCsCwoFL9YGoxFi5cKPL9wEfwXBMB/fgD14eaTKGk0XG43z+n1ek1BoR4HOl8lXd09tPNoH7p730TfwCDODAyhq/soWvfuR1OzB7vbDuB0/yB6z/Tj+Ot9OH7qDfScPE7d+61EqcpEWVkZz1XX1lRYqzfjm2/vkDt379K7P4zj/tgkxicmMTYxhXv3R/Hvb7/DF//6Ct98dwcTkw8wPjGF8R8ZXoz9+CP9593bxGA0ged5nrPZ7fWbamoxMnqfjIyO0PGJ+xgfv4+R0XsYHb/H9jE2/gPGJ0YwNjYiXDNGxxjfs306NjZGSowmaHJzea7SXJufxStRVpdCCq1yaq5Pw/ZOA+patSi1K2DcrEKZjUdtcx4a9mlg86hRaleiZnseth7UobGjgm45qCXpmckoM1l5rtrqzlVmpyJNt4js6DDThr0bkV0ejg1VMigKVkBnXYNs/TpUuHUw2JXQ2xQw1q2FZVshanfpsKlFS+vatCQtQ47y0hqe27Vrn12lWQ9lsZh0dLfRk2dOodKdg7QNEVitioBcKYLWkIJz5waxdY8FZc4sWBr1MNUpYN6qhrVFQxtfyyOqnFQYDVU819jYXJuqSEaFcwO5dOk87T15Ehc++ABJ6UGQp4uRpo3D3pNH0NpgxdbD3TA6smBzWFBgTUF9exNaXz1Imxo1JCUtAZVmG8+5nY25cUmxcDcaSG/fCery7MXA2fPIqVQjPmUx9FV6bDt8GiX5Suw4chqn3vsQJnMF9vd2oXtHGdpMGrqrppisUqphKTfxnMvZwCcly7Frp4W0bLdQdZYaLdubkMInQmVYiab6EmzdUYeQlRJ0NjvgbjAjSCLFm0d34YQuCh26lXSbo4DEyFNhr67iuZpaG782LQ1l5vWkuaGE5ucrEJ8gg8WRhcKytdjiMuDCpX6os3PQvceFgiI10pUq9PZ4YBKHoNtmobIEGYmMikZdXR3PmTfa+Mzsdeh5TUH2d7ho4/ZyNNtzcfFyDQbPWnC4uxRHWirgthpxaL8Dhzo3w2k3oqu9FsYyLTwNFqrNyyWKtFSUVmzkOZPJxmvyUjDwVi7xdDXTzv1OXOirwPu/q8LVD6vwy6smHO+wor66FG31Ggz2N2Kzowjn+hvgaaqEx1VATxw7QBSKVJhKzTxXUrKZz81bg5MDBrLRbqcbzWq83sNj8Eouhi7n4cLVXNR7jNDmaTHQbcLO3ToUl6rQ1ZOPSrMOO1sttPu1JpKybhUqyi08V6K389naJHQcSyc1rnyqUstwuCcTg++n4uyVZLz9q7Uot2QgT7UKA0Nm1Ddno65RgTfeKQSfuwZ7Duho5z4HychIg8FYznP6ohpek7caZ98tIf3nGqlnzyb0DW7Hx3+ux8d/2oI/XtuN02e2wemoxK8/6sPlK734/R+G8NuPLuBYbzc+/M15evHtfpKfvwF5Gwp4TpPL86bSMkxOTpBp6qWETGLqwRTo9PRDgOlpisnJKTx4QMEWpTM8XHRqykuK9QZoNBqey9FoBOH0NCUsiZ+suVtUMM0wE4MSQoheb0DOI6HRZILX6xWE0+xUlM4iNFO2Nz0rmZsHZb1FRcVzhewuMweYU4wfRsdx5/sR3L03ipHR8dlTPi0sLCp6LCwxGp8p/PLrO7j1j6/wyd++wJdff/e/CfWGktmRn5TR2adP54z7pHRWWFj4bOHjh4+f+1bxVI0g1M0VGjA1NUWYjU3+/8B6WK9O91CYnZ3Dl5QY2Z2e8dr818V6SFFxMbKys3kuv6CA53ktrl27Rm7evEmvX7+BGzeeDcs/5jpYz/DwMNFoNGAuzuFw8GJxOHx9/chzzz1PAwOfQ8AjAgLnsGBBwE8ICAikHOdDwkQiONjvYZXFwgeHhLI/dsTH148l8SQ+Pr7w85snwGKO457Ch7Le4OBgMBfndLm0IpFYEPr5zaM+Pr4ModnX1w/+/vPxwsKFAixme4/yrNZXOARHwkRiMBfXstOTFRYmYsIpNjY7PsPHx5ewwpeWLCGJSYkCLBYmEXIzdTM93BRztHg8WdzgW0OLM9XqT+fPXzA74iPY9bx5/hCHhwuw+OdqWK8qM/PTs+cGF3M3bv2VszvsSYVFRe8plRl/ychQDWdkqK49Il2pvLZ23ToBFrM9ZUbGo/ww62G9zMFcXKZa7ffZ559xw5/c4Fa9Ig+QJyUGJiUlBsjliYFJiQmBycmrA/ML8gRYzPaEXFKCUMt6bv39Nnf7888E138A337Fq63H4M4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Image&quot;
        title=&quot;Image&quot;
        src=&quot;/static/1d07c4a8df6dab7bba7ff517aaea1822/db910/screen-shot-2012-08-18-at-11-20-12-am.png&quot;
        srcset=&quot;/static/1d07c4a8df6dab7bba7ff517aaea1822/772e8/screen-shot-2012-08-18-at-11-20-12-am.png 200w,
/static/1d07c4a8df6dab7bba7ff517aaea1822/db910/screen-shot-2012-08-18-at-11-20-12-am.png 396w&quot;
        sizes=&quot;(max-width: 396px) 100vw, 396px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Objective C State Machine]]></title><description><![CDATA[Im currently working on a game in which the user interface has quite a number of possible states, with certain sprites being enabled…]]></description><link>https://www.ginocoates.com/2011-06-12-objective-c-state-machine/</link><guid isPermaLink="false">https://www.ginocoates.com/2011-06-12-objective-c-state-machine/</guid><pubDate>Sun, 12 Jun 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Im currently working on a game in which the user interface has quite a number of possible states, with certain sprites being enabled, disabled, visible or invisible depending on the what the user touches on screen.&lt;/p&gt;
&lt;p&gt;Rather than coding all of the UI state logic into a view controller or helper class, I decided to put together a simple state machine instead. It seemed to work for my current app quite well, and I reckon its something that will come in handly in the future.&lt;/p&gt;
&lt;p&gt;You can find the source code here: &lt;a href=&quot;http://code.google.com/p/objective-c-state-machine/&quot;&gt;http://code.google.com/p/objective-c-state-machine/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So How do you use it?&lt;/p&gt;
&lt;p&gt;Each state is represented by a StateInfo object, which has a name, a set of transitions, and a selector to execute when the state is entered.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;objc&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;@interface StateInfo : NSObject
{
  NSString\* \_stateName;
  NSMutableArray\* \_transitions;
  SEL \_selector;
  }
  @property (nonatomic, retain) NSMutableArray\* transitions;
  @property (nonatomic, retain) NSString\* stateName;
  @property (nonatomic) SEL selector;
  -(id) initWithStateName:(NSString\*)stateName;
  -(StateInfo\*) executeTransition:(id)anObject;
  -(BOOL) canExecuteTransition:(id)anObject;
  -(void) registerNextState:(StateInfo\*)nextState withCondition:(NSString\*)condition;
@end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The collection of transitions is made up of Transition objects, each with a condition and a StateInfo object pointing to the next state. Underneath, the condition uses an NSPredicate object, so the format for specifying the condition should follow the NSPredicate syntax.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;objc&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;@interface Transition : NSObject
  {
    NSString* _condition;
    StateInfo* _nextState;
  }
  @property (nonatomic,retain) NSString* condition;
  @property (nonatomic, retain) StateInfo* nextState;
  -(id)initWithCondition:(NSString*)condition andState:(StateInfo*)state;
  -(StateInfo*)execute:(id)anObject;
  @end

  The StateMachine object is then a simple set of states and a pointer to the current state.

  @interface StateMachine : NSObject {
    NSMutableArray* _states;
    StateInfo* _currentState;
  }
  @property (nonatomic, assign) StateInfo* currentState;
  @property (nonatomic, retain) NSMutableArray* states;
  -(id)initWithInitialState:(StateInfo*)state;
  -(void) registerState:(StateInfo*)state;
  -(StateInfo*) nextState:(id)anObject;
  -(void) registerInitialState:(StateInfo*)state;
  -(BOOL) canTransition:(id)anObject;
  -(BOOL) isCurrentState:(NSString*)stateName;
@end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The main method for the StateMachine is called nextState, and its called by passing a target object to test the transitions against, e.g.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;objc&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;StateMachine* stateMachine = [[StateMachine alloc] initWithInitialState:initialState];
...
[stateMachine nextState:targetObject];&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;When its called, the StateMachine tests the condition of each transition in the currentState against targetObject.&lt;/p&gt;
&lt;p&gt;If a transitions condition is true, Transition.nextState becomes StateMachine.currentState, and the currentState selector is performed in the targetObject.&lt;/p&gt;
&lt;p&gt;To setup a statemachine its pretty simple, create a statemachine object, then register the states one by one....&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;objc&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;StateInfo* initialState = [[StateInfo alloc] initWithStateName:@&amp;quot;initialState&amp;quot;];
StateMachine* stateMachine = [[StateMachine alloc] initWithInitialState:initialState];
StateInfo* state1 = [[StateInfo alloc] initWithStateName:@&amp;quot;state1&amp;quot;];
state1.selector = @selector(state1Method);

//--register the next state for the initial state
[initialState registerNextState:state1 withCondition:@&amp;quot;TRUEPREDICATE&amp;quot;];&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The project on google code contains a sample objective-c application that uses the state machine. To get the code, use svn at the command line.... svn checkout &lt;a href=&quot;http://objective-c-state-machine.googlecode.com/svn/trunk/&quot;&gt;http://objective-c-state-machine.googlecode.com/svn/trunk/&lt;/a&gt; objective-c-state-machine-read-only&lt;/p&gt;</content:encoded></item></channel></rss>