PageSourceSearch

https://matthewbonig.com/posts/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/

html matthewbonig.com collected 2026-10-03 20:47:44 UTC 79,240 bytes, 185 lines download raw bytes

1<!DOCTYPE html><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"/><link rel="manifest" href="/favicon/site.webmanifest"/><link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#000000"/><link rel="shortcut icon" href="/favicon/favicon.ico"/><meta name="msapplication-TileColor" content="#000000"/><meta name="msapplication-config" content="/favicon/browserconfig.xml"/><meta name="theme-color" content="#000"/><link rel="alternate" type="application/rss+xml" href="/feed.xml"/><meta property="og:image" content="/assets/images/profile.jpg"/><title>Creating a CICD for your Ionic 4 app in AWS in 5 minutes | Matthew Bonig</title><meta name="description" content="Here are the results of the 2022 CDK community survey."/><meta property="og:type" content="website"/><meta property="og:title" content="Creating a CICD for your Ionic 4 app in AWS in 5 minutes"/><meta property="og:description" content="Ionic 4 Websites (or any static website) can now be deployed very easily."/><meta property="og:image" content="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png"/><meta name="twitter:image" content="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png"/><meta name="twitter:title" content="Creating a CICD for your Ionic 4 app in AWS in 5 minutes"/><meta name="twitter:description" content="Ionic 4 Websites (or any static website) can now be deployed very easily."/><meta name="twitter:card" content="summary_large_image"/><link rel="preload" href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png" as="image" fetchpriority="high"/><meta name="next-head-count" content="24"/><link rel="preconnect" href="https://fonts.googleapis.com"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin=""/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /><link rel="preload" href="/_next/static/css/ec2cd261197f9ce0.css" as="style"/><link rel="stylesheet" href="/_next/static/css/ec2cd261197f9ce0.css" data-n-g=""/><noscript data-n-css=""></noscript>
1<script defer="" nomodule="" src="/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script>
1<script src="/_next/static/chunks/webpack-ee7e63bc15b31913.js" defer=""></script>
1<script src="/_next/static/chunks/framework-18c7e6fcf99e5daa.js" defer=""></script>
1<script src="/_next/static/chunks/main-ae09fb0e0bd758b6.js" defer=""></script>
1<script src="/_next/static/chunks/pages/_app-12ed59e3f3223d82.js" defer=""></script>
1<script src="/_next/static/chunks/996-ea0cdc55f980b418.js" defer=""></script>
1<script src="/_next/static/chunks/392-516ffcfa1439bc39.js" defer=""></script>
1<script src="/_next/static/chunks/566-c8026121a920e00a.js" defer=""></script>
1<script src="/_next/static/chunks/278-b7a37dde6fcf3083.js" defer=""></script>
1<script src="/_next/static/chunks/587-d5a7447cb2e6deda.js" defer=""></script>
1<script src="/_next/static/chunks/pages/posts/%5Bslug%5D-61ea3e77837440fe.js" defer=""></script>
1<script src="/_next/static/hyZ6GYxnkw3oJ7wBfe284/_buildManifest.js" defer=""></script>
1<script src="/_next/static/hyZ6GYxnkw3oJ7wBfe284/_ssgManifest.js" defer=""></script>
1<style data-href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap">@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfMZs.woff) format('woff')}@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuI6fMZs.woff) format('woff')}@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuGKYMZs.woff) format('woff')}@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuFuYMZs.woff) format('woff')}@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuDyYMZs.woff) format('woff')}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;
1src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
1unicode-range:U+1F00-1FFF}@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}</style></head><body class="bg-surface"><div id="__next"><div class="site-banner bg-gradient-to-r from-amber-300 via-orange-400 to-rose-400 text-surface"><div class="max-w-screen-lg mx-auto px-5 py-3 text-center text-sm md:text-base font-medium">I&#x27;ve created an Advanced CDK course, check it out<!-- --> <a class="underline underline-offset-2 decoration-surface/50 hover:decoration-surface transition-colors" href="https://matthewbonig.sidkik.app">here</a>!</div></div><nav class="sticky top-0 z-50 backdrop-blur-xl bg-surface/80 border-b border-white/[0.06]"><div class="max-w-screen-lg mx-auto px-5"><div class="flex flex-wrap flex-row justify-center items-center gap-1 md:gap-2 py-4 text-sm md:text-base font-medium"><a class="px-4 py-2 rounded-lg text-gray-400 hover:text-brand-light hover:bg-white/[0.06] transition-all duration-200" href="/">Blog</a><a class="px-4 py-2 rounded-lg text-gray-400 hover:text-brand-light hover:bg-white/[0.06] transition-all duration-200" href="/resume/">About Me</a><a class="px-4 py-2 rounded-lg text-gray-400 hover:text-brand-light hover:bg-white/[0.06] transition-all duration-200" href="/timeline/">Timeline</a><a target="_blank" rel="noopener noreferrer" class="px-4 py-2 rounded-lg text-gray-400 hover:text-brand-light hover:bg-white/[0.06] transition-all duration-200" href="https://github.com/mbonig">GitHub</a><a target="_blank" rel="noopener noreferrer" class="px-4 py-2 rounded-lg text-gray-400 hover:text-brand-light hover:bg-white/[0.06] transition-all duration-200" href="https://www.linkedin.com/in/matthewbonig/">LinkedIn</a><button type="button" class="px-4 py-2 rounded-lg text-gray-400 hover:text-brand-light hover:bg-white/[0.06] transition-all duration-200">The Pure Web</button><button type="button" class="px-4 py-2 rounded-lg text-gray-400 hover:text-brand-light hover:bg-white/[0.06] transition-all duration-200">2000s</button></div></div></nav><div class="min-h-screen pt-4"><main>
1<div class="undefined container mx-auto px-5 max-w-screen-lg"><article class="mb-32"><h1 class="text-3xl md:text-5xl lg:text-6xl font-extrabold tracking-tighter leading-tight md:leading-none mb-12 text-center md:text-left text-white">Creating a CICD for your Ionic 4 app in AWS in 5 minutes</h1><div class="mb-8 md:mb-12 sm:mx-0 overflow-hidden rounded-xl"><div class="sm:mx-0 overflow-hidden rounded-lg"><img alt="Cover Image for Creating a CICD for your Ionic 4 app in AWS in 5 minutes" fetchpriority="high" width="1300" height="630" decoding="async" data-nimg="1" class="w-full max-w-screen-md m-auto rounded-lg" style="color:transparent" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png"/></div></div><div class="max-w-2xl mx-auto"><div class="mb-8 text-sm font-medium text-gray-500 uppercase tracking-wider"><time dateTime="2019-01-19">January	19, 2019</time></div></div><div class="max-w-2xl mx-auto"><p class="mb-4 text-gray-300 leading-relaxed">Ionic 4 just hit RC2, and I am playing around with a simple Task app for a friend. He wants mobile apps and a website,
2so
3a perfect fit for Ionic, since it can be deployed as a static website pretty easily.</p>
4<p class="mb-4 text-gray-300 leading-relaxed">Combine that with AWS&#x27;s recent announcement
5of <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://aws.amazon.com/about-aws/whats-new/2019/01/aws-codepipeline-now-supports-deploying-to-amazon-s3/">S3 deployment support for CodePipeline</a>
6means we now have a very simple method for building and deploying the PWA side of the Ionic app. I intend to show you
7how you can get it setup in about 5 minutes**. When all done, the process will look something like this:</p>
8<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png" target="_blank"><img class="rounded-lg my-4" alt="Basically, the CICD for an Ionic PWA" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png"/></a></p>
9<ol class="list-decimal list-inside mb-4 pl-4 text-gray-300" depth="0">
10<li>Developer pushes code to CodeCommit/Github repository</li>
11<li>CodeCommit fires a webhook to CodePipeline</li>
12<li>CodePipeline pulls code from the repository and hands over to CodeBuild</li>
13<li>CodeBuild compiles Ionic into static files for the web and pushes to S3</li>
14<li>CloudFront updates cache from S3</li>
15<li>End users read built files from CloudFront site.</li>
16</ol>
17<p class="mb-4 text-gray-300 leading-relaxed">By the end of this blog, you should be able to deploy your own CodeCommit/Github repository to the public web entirely
18by CICD.</p>
19<p class="mb-4 text-gray-300 leading-relaxed">We&#x27;re going to start with a basic Ionic 4 application. I assume you already have the Ionic cli installed ( npm i -g
20ionic if not), so let&#x27;s bootstrap a new project:</p>
21<pre><pre style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;padding:1em;margin:0.5em 0;overflow:auto;border-radius:0.75rem;border:1px solid rgba(255,255,255,0.06)"><code class="language-shell" style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none"><span>$ ionic start myApp tabs --type</span><span class="token" style="color:hsl(207, 82%, 66%)">=</span><span>angular </span><span class="token" style="color:hsl(207, 82%, 66%)">&amp;&amp;</span><span> </span><span class="token" style="color:hsl(29, 54%, 61%)">cd</span><span> myApp
22</span></code></pre></pre>
23<p class="mb-4 text-gray-300 leading-relaxed">You can verify the build for the web app works:</p>
24<pre><pre style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;padding:1em;margin:0.5em 0;overflow:auto;border-radius:0.75rem;border:1px solid rgba(255,255,255,0.06)"><code class="language-shell" style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none"><span>$ ionic build --prod
25</span></code></pre></pre>
26<p class="mb-4 text-gray-300 leading-relaxed">When done you should now see a bunch of compiled files in the ./www/ directory. These are Ionic&#x27;
26s deployable web assets.</p>
27<p class="mb-4 text-gray-300 leading-relaxed">Let&#x27;s make this a process we can easily run with npm. Update your package.json, adding a new &quot;build:prod&quot; to your
28scripts config:</p>
29<pre><pre style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;padding:1em;margin:0.5em 0;overflow:auto;border-radius:0.75rem;border:1px solid rgba(255,255,255,0.06)"><code class="language-json" style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none"><span class="token" style="color:hsl(220, 14%, 71%)">{</span><span>
30</span><span>  </span><span class="token" style="color:hsl(355, 65%, 65%)">&quot;scripts&quot;</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(220, 14%, 71%)">{</span><span>
31</span><span>    </span><span class="token" style="color:hsl(355, 65%, 65%)">&quot;ng&quot;</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&quot;ng&quot;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span>
32</span><span>    </span><span class="token" style="color:hsl(355, 65%, 65%)">&quot;start&quot;</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&quot;ng serve&quot;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span>
33</span><span>    </span><span class="token" style="color:hsl(355, 65%, 65%)">&quot;build&quot;</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&quot;ng build&quot;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span>
34</span><span>    </span><span class="token" style="color:hsl(355, 65%, 65%)">&quot;test&quot;</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&quot;ng test&quot;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span>
35</span><span>    </span><span class="token" style="color:hsl(355, 65%, 65%)">&quot;lint&quot;</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&quot;ng lint&quot;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span>
36</span><span>    </span><span class="token" style="color:hsl(355, 65%, 65%)">&quot;e2e&quot;</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&quot;ng e2e&quot;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span>
37</span><span>    </span><span class="token" style="color:hsl(355, 65%, 65%)">&quot;build:prod&quot;</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&quot;ionic build --prod&quot;</span><span>
38</span><span>  </span><span class="token" style="color:hsl(220, 14%, 71%)">}</span><span>
39</span><span></span><span class="token" style="color:hsl(220, 14%, 71%)">}</span><span>
40</span></code></pre></pre>
41<p class="mb-4 text-gray-300 leading-relaxed">Now let&#x27;s add a buildspec.yml file to the root of the project. We&#x27;re going to point CodeBuild at this later.</p>
42<pre><pre style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;padding:1em;margin:0.5em 0;overflow:auto;border-radius:0.75rem;border:1px solid rgba(255,255,255,0.06)"><code class="language-yaml" style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none"><span class="token key" style="color:hsl(29, 54%, 61%)">version</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span> </span><span class="token" style="color:hsl(29, 54%, 61%)">0.2</span><span>
43</span><span></span><span class="token key" style="color:hsl(29, 54%, 61%)">phases</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span>
44</span><span>  </span><span class="token key" style="color:hsl(29, 54%, 61%)">pre_build</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span>
45</span><span>    </span><span class="token key" style="color:hsl(29, 54%, 61%)">commands</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span>
46</span><span>      </span><span class="token" style="color:hsl(220, 14%, 71%)">-</span><span> npm i
47</span><span>  </span><span class="token key" style="color:hsl(29, 54%, 61%)">build</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span>
48</span><span>    </span><span class="token key" style="color:hsl(29, 54%, 61%)">commands</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span>
49</span><span>      </span><span class="token" style="color:hsl(220, 14%, 71%)">-</span><span> npm run build</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span>prod
50</span><span></span><span class="token key" style="color:hsl(29, 54%, 61%)">artifacts</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span>
51</span><span>  </span><span class="token key" style="color:hsl(29, 54%, 61%)">files</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span>
52</span><span>    </span><span class="token" style="color:hsl(220, 14%, 71%)">-</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;**/*&#x27;</span><span>
53</span><span>  </span><span class="token key" style="color:hsl(29, 54%, 61%)">base-directory</span><span class="token" style="color:hsl(220, 14%, 71%)">:</span><span> www
54</span></code></pre></pre>
55<p class="mb-4 text-gray-300 leading-relaxed">That&#x27;s all we need to do to the code, so now push this to your repo, either AWS CodeCommit or Github.</p>
56<p class="mb-4 text-gray-300 leading-relaxed">Now login to your <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://console.aws.amazon.com/codepipeline/home">AWS CodePipeline Console</a>. We&#x27;re going to create a
57new CodePipeline:</p>
58<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/pipeline-settings.png" target="_blank"><img class="rounded-lg my-4" alt="Start creating the CodePipeline" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/pipeline-settings.png"/></a></p>
59<p class="mb-4 text-gray-300 leading-relaxed">Fill it out similar to above. You can override as you see fit but the defaults should work. Click Next.</p>
60<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/source.png" target="_blank"><img class="rounded-lg my-4" alt="Pick your source" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/source.png"/></a></p>
61<p class="mb-4 text-gray-300 leading-relaxed">Fill out the source, in this case I&#x27;m using a Github repository. Click Next. Now we setup the Build State, the CodeBuild
62step:</p>
63<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build.png" target="_blank"><img class="rounded-lg my-4" alt="Do the build" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build.png"/></a></p>
64<p class="mb-4 text-gray-300 leading-relaxed">Click Create Project.</p>
65<p class="mb-4 text-gray-300 leading-relaxed">In the new window start to fill out some basic CodeBuild info:</p>
66<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/codebuild.png" target="_blank"><img class="rounded-lg my-4" alt="CodeBuild project details" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/codebuild.png"/></a></p>
67<p class="mb-4 text-gray-300 leading-relaxed">Then fill out the specifics for the build:</p>
68<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/buildspec.png" target="_blank"><img class="rounded-lg my-4" alt="" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/buildspec.png"/></a></p>
69<p class="mb-4 text-gray-300 leading-relaxed">Ionic is Node-based, hence using a Node Runtime. Your mileage may vary.</p>
70<p class="mb-4 text-gray-300 leading-relaxed">We&#x27;ll be using a Node-based container for our build environment. You can leave everything else on the defaults:</p>
71<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build-defaults.png" target="_blank"><img class="rounded-lg my-4" alt="Defaults are good here" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build-defaults.png"/></a></p>
72<p class="mb-4 text-gray-300 leading-relaxed"><strong>If you want to log the build output - which I suggest you do but is not required - go ahead and fill out CloudWatch
73specifics (the correct CloudWatch policies will NOT automatically be added to the related service role. You&#x27;ll need
74to <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-identity-based-access-control-cwl.html"></a>add those
75yourself).</strong></p>
76<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/logs.png" target="_blank"><img class="rounded-lg my-4" alt="Remember to add the policies later" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/logs.png"/></a></p>
77<p class="mb-4 text-gray-300 leading-relaxed">When you&#x27;re all done, click Continue to CodePipeline. Your CodeBuild project should now be selected.</p>
78<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build-final.png" target="_blank"><img class="rounded-lg my-4" alt="Confirm your newly created
79CodeBuild project" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build-final.png"/></a></p>
80<p class="mb-4 text-gray-300 leading-relaxed">The next step you will select an S3 bucket to deploy your files to. However, that screen doesn&#x27;t give you the option to
81create the bucket the same way we got to create a CodeBuild project on the fly. So go ahead and open a new tab for
82the <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://s3.console.aws.amazon.com/s3/home">AWS S3 Console</a> and create a bucket over there.</p>
83<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/bucket.png" target="_blank"><img class="rounded-lg my-4" alt="Bucket time!" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/bucket.png"/></a></p>
84<p class="mb-4 text-gray-300 leading-relaxed">You can accept all defaults if you want, the bucket does not need to be, and shouldn&#x27;t be, public.</p>
85<p class="mb-4 text-gray-300 leading-relaxed">Review and Create the bucket. Now go back to your other tab, where you have setup the CodeBuild project in CodePipeline
86and hit Next.</p>
87<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/deploy.png" target="_blank"><img class="rounded-lg my-4" alt="Pick your bucket" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/deploy.png"/></a></p>
88<p class="mb-4 text-gray-300 leading-relaxed">Pick your bucket and make sure you pick to &#x27;Extract file before deploy&#x27;. Hit Next.</p>
89<p class="mb-4 text-gray-300 leading-relaxed">Review all your settings one last time and then hit Create Pipeline:</p>
90<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/review.png" target="_blank"><img class="rounded-lg my-4" alt="Review and Create the
91CodePipeline!" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/review.png"/></a></p>
92<p class="mb-4 text-gray-300 leading-relaxed">Once created, CodePipeline will automatically run the first build. No need to push anything to the repository.</p>
93<p class="mb-4 text-gray-300 leading-relaxed">Now, if you picked to log your CodeBuild project but didn&#x27;t remember to setup the permissions manually, or didn&#x27;t get to
94it quick enough, you&#x27;re going to see an error:</p>
95<pre><pre style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;padding:1em;margin:0.5em 0;overflow:auto;border-radius:0.75rem;border:1px solid rgba(255,255,255,0.06)"><code class="language-text" style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none"><span>ACCESS_DENIED: Service role arn:aws:iam::XXXXXX:role/service-role/codebuild-ionic-cicd-build-service-role does not allow AWS CodeBuild to create Amazon CloudWatch Logs log streams for build arn:aws:codebuild:us-east-1:XXXXXX:build/ionic-cicd-build:ABCDEF....
96</span></code></pre></pre>
97<p class="mb-4 text-gray-300 leading-relaxed">Even though AWS setup the service role for you, it did not give it permissions to write CloudWatch
98logs. <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-identity-based-access-control-cwl.html">Read here</a>
99if you are unsure how to do this. For my testing, I just attached
100the <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://mbonig.wordpress.com/w
100p-admin/policy/CloudWatchAgentServerPolicy">CloudWatchAgentServerPolicy</a> to my
101service role.</p>
102<p class="mb-4 text-gray-300 leading-relaxed">You should see a completed CodePipeline like this:</p>
103<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/codepipeline-build.png" target="_blank"><img class="rounded-lg my-4" alt="First run of the
104build!" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/codepipeline-build.png"/></a></p>
105<p class="mb-4 text-gray-300 leading-relaxed">And if you check your S3 bucket, you should see files similar to this:</p>
106<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/bucket-contents.png" target="_blank"><img class="rounded-lg my-4" alt="Look at those glorious files!" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/bucket-contents.png"/></a></p>
107<p class="mb-4 text-gray-300 leading-relaxed">Great! We&#x27;re almost done. Now we just need a CloudFront distribution in front of this. Now, I know what you&#x27;re
108wondering:</p>
109<blockquote class="border-l-2 border-brand pl-4 mb-4 text-gray-400 italic">
110<p class="mb-4 text-gray-300 leading-relaxed">&quot;But can&#x27;t I just use S3 Static Website Hosting&quot; - me, at the start of this</p>
111</blockquote>
112<p class="mb-4 text-gray-300 leading-relaxed">You&#x27;d think, and if you didn&#x27;t care about SSL, you probably could. However, any good website should have SSL and
113CloudFront also solves a few issues around routing for us, so let&#x27;s continue...</p>
114<p class="mb-4 text-gray-300 leading-relaxed">Start creating a new Web distribution in the <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://console.aws.amazon.com/codepipeline/home">CloudFront Console</a>:</p>
115<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/origin.png" target="_blank"><img class="rounded-lg my-4" alt="Pick your bucket, and setup access" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/origin.png"/></a></p>
116<p class="mb-4 text-gray-300 leading-relaxed">Pick your S3 bucket as the origin domain name. We want to restrict bucket access, so that we don&#x27;t have to make our S3
117bucket public. Let CloudFront take care of granting the permissions in the bucket policy. The only other default you
118should have to change is the Default Root Object near the bottom, set that to &quot;index.html&quot;.</p>
119<p class="mb-4 text-gray-300 leading-relaxed">I&#x27;m not going to setup a CNAME alternative for this (for custom domain names) but you probably will want to, so read up
120on it <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html">here</a>. When
121you&#x27;re satisfied, go ahead and create the distribution.</p>
122<p class="mb-4 text-gray-300 leading-relaxed">It&#x27;ll take a few minutes, but once complete you should be able to load the site at the Domain Name CloudFront generated
123for your distribution.</p>
124<p class="mb-4 text-gray-300 leading-relaxed">And that&#x27;s generally it. You should now have a functioning website based on the Ionic starter template. Any updates you
125push to your repository will be deployed out after a few minutes of the CICD process running. However, since we&#x27;re using
126CloudFront, the cache will still show the old files. That&#x27;s not good. You COULD let the cache naturally expire, or
127invalidate it yourself, but one requires waiting FAR too long to see the new code, and the other is manual. Both are
128gross.</p>
129<p class="mb-4 text-gray-300 leading-relaxed">We need a way to tell CloudFront to invalidate the files after they&#x27;ve been redeployed to S3. So, let&#x27;s take a look at
130our options. If we go into editing the CodePipeline, let&#x27;s take a look at what kind of actions we can take after the S3
131push:</p>
132<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/deploy-options.png" target="_blank"><img class="rounded-lg my-4" alt="I don&#x27;t see anything that
133says CloudFront" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/deploy-options.png"/></a></p>
134<p class="mb-4 text-gray-300 leading-relaxed">Hmm, so nothing regarding CloudFront, bummer. What else we got?</p>
135<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invoke-lambda.png" target="_blank"><img class="rounded-lg my-4" alt="Lambda to the rescue!" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invoke-lambda.png"/></a></p>
136<p class="mb-4 text-gray-300 leading-relaxed">Ah ha, Lambda!
137The <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://www.amazon.com/Victorinox-1-6795-XAVT-Swiss-Swisschamp-Pocket/dp/B000JC19FE/ref=sr_1_31_sspa?ie=UTF8&amp;keywords=swiss+army+knife&amp;link_code=qs&amp;qid=1547938319&amp;sr=8-31-spons&amp;tag=wwwcanoniccom-20&amp;psc=1">Swiss Army Knife</a>
138of AWS. Since we can call a Lambda we can easily create one that will invalidate the CloudFront cache:</p>
139<pre><pre style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;padding:1em;margin:0.5em 0;overflow:auto;border-radius:0.75rem;border:1px solid rgba(255,255,255,0.06)"><code class="language-javascript" style="background:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3);font-family:&quot;Fira Code&quot;, &quot;Fira Mono&quot;, Menlo, Consolas, &quot;DejaVu Sans Mono&quot;, monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none"><span class="token" style="color:hsl(286, 60%, 67%)">const</span><span> </span><span class="token" style="color:hsl(29, 54%, 61%)">AWS</span><span> </span><span class="token" style="color:hsl(207, 82%, 66%)">=</span><span> </span><span class="token" style="color:hsl(207, 82%, 66%)">require</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;aws-sdk&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
140</span><span></span><span class="token" style="color:hsl(286, 60%, 67%)">const</span><span> cloudfront </span><span class="token" style="color:hsl(207, 82%, 66%)">=</span><span> </span><span class="token" style="color:hsl(286, 60%, 67%)">new</span><span> </span><span class="token" style="color:hsl(29, 54%, 61%)">AWS</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token" style="color:hsl(29, 54%, 61%)">CloudFront</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
141</span><span>exports</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token method-variable function-variable method property-access" style="color:hsl(207, 82%, 66%)">handler</span><span> </span><span class="token" style="color:hsl(207, 82%, 66%)">=</span><span> </span><span class="token" style="color:hsl(286, 60%, 67%)">async</span><span> </span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token parameter">event</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span> </span><span class="token arrow" style="color:hsl(207, 82%, 66%)">=&gt;</span><span> </span><span class="token" style="color:hsl(220, 14%, 71%)">{</span><span>
142</span><span>  </span><span class="token" style="color:hsl(220, 10%, 40%);font-style:italic">// Extract the Job ID     </span><span>
143</span><span>  </span><span class="token console" style="color:hsl(29, 54%, 61%)">console</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token method property-access" style="color:hsl(207, 82%, 66%)">log</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;event:&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span> event</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
144</span><span>  </span><span class="token" style="color:hsl(286, 60%, 67%)">const</span><span> job_id </span><span class="token" style="color:hsl(207, 82%, 66%)">=</span><span> event</span><span class="token" style="color:hsl(220, 14%, 71%)">[</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;CodePipeline.job&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">]</span><span class="token" style="color:hsl(220, 14%, 71%)">[</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;id&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">]</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
145</span>
146<span>  </span><span class="token" style="color:hsl(220, 10%, 40%);font-style:italic">// Extract the Job Data     </span><span>
147</span><span>  </span><span class="token" style="color:hsl(286, 60%, 67%)">const</span><span> job_data </span><span class="token" style="color:hsl(207, 82%, 66%)">=</span><span> event</span><span class="token" style="color:hsl(220, 14%, 71%)">[</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;CodePipeline.job&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">]</span><span class="token" style="color:hsl(220, 14%, 71%)">[</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;data&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">]</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
148</span>
149<span>  </span><span class="token console" style="color:hsl(29, 54%, 61%)">console</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token method property-access" style="color:hsl(207, 82%, 66%)">log</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;job_data:&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span> job_data</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
150</span><span>  </span><span class="token" style="color:hsl(286, 60%, 67%)">const</span><span>
150 distribution_id </span><span class="token" style="color:hsl(207, 82%, 66%)">=</span><span> job_data</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token property-access">actionConfiguration</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token property-access">configuration</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token property-access maybe-class-name">UserParameters</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
151</span><span>  </span><span class="token console" style="color:hsl(29, 54%, 61%)">console</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token method property-access" style="color:hsl(207, 82%, 66%)">log</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;invalidating distribution:&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span> distribution_id</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
152</span><span>  </span><span class="token control-flow" style="color:hsl(286, 60%, 67%)">await</span><span> cloudfront</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token method property-access" style="color:hsl(207, 82%, 66%)">createInvalidation</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(220, 14%, 71%)">{</span><span>
153</span><span>    </span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">DistributionId</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> distribution_id</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span>
154</span><span>    </span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">InvalidationBatch</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(220, 14%, 71%)">{</span><span>
155</span><span>      </span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">CallerReference</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token template-string template-punctuation" style="color:hsl(95, 38%, 62%)">`</span><span class="token template-string" style="color:hsl(95, 38%, 62%)">invalidate-after-s3-</span><span class="token template-string interpolation" style="color:hsl(220, 14%, 71%)">${</span><span class="token template-string interpolation" style="color:hsl(286, 60%, 67%)">new</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation" style="color:hsl(29, 54%, 61%)">Date</span><span class="token template-string interpolation" style="color:hsl(220, 14%, 71%)">(</span><span class="token template-string interpolation" style="color:hsl(220, 14%, 71%)">)</span><span class="token template-string interpolation" style="color:hsl(220, 14%, 71%)">.</span><span class="token template-string interpolation method property-access" style="color:hsl(207, 82%, 66%)">getTime</span><span class="token template-string interpolation" style="color:hsl(220, 14%, 71%)">(</span><span class="token template-string interpolation" style="color:hsl(220, 14%, 71%)">)</span><span class="token template-string interpolation" style="color:hsl(220, 14%, 71%)">}</span><span class="token template-string template-punctuation" style="color:hsl(95, 38%, 62%)">`</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span>
156</span><span>      </span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">Paths</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(220, 14%, 71%)">{</span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">Quantity</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(29, 54%, 61%)">1</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span> </span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">Items</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(220, 14%, 71%)">[</span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;/*&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">]</span><span class="token" style="color:hsl(220, 14%, 71%)">}</span><span>
157</span><span>    </span><span class="token" style="color:hsl(220, 14%, 71%)">}</span><span>
158</span><span>  </span><span class="token" style="color:hsl(220, 14%, 71%)">}</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token method property-access" style="color:hsl(207, 82%, 66%)">promise</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
159</span><span>  </span><span class="token" style="color:hsl(286, 60%, 67%)">var</span><span> codepipeline </span><span class="token" style="color:hsl(207, 82%, 66%)">=</span><span> </span><span class="token" style="color:hsl(286, 60%, 67%)">new</span><span> </span><span class="token" style="color:hsl(29, 54%, 61%)">AWS</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token" style="color:hsl(29, 54%, 61%)">CodePipeline</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
160</span><span>  </span><span class="token control-flow" style="color:hsl(286, 60%, 67%)">await</span><span> codepipeline</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token method property-access" style="color:hsl(207, 82%, 66%)">putJobSuccessResult</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(220, 14%, 71%)">{</span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">jobId</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> job_id</span><span class="token" style="color:hsl(220, 14%, 71%)">}</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">.</span><span class="token method property-access" style="color:hsl(207, 82%, 66%)">promise</span><span class="token" style="color:hsl(220, 14%, 71%)">(</span><span class="token" style="color:hsl(220, 14%, 71%)">)</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
161</span><span>  </span><span class="token control-flow" style="color:hsl(286, 60%, 67%)">return</span><span> </span><span class="token" style="color:hsl(220, 14%, 71%)">{</span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">statusCode</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(29, 54%, 61%)">200</span><span class="token" style="color:hsl(220, 14%, 71%)">,</span><span> </span><span class="token literal-property" style="color:hsl(355, 65%, 65%)">body</span><span class="token" style="color:hsl(207, 82%, 66%)">:</span><span> </span><span class="token" style="color:hsl(95, 38%, 62%)">&#x27;&#x27;</span><span class="token" style="color:hsl(220, 14%, 71%)">}</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
162</span><span></span><span class="token" style="color:hsl(220, 14%, 71%)">}</span><span class="token" style="color:hsl(220, 14%, 71%)">;</span><span>
163</span></code></pre></pre>
164<p class="mb-4 text-gray-300 leading-relaxed">I&#x27;m not going to cover all the steps in creating this Lambda. Just don&#x27;t forget to give it an execution role that Allows
165the cloudfront:CreateInvalidation and codepipeline:PutJobSuccessResult Actions. Let&#x27;s go ahead and setup a new Action
166Group on the Deploy stage of our CodePipeline and add the Lambda:</p>
167<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/edit-deploy.png" target="_blank"><img class="rounded-lg my-4" alt="Add a new action group below
168the S3 deploy" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/edit-deploy.png"/></a></p>
169<p class="mb-4 text-gray-300 leading-relaxed">For the UserParameter, pass the CloudFront Distribution ID you find over in the CloudFront console.</p>
170<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/edit-action.png" target="_blank"><img class="rounded-lg my-4" alt="Add the invalidate action" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/edit-action.png"/></a></p>
171<p class="mb-4 text-gray-300 leading-relaxed">Alright, now if you Release the CodePipeline, you should see the S3 files get deployed and then our CloudFront cache is
172invalidated.</p>
173<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invalidate.png" target="_blank"><img class="rounded-lg my-4" alt="Deploy + Invalidate" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invalidate.png"/></a></p>
174<p class="mb-4 text-gray-300 leading-relaxed">You should see a related record in the CloudFront Console:</p>
175<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invalidated.png" target="_blank"><img class="rounded-lg my-4" alt=" Invalidated!" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invalidated.png"/></a></p>
176<p class="mb-4 text-gray-300 leading-relaxed">Great! Now we have this fully deployable application when we do a git push to &#x27;master&#x27;. Try it out now, change something
177significant (and obvious) in your Ionic project and then push to your repository.</p>
178<p class="mb-4 text-gray-300 leading-relaxed"><a href="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/magic.gif" target="_blank"><img class="rounded-lg my-4" alt="CICD!" src="/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/magic.gif"/></a></p>
179<p class="mb-4 text-gray-300 leading-relaxed">As you can imagine, this process would work with any static website, since nothing we did here was specific to Ionic 4,
180other than our buildspec.yml file and some config when we setup the CodeBuild project.</p>
181<p class="mb-4 text-gray-300 leading-relaxed">Now, as you probably noticed, this was likely a little more than 5 minutes. I think if you knew the process and
182weren&#x27;t following the guide, 5 minutes could be enough time. So, yes, &quot;5 minutes&quot; was clickbait in my title. Sue me.
183But, since that&#x27;s still too long and I now have guilt, I created a <a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://www.terraform.io/">Terraform</a> module you can
184use to create all of this in just a minute. Check it out here:</p>
185<p class="mb-4 text-gray-300 leading-relaxed"><a class="text-brand-light hover:text-brand underline underline-offset-2 decoration-brand/30 hover:decoration-brand transition-colors" href="https://github.com/mbonig/simple-aws-cicd">https://github.com/mbonig/simple-aws-cicd</a></p></div></article></div></main></div><footer class="border-t border-white/[0.06]"><div class="undefined container mx-auto px-5 max-w-screen-lg"><div class="py-16 flex flex-col md:flex-row items-center justify-between gap-6"><p class="text-gray-500 text-sm font-medium">Made with <span class="text-red-400">♥</span> by Matthew Bonig</p><a href="https://github.com/mbonig" class="text-sm text-gray-500 hover:text-brand-light transition-colors duration-200">View me on GitHub</a></div></div></footer></div>
185<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"post":{"title":"Creating a CICD for your Ionic 4 app in AWS in 5 minutes","date":"2019-01-19","slug":"creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes","content":"\nIonic 4 just hit RC2, and I am playing around with a simple Task app for a friend. He wants mobile apps and a website,\nso\na perfect fit for Ionic, since it can be deployed as a static website pretty easily.\n\nCombine that with AWS's recent announcement\nof [S3 deployment support for CodePipeline](https://aws.amazon.com/about-aws/whats-new/2019/01/aws-codepipeline-now-supports-deploying-to-amazon-s3/)\nmeans we now have a very simple method for building and deploying the PWA side of the Ionic app. I intend to show you\nhow you can get it setup in about 5 minutes\\*\\*. When all done, the process will look something like this:\n\n![Basically, the CICD for an Ionic PWA](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png)\n\n1. Developer pushes code to CodeCommit/Github repository\n2. CodeCommit fires a webhook to CodePipeline\n3. CodePipeline pulls code from the repository and hands over to CodeBuild\n4. CodeBuild compiles Ionic into static files for the web and pushes to S3\n5. CloudFront updates cache from S3\n6. End users read built files from CloudFront site.\n\nBy the end of this blog, you should be able to deploy your own CodeCommit/Github repository to the public web entirely\nby CICD.\n\nWe're going to start with a basic Ionic 4 application. I assume you already have the Ionic cli installed ( npm i -g\nionic if not), so let's bootstrap a new project:\n\n```shell\n$ ionic start myApp tabs --type=angular \u0026\u0026 cd myApp\n```\n\nYou can verify the build for the web app works:\n\n```shell\n$ ionic build --prod\n```\n\nWhen done you should now see a bunch of compiled files in the ./www/ directory. These are Ionic's deployable web assets.\n\nLet's make this a process we can easily run with npm. Update your package.json, adding a new \"build:prod\" to your\nscripts config:\n\n```json\n{\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"test\": \"ng test\",\n    \"lint\": \"ng lint\",\n    \"e2e\": \"ng e2e\",\n    \"build:prod\": \"ionic build --prod\"\n  }\n}\n```\n\nNow let's add a buildspec.yml file to the root of the project. We're going to point CodeBuild at this later.\n\n```yaml\nversion: 0.2\nphases:\n  pre_build:\n    commands:\n      - npm i\n  build:\n    commands:\n      - npm run build:prod\nartifacts:\n  files:\n    - '**/*'\n  base-directory: www\n```\n\nThat's all we need to do to the code, so now push this to your repo, either AWS CodeCommit or Github.\n\nNow login to your [AWS CodePipeline Console](https://console.aws.amazon.com/codepipeline/home). We're going to create a\nnew CodePipeline:\n\n![Start creating the CodePipeline](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/pipeline-settings.png)\n\nFill it out similar to above. You can override as you see fit but the defaults should work. Click Next.\n\n![Pick your source](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/source.png)\n\nFill out the source, in this case I'm using a Github repository. Click Next. Now we setup the Build State, the CodeBuild\nstep:\n\n![Do the build](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build.png)\n\nClick Create Project.\n\nIn the new window start to fill out some basic CodeBuild info:\n\n![CodeBuild project details](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/codebuild.png)\n\nThen fill out the specifics for the build:\n\n![](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/buildspec.png)\n\nIonic is Node-based, hence using a Node Runtime. Your mileage may vary.\n\nWe'll be using a Node-based container for our build environment. You can leave everything else on the defaults:\n\n![Defaults are good here](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build-defaults.png)\n\n**If you want to log the build output - which I suggest you do but is not required - go ahead and fill out CloudWatch\nspecifics (the correct CloudWatch policies will NOT automatically be added to the related service role. You'll need\nto [](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-identity-based-access-control-cwl.html)add those\nyourself).**\n\n![Remember to add the policies later](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/logs.png)\n\nWhen you're all done, click Continue to CodePipeline. Your CodeBuild project should now be selected.\n\n![Confirm your newly created\nCodeBuild project](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/build-final.png)\n\nThe next step you will select an S3 bucket to deploy your files to. However, that screen doesn't give you the option to\ncreate the bucket the same way we got to create a CodeBuild project on the fly. So go ahead and open a new tab for\nthe [AWS S3 Console](https://s3.console.aws.amazon.com/s3/home) and create a bucket over there.\n\n![Bucket time!](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/bucket.png)\n\nYou can accept all defaults if you want, the bucket does not need to be, and shouldn't be, public.\n\nReview and Create the bucket. Now go back to your other tab, where you have setup the CodeBuild project in CodePipeline\nand hit Next.\n\n![Pick your bucket](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/deploy.png)\n\nPick your bucket and make sure you pick to 'Extract file before deploy'. Hit Next.\n\nReview all your settings one last time and then hit Create Pipeline:\n\n![Review and Create the\nCodePipeline!](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/review.png)\n\nOnce created, CodePipeline will automatically run the first build. No need to push anything to the repository.\n\nNow, if you picked to log your CodeBuild project but didn't remember to setup the permissions manually, or didn't get to\nit quick enough, you're going to see an error:\n\n```text\nACCESS_DENIED: Service role arn:aws:iam::XXXXXX:role/service-role/codebuild-ionic-cicd-build-service-role does not allow AWS CodeBuild to create Amazon CloudWatch Logs log streams for build arn:aws:codebuild:us-east-1:XXXXXX:build/ionic-cicd-build:ABCDEF....\n```\n\nEven though AWS setup the service role for you, it did not give it permissions to write CloudWatch\nlogs. [Read here](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-identity-based-access-control-cwl.html)\nif you are unsure how to do this. For my testing, I just attached\nthe [CloudWatchAgentServerPolicy](https://mbonig.wordpress.com/w
185p-admin/policy/CloudWatchAgentServerPolicy) to my\nservice role.\n\nYou should see a completed CodePipeline like this:\n\n![First run of the\nbuild!](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/codepipeline-build.png)\n\nAnd if you check your S3 bucket, you should see files similar to this:\n\n![Look at those glorious files!](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/bucket-contents.png)\n\nGreat! We're almost done. Now we just need a CloudFront distribution in front of this. Now, I know what you're\nwondering:\n\n\u003e \"But can't I just use S3 Static Website Hosting\" - me, at the start of this\n\nYou'd think, and if you didn't care about SSL, you probably could. However, any good website should have SSL and\nCloudFront also solves a few issues around routing for us, so let's continue...\n\nStart creating a new Web distribution in the [CloudFront Console](https://console.aws.amazon.com/codepipeline/home):\n\n![Pick your bucket, and setup access](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/origin.png)\n\nPick your S3 bucket as the origin domain name. We want to restrict bucket access, so that we don't have to make our S3\nbucket public. Let CloudFront take care of granting the permissions in the bucket policy. The only other default you\nshould have to change is the Default Root Object near the bottom, set that to \"index.html\".\n\nI'm not going to setup a CNAME alternative for this (for custom domain names) but you probably will want to, so read up\non it [here](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html). When\nyou're satisfied, go ahead and create the distribution.\n\nIt'll take a few minutes, but once complete you should be able to load the site at the Domain Name CloudFront generated\nfor your distribution.\n\nAnd that's generally it. You should now have a functioning website based on the Ionic starter template. Any updates you\npush to your repository will be deployed out after a few minutes of the CICD process running. However, since we're using\nCloudFront, the cache will still show the old files. That's not good. You COULD let the cache naturally expire, or\ninvalidate it yourself, but one requires waiting FAR too long to see the new code, and the other is manual. Both are\ngross.\n\nWe need a way to tell CloudFront to invalidate the files after they've been redeployed to S3. So, let's take a look at\nour options. If we go into editing the CodePipeline, let's take a look at what kind of actions we can take after the S3\npush:\n\n![I don't see anything that\nsays CloudFront](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/deploy-options.png)\n\nHmm, so nothing regarding CloudFront, bummer. What else we got?\n\n![Lambda to the rescue!](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invoke-lambda.png)\n\nAh ha, Lambda!\nThe [Swiss Army Knife](https://www.amazon.com/Victorinox-1-6795-XAVT-Swiss-Swisschamp-Pocket/dp/B000JC19FE/ref=sr_1_31_sspa?ie=UTF8\u0026keywords=swiss+army+knife\u0026link_code=qs\u0026qid=1547938319\u0026sr=8-31-spons\u0026tag=wwwcanoniccom-20\u0026psc=1)\nof AWS. Since we can call a Lambda we can easily create one that will invalidate the CloudFront cache:\n\n```javascript\nconst AWS = require('aws-sdk');\nconst cloudfront = new AWS.CloudFront();\nexports.handler = async (event) =\u003e {\n  // Extract the Job ID     \n  console.log('event:', event);\n  const job_id = event['CodePipeline.job']['id'];\n\n  // Extract the Job Data     \n  const job_data = event['CodePipeline.job']['data'];\n\n  console.log('job_data:', job_data);\n  const distribution_id = job_data.actionConfiguration.configuration.UserParameters;\n  console.log('invalidating distribution:', distribution_id);\n  await cloudfront.createInvalidation({\n    DistributionId: distribution_id,\n    InvalidationBatch: {\n      CallerReference: `invalidate-after-s3-${new Date().getTime()}`,\n      Paths: {Quantity: 1, Items: ['/*']}\n    }\n  }).promise();\n  var codepipeline = new AWS.CodePipeline();\n  await codepipeline.putJobSuccessResult({jobId: job_id}).promise();\n  return {statusCode: 200, body: ''};\n};\n```\n\nI'm not going to cover all the steps in creating this Lambda. Just don't forget to give it an execution role that Allows\nthe cloudfront:CreateInvalidation and codepipeline:PutJobSuccessResult Actions. Let's go ahead and setup a new Action\nGroup on the Deploy stage of our CodePipeline and add the Lambda:\n\n![Add a new action group below\nthe S3 deploy](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/edit-deploy.png)\n\nFor the UserParameter, pass the CloudFront Distribution ID you find over in the CloudFront console.\n\n![Add the invalidate action](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/edit-action.png)\n\nAlright, now if you Release the CodePipeline, you should see the S3 files get deployed and then our CloudFront cache is\ninvalidated.\n\n![Deploy + Invalidate](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invalidate.png)\n\nYou should see a related record in the CloudFront Console:\n\n![ Invalidated!](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/invalidated.png)\n\nGreat! 
185Now we have this fully deployable application when we do a git push to 'master'. Try it out now, change something\nsignificant (and obvious) in your Ionic project and then push to your repository.\n\n![CICD!](/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/magic.gif)\n\nAs you can imagine, this process would work with any static website, since nothing we did here was specific to Ionic 4,\nother than our buildspec.yml file and some config when we setup the CodeBuild project.\n\nNow, as you probably noticed, this was likely a little more than 5 minutes. I think if you knew the process and\nweren't following the guide, 5 minutes could be enough time. So, yes, \"5 minutes\" was clickbait in my title. Sue me.\nBut, since that's still too long and I now have guilt, I created a [Terraform](https://www.terraform.io/) module you can\nuse to create all of this in just a minute. Check it out here:\n\n[https://github.com/mbonig/simple-aws-cicd](https://github.com/mbonig/simple-aws-cicd)\n","ogImage":{"url":"/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png"},"coverImage":"/assets/blog/creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes/cicd.png","description":"Ionic 4 Websites (or any static website) can now be deployed very easily."},"playlist":[{"slug":"ai","title":"AI and the Future of Software Development"},{"slug":"batching-part-3","title":"Batch Process in AWS - Part 3 - What's the REAL solution?"},{"slug":"batching-part-2","title":"Batch Process in AWS - Part 2 - 500's Galore!"},{"slug":"batching","title":"Batch Process in AWS - An Exercise in Nuance"},{"slug":"ocr","title":"Creating an OCR pipeline with AWS Textract"},{"slug":"complex-importing-with-the-cdk","title":"Importing CloudFormation Resources With CDK L2s"},{"slug":"beavers-and-llms","title":"Beavers and LLMs"},{"slug":"community-survey-2023","title":"CDK Community Survey - 2023"},{"slug":"advanced-cdk-course","title":"Announcing my Advanced CDK Course"},{"slug":"community-survey-2022","title":"CDK Community Survey - 2022"},{"slug":"step-functions-and-the-cdk","title":"Developing Step Functions with the AWS CDK"},{"slug":"importing-with-the-cdk","title":"How to handle importing CloudFormation resources when you use the CDK"},{"slug":"community-survey-2021q1","title":"CDK Community Survey - 2021 Q1"},{"slug":"automating-construct-publishing","title":"Automating CDK Version Bumping with AWS Serverless and Github"},{"slug":"community-survey-2020q4","title":"CDK Community Survey - 2020 Q4"},{"slug":"from-amplify-to-cdk","title":"Moving from AWS Amplify to the AWS CDK"},{"slug":"community-survey-2020q3","title":"CDK Community Survey - 2020 Q3"},{"slug":"converting-to-projen","title":"Converting a CDK construct to using Projen"},{"slug":"putting-code-in-the-right-place","title":"Putting code in the right places."},{"slug":"why-the-cdk-is-taking-off","title":"Why the AWS Cloud Development Kit is taking off."},{"slug":"deno-vs-node","title":"Deno vs Node showdown!"},{"slug":"creating-constructs","title":"How to Create CDK Constructs"},{"slug":"2019-year-in-review","title":"2019 - A Year in Review"},{"slug":"cdk-the-wrong-way","title":"The Wrong Way to Use the AWS CDK."},{"slug":"dynamodb-client","title":"AWS's new NoSQL Workbench for DynamoDB is out! Is it for you?"},{"slug":"am-i-misuing-the-cdk","title":"Am I Misusing the AWS Cloud Development Kit (CDK)?"},{"slug":"simple-es-api","title":"A Simple Event Source Based API Microservice and the AWS CDK"},{"slug":"dynamodb-pros-cons","title":"The Pros and Cons of DynamoDB"},{"slug":"idempotent-lambdas","title":"Creating Idempotent Lambda Handlers with DynamoDB"},{"slug":"a-typescript-runtime-for-lambda-and-why-you-may-not-want-to-use-it","title":"A Typescript Runtime for Lambda and Why You May Not Want To Use It"},{"slug":"creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes","title":"Creating a CICD for your Ionic 4 app in AWS in 5 minutes"},{"slug":"turning-a-crud-model-into-an-event-sourced-model","title":"Turning a CRUD Model into an Event Sourced Model"},{"slug":"cicd-in-aws-for-barracuda","title":"CICD in AWS for Barracuda"},{"slug":"aws-verifying-outgoing-ses-email-addresses-without-an-email-server","title":"AWS – Verifying Outgoing SES Email Addresses Without an Email Server"}]},"__N_SSG":true},"page":"/posts/[slug]","query":{"slug":"creating-a-cicd-for-your-ionic-4-app-in-aws-in-5-minutes"},"buildId":"hyZ6GYxnkw3oJ7wBfe284","isFallback":false,"gsp":true,"scriptLoader":[]}</script>
185</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.