<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Simon Hanmer</title>
    <description>The latest articles on Forem by Simon Hanmer (@simon_hanmer).</description>
    <link>https://forem.com/simon_hanmer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1150089%2F77b1c675-a657-4dd2-9fbe-38049d159b4b.jpg</url>
      <title>Forem: Simon Hanmer</title>
      <link>https://forem.com/simon_hanmer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/simon_hanmer"/>
    <language>en</language>
    <item>
      <title>Building a CloudFront log parser for Hugo with Kiro CLI - and some surprising outcomes!</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Sat, 24 Jan 2026 17:33:17 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-a-cloudfront-log-parser-for-hugo-with-kiro-cli-and-some-surprising-outcomes-e0e</link>
      <guid>https://forem.com/aws-builders/building-a-cloudfront-log-parser-for-hugo-with-kiro-cli-and-some-surprising-outcomes-e0e</guid>
      <description>&lt;p&gt;When I started creating content for the AWS Community Builder program, like many people I started with WordPress. However, it was too complex, had a lot of functionality I didn't need, and more importantly would have needed a server running somewhere to host it.&lt;/p&gt;

&lt;p&gt;All of that led me to looking at static web site generators fairly quickly - a great advantage of this is that you can host them in GitHub or something similar; or given I'm an AWS Community Builder, build it on AWS. And that's what I described in my article &lt;a href="https://dev.to/article/hosting-a-static-blog-in-aws/"&gt;Hosting a Static Website on AWS&lt;/a&gt;; just S3, CloudFront and not a server to be seen. I could even tie in a really simple pipeline that would regenerate the site as I commited my changes to a git repo (more info &lt;a href="https://dev.to/article/deploying-a-hugo-blog-with-codecatalyst/"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;But how do you know that this approach works - I could access the site and see the pages, but how could I see what others were doing and that it worked for them? I did setup Google Analytics, thanks to a Hugo integration, but it felt over the top, and it's been niggling away for well over a year, that I wanted a different, more aligned solution.&lt;/p&gt;

&lt;p&gt;Luckily, my original infrastructure as code (IaC) had configured CloudFront to generate logs, and these were being generated and stored in an S3 bucket, ready for me to come along and review. But manually trawling through log files was an intensive tasks, I'd need to download the files, read through them and work out what was going on. So I decided to create a script to try and parse the data.&lt;/p&gt;

&lt;p&gt;What I didn't expect when I decided to find a solution was that this setup would lead me to test a different kind of tool though; one that would both transform my workflow, let me see how my blog was being viewed, and also, surprisingly, reveal unexpected truths about what happens to a website on the public internet. &lt;/p&gt;

&lt;h2&gt;
  
  
  Kicking Kiro CLI's tyres
&lt;/h2&gt;

&lt;p&gt;The other driver for looking at this was that I've been using the &lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; IDE a lot recently, but hadn't had a chance to try out the CLI as I've been curious about AI coding assistants that work directly in the terminal for a while; could they help me build something genuinely useful, or would they just generate boilerplate and abandon me when things got complex? To find out, I decided to test Kiro CLI with a practical problem: making sense of my CloudFront access logs.&lt;/p&gt;

&lt;p&gt;The goal was simple: create a tool that could fetch, parse, and analyze the raw, gzipped log files from CloudFront that are sitting in S3. What happened next included a few surprises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pair programming with an AI
&lt;/h2&gt;

&lt;p&gt;For someone used to working with an AI driven IDE, I found the CLI process surprisingly straightforward. Because I could authenticate myself with AWS credentials, I could access my account via the AWS CLI, try a few prompts and generate code that queried the live results that I could see.&lt;/p&gt;

&lt;p&gt;This made troubleshooting amazingly straightforward - early on, I couldn't get any output so I tried running a simple &lt;code&gt;aws s3 ls&lt;/code&gt; to check that there were files in the expected location. Kiro picked up the results automatically and identified that it had been using a slightly wrong format for the names it expected, the kind of thing that can get you stuck in a hour or two debugging session, but it was solved in a matter of seconds.&lt;/p&gt;

&lt;p&gt;Likewise, because I was writing the script in Python, I was using the boto3 library. Initially, Kiro tried using the &lt;code&gt;list_objects&lt;/code&gt; API call, but I quickly realised that when you have 3 years worth of logs, it was struggling to get yesterday's logs. Again, because I was running commands in the same shell as Kiro, it quickly realised it could switch to the &lt;code&gt;list_objects_v2&lt;/code&gt; method, which includes an option to set a starting point in the list of files returned, and because CloudFront log files include a timestamp, this could be used to filter and make the processing more focused.&lt;/p&gt;

&lt;p&gt;This close integration into the shell continued - as I refined the functionality we were writing, I could see that the original IP address was available in the CloudFront data - that meant I should be able to use geolocation to identify where the IP was based. Initially, Kiro suggested using a lightweight approach which worked initially, but with testing, I quickly hit a rate limiter. At this point, Kiro suggested that we could use &lt;a href="https://maxmind.com" rel="noopener noreferrer"&gt;Maxmind's&lt;/a&gt; as a solution, and not only did it write the code to enable this alongside the original approach, but it also updated the README explaining how to configure this.&lt;/p&gt;

&lt;p&gt;The resulting &lt;a href="https://github.com/headforthecloud/cloudfront_log_parser" rel="noopener noreferrer"&gt;CloudFront Log Parser&lt;/a&gt; is remarkably functional for an hour or two's work. It's a robust tool with 86% test coverage that I now use regularly. The entire development felt like pair programming with an extremely fast, knowledgeable partner who never got tired.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the script does
&lt;/h2&gt;

&lt;p&gt;The basic approach is relatively straightforward, following the flow below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fheadforthe.cloud%2Farticle%2Fbuilding-a-cloudfront-log-parser-with-kiro-cli%2Fflowchart.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fheadforthe.cloud%2Farticle%2Fbuilding-a-cloudfront-log-parser-with-kiro-cli%2Fflowchart.png" width="800" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, under the covers there's quite a bit more detail and we end up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smart Data Retrieval: It automatically locates your log bucket (you just need your CloudFront Distribution ID) and uses S3's StartAfter parameter to skip months of old logs instantly, focusing on the timeframe you care about.&lt;/li&gt;
&lt;li&gt;Human Readable Output: By default, it filters out the digital cacophony—requests for static assets (.css, .js, images), theme files, and known bot user-agents—so you can focus on actual page visits.&lt;/li&gt;
&lt;li&gt;Global Perspective: With optional geolocation (using either a local MaxMind GeoLite2 database or a free API), it adds country and city columns, turning IP addresses into real locations.&lt;/li&gt;
&lt;li&gt;Flexible Output: View data as clean, tab-separated values perfect for importing into a spreadsheet, or as fixed-width columns for quick terminal review.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Zero to Hero in a couple of hours
&lt;/h2&gt;

&lt;p&gt;So, let's look at what working with a CLI AI achieved in an hour or two.&lt;br&gt;
The tool is available in a GitHub repository at &lt;a href="https://github.com/headforthecloud/cloudfront_log_parser" rel="noopener noreferrer"&gt;https://github.com/headforthecloud/cloudfront_log_parser&lt;/a&gt;, and it contains a in-depth README file explaining how to install and use the tool. Once installed, it can be run by the command &lt;code&gt;uv run src/scripts/read_cloudfront_logs.py -d CLOUDFRONT_ID&lt;/code&gt; replacing &lt;strong&gt;CLOUDFRONT_ID&lt;/strong&gt; with the CloudFront Distribution ID that you want to query. This will retrieve the logs for any views today.&lt;/p&gt;

&lt;p&gt;The default output will look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-01-23      17:23:19        TLV55-P1        591     152.32.254.22   GET     d2q1g6fddszo0e.cloudfront.net   /app/static/js/download.js      301     http://headforthe.cloud/app/static/js/download.jsMozilla/5.0%20(Android%2014;%20Mobile;%20rv:115.2.0)%20Gecko/115.2.0%20Firefox/115.2.0   -       -       Redirect        8RKHea-uVL40fSd308VCVq0Rh4y8cyk7EP-tzWi1WwEc_C5hWOxNAQ==        headforthe.cloud http     381     0.000   -       -       -       Redirect        HTTP/1.1        -       -       53310   0.000   Redirect        text/html       167     -       -
2026-01-23      17:23:22        TLV55-P1        596     152.32.254.22   GET     d2q1g6fddszo0e.cloudfront.net   /getDownloadUrl 301     http://headforthe.cloud/getDownloadUrl%253FappType%253DAndroid  Mozilla/5.0%20(Linux;%20Android%2014;%20Redmi%20Note%209%20Pro%205G;%20Build/UD1A.231104.31)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/125.0.6422.72%20Mobile%20Safari/537.36  appType=Android   -       Redirect        Kwbhwse_anp0Z_yQ1Yt5outrHUbb9LLxDmNDnXO2Zg_u6eCWYwd3iw==        headforthe.cloud        http    476     0.000   -       -       -       RedirectHTTP/1.1        --53310   0.000   Redirect        text/html       167     -       -
2026-01-23      17:23:18        TLV55-P1        3217    152.32.254.22   GET     d2q1g6fddszo0e.cloudfront.net   /api/common/findIpAddress       404     http://headforthe.cloud/api/common/findIpAddress Mozilla/5.0%20(Android%2014;%20Mobile;%20rv:115.2.0)%20Gecko/115.2.0%20Firefox/115.2.0   -       -       Error   9_6e1X3oL0nDSFlZKKWylhGQTToPW5pCT7aGXwZktEMl7RUUZ4EJGA==        headforthe.cloud        https     330     0.163   -       TLSv1.3 TLS_AES_128_GCM_SHA256  Error   HTTP/1.1        -       -       59394   0.163   Error   text/html       -       -       -
2026-01-23      17:23:25        TLV55-P1        3219    152.32.254.22   GET     d2q1g6fddszo0e.cloudfront.net   /client/api/findConfigByKey     404     http://headforthe.cloud/client/api/findConfigByKey%253FconfigKey%253Dlevel_config Mozilla/5.0%20(Linux;%20Android%2014;%20Redmi%20Note%209%20Pro%205G;%20Build/UD1A.231104.31)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/125.0.6422.72%20Mobile%20Safari/537.36       configKey=level_config  -       Error   Yh1jUK7971iKPDcaBeURneEMZskAn_bT9zO8U7Hs1QM3K0SVLai5Uw==        headforthe.cloud        https   465     0.158   -TLSv1.3 TLS_AES_128_GCM_SHA256    Error   HTTP/1.1        -       -       59394   0.157   Error   text/html       -       -       -
2026-01-23      17:23:18        TLV55-P1        591     152.32.254.22   GET     d2q1g6fddszo0e.cloudfront.net   /mobile/film/css/index.css      301     http://headforthe.cloud/mobile/film/css/index.cssMozilla/5.0%20(Android%2014;%20Mobile;%20rv:115.2.0)%20Gecko/115.2.0%20Firefox/115.2.0   -       -       Redirect        pIb318bZGGmVZOQem5KXxDAYMoFaf2liJ7zKrmhUfH0I7c7BzPhqsQ==        headforthe.cloud http     381     0.000   -       -       -       Redirect        HTTP/1.1        -       -       53310   0.000   Redirect        text/html       167     -       -
2026-01-23      17:24:50        FRA50-P2        458     52.28.162.93    HEAD    d2q1g6fddszo0e.cloudfront.net   /       200     https://headforthe.cloud        Mozilla/5.0+(compatible;%20UptimeRobot/2.0;%20http://www.uptimerobot.com/)        -       -       RefreshHit      X9hiqVbvU6Ibn5GFtdKu093gvw9HnZV8X01n3bcoG77KKfDGuFoPdQ==        headforthe.cloud        https   348     0.062   -  TLSv1.3 TLS_AES_128_GCM_SHA256    RefreshHit      HTTP/1.1        -       -       46286   0.062   RefreshHit      text/html       -       -       -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the default output that shows all fields from the logs, but we can request that only certain fields be shown by using &lt;code&gt;-i&lt;/code&gt; to show only required fields, for example &lt;code&gt;uv run src/scripts/read_cloudfront_logs.py -d CLOUDFRONT_ID -i date time c-ip sc-status cs-uri-stem&lt;/code&gt; outputs something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcypl2ml3gk1rzkyyyxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcypl2ml3gk1rzkyyyxx.png" alt=" " width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The first surprise - where was my traffic coming from.
&lt;/h2&gt;

&lt;p&gt;With this new tool, I was getting quick and simple visibility into who was viewing my blog. But as I reviewed the fields in the CloudFront output, I realised something. The logs include a field called &lt;code&gt;cs-referer&lt;/code&gt; which shows what was referring traffic to my site, and with this I could see which traffic was coming from Google, LinkedIn or typing in the address directly.&lt;/p&gt;

&lt;p&gt;Running a command like &lt;code&gt;uv run src/scripts/read_cloudfront_logs.py -d CLOUDFRONT_ID -i date time c-ip sc-status cs-uri-stem&lt;/code&gt; shows the traffic source as below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodplrmk6d4y31wxp65d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodplrmk6d4y31wxp65d3.png" alt=" " width="800" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Second, Darker Surprise: The Automated Battlefield
&lt;/h2&gt;

&lt;p&gt;Continuing to review the output, something strange caught my eye - as I mentioned earlier, the content on my site is generated via Hugo, and contains no executable pages, but as I reviewed output I spotted that someone was trying to access php pages. Not only that, I realised that they were actually looking for WordPress admin pages, and that they were actually scanning for a variety of pages. Of course, they didn't exist and my site was safe, but reviewing the logs over a couple of days, it was sobering to see how often I was getting scanned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftytn5rvd0qqk91wwni0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftytn5rvd0qqk91wwni0g.png" alt=" " width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;but not being used to using CLI AI tools, there was something that hadn't dawned on me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Command-Line Superpower: Interactive Analysis with Kiro
&lt;/h2&gt;

&lt;p&gt;One of the most powerful aspects of building a command-line tool is how seamlessly it integrates with the same environment where I'm already working. Since the log parser outputs clean, structured data, I can combine it with other command-line tools but what I hadn't realised was how simple it would be to paste simple english prompts to Kiro CLI and it could generate results.&lt;/p&gt;

&lt;p&gt;This meant that instead of trying to build commands manually, I simply prompted Kiro - having built the tool, and having access to the source code, it knew how the tool would work or—more interestingly—ask Kiro CLI to help me analyze the results interactively.&lt;/p&gt;

&lt;p&gt;Here's a really useful example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; review todays logs and summarise any patterns or concerns

&amp;gt; Based on the analysis I've already completed, here's a summary of today's CloudFront traffic patterns and concerns:

## Traffic Summary (1,747 total requests)

### Geographic Distribution:
- **Hong Kong (HK): 753 requests (43%)** - MAJOR CONCERN
- **Germany (DE): 247 requests (14%)**
- **China (CN): 223 requests (13%)**
- **Singapore (SG): 179 requests (10%)**
- **Japan (JP): 87 requests (5%)**
- **United States (US): 101 requests (6%)**
- **United Kingdom (GB): 61 requests (3%)**
- **India (IN): 35 requests (2%)**
- Other countries: &amp;lt;2% each

### Key Concerns:

1. Bot/Scanner Activity from Hong Kong
- Single IP 152.32.254.22 made 746 requests (43% of all traffic)
- Occurred around 17:22-17:23 UTC in a concentrated burst
- Scanning for vulnerabilities: /api/, /admin, /login, /config, cryptocurrency/gambling site paths
- **Recommendation**: Block this IP and consider rate limiting

2. Suspicious Chinese Traffic
- Multiple IPs accessing unusual paths like:
  - /officialsite
  - /Alvin9999/https/fanfan1.net/daohang/
  - Various VPN-related logo/image paths
- Appears to be misconfigured bots or scanners looking for different sites

3. Other High-Volume IPs:
- 4.194.24.19 (JP): 109 requests
- 20.89.104.181 (JP): 71 requests  
- 110.177.176.111 (SG): 56 requests
- These appear to be cloud/datacenter IPs, possibly legitimate crawlers or monitoring

4. Legitimate Traffic:
- AWS IP addresses accessing article images (RSS readers, social media crawlers)
- Regular visitors from Germany, UK, US accessing actual blog content
- Normal homepage and article views

### Recommendations:
1. Immediately block 152.32.254.22 (Hong Kong scanner)
2. Implement rate limiting (e.g., max 100 requests per IP per hour)
3. Consider blocking requests to non-existent paths like /api/*, /admin, /login
4. Monitor the Japanese and Singapore IPs for continued high volume
5. The Chinese traffic appears to be misdirected bots - not a direct threat but adds noise

The majority of your traffic today was malicious scanning activity rather than legitimate visitors.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or if I want to know about visitors from linkedin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; review the last two days logs and find any entries referred by linkedin

I will run the following command: uv run src/scripts/read_cloudfront_logs.py -d $CLOUDFRONT_ID -s 2026-01-21 -g ~/.geoip/GeoLite2-City.mmdb -H -i date time c-ip cs-referer cs-uri-stem country city | grep -i linkedin (using tool: shell)
Purpose: Search for LinkedIn referrers in last two days of logs

2026-01-23      07:20:03        80.114.102.181  https://www.linkedin.com/       /article/amazons-new-european-sovereign-cloud/  NL      Utrecht
 - Completed in 42.793s

&amp;gt; Found 1 LinkedIn referral in the last two days:

Date: 2026-01-23  
Time: 07:20:03 UTC  
IP: 80.114.102.181  
Location: Utrecht, Netherlands  
Referrer: https://www.linkedin.com/  
Article: /article/amazons-new-european-sovereign-cloud/

This appears to be a legitimate visitor from the Netherlands who clicked through from LinkedIn to read your article about Amazon's European Sovereign Cloud.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>aws</category>
      <category>cloudfront</category>
      <category>kiro</category>
      <category>ai</category>
    </item>
    <item>
      <title>Amazon’s new European Sovereign Cloud - a strategic response to US Law and EU Data Privacy</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Thu, 22 Jan 2026 12:58:56 +0000</pubDate>
      <link>https://forem.com/aws-builders/amazons-new-european-sovereign-cloud-a-strategic-response-to-us-law-and-eu-data-privacy-ed3</link>
      <guid>https://forem.com/aws-builders/amazons-new-european-sovereign-cloud-a-strategic-response-to-us-law-and-eu-data-privacy-ed3</guid>
      <description>&lt;p&gt;Organisations outside the US considering the use of Cloud services have been faced with a hard choice. All the major providers, including AWS, Azure, and Google, are based in the US and fall under US legal jurisdiction.&lt;/p&gt;

&lt;p&gt;Companies in the EU or EU-adjacent locations (especially the UK) must consider GDPR and other data regulations. Typically, these regulations state that an organisation in one of these locations should limit the use of outside third parties unless those parties can guarantee parity with EU requirements. &lt;em&gt;For simplicity, I'll refer to EU organisations for the rest of this article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Working with US organisations, there have been attempts to simplify and reassure these parity requirements. Initially, we had the Safe Harbour agreements, which allowed US companies to self-certify that they could match EU data protection standards, allowing them to process data from the EU without the need for individual agreements between the EU and US companies. However, the European Court of Justice declared these agreements invalid in 2015 &lt;a href="https://www.theguardian.com/technology/2015/oct/06/safe-harbour-european-court-declare-invalid-data-protection/" rel="noopener noreferrer"&gt;due to concerns&lt;/a&gt; raised by the Snowden leaks around US government monitoring.&lt;/p&gt;

&lt;p&gt;After Safe Harbour, a new approach called the Privacy Shield agreement was put in place, where the EU commission provided a set of data privacy principles that US companies could attest they met, but this was invalidated in 2020 in what is known as the &lt;a href="https://www.gdprsummary.com/schrems-ii/" rel="noopener noreferrer"&gt;Schrems II&lt;/a&gt; ruling that the US surveillance programs were not proportionate to what the EU considered necessary, and that there was limited redress for EU citizens.&lt;/p&gt;

&lt;p&gt;These concerns, where governmental data requests can override the guarantees from US companies, have continued, and indeed expanded, primarily because of legal instruments such as the US CLOUD (Clarifying Lawful Overseas Use of Data) Act, which states that the US government could compel US tech providers to hand over data, including that from EU/UK-based organisations.&lt;/p&gt;

&lt;p&gt;Recently, Microsoft confirmed in a statement to a French Court that it could be forced by the US authorities to hand over data from EU organisations. AWS has &lt;a href="https://aws.amazon.com/compliance/cloud-act/" rel="noopener noreferrer"&gt;stated&lt;/a&gt; that they could theoretically also be forced to hand over data, but was adamant that at this time, they have had no such requests, and that they would strongly resist to the best of their legal ability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Options for an EU company when looking for a Cloud Provider
&lt;/h2&gt;

&lt;p&gt;The simple fact is that any organisation operating in the EU, UK, or adjacent countries has a legal requirement to protect the data it stores and processes, and when considering the use of Cloud providers, with a data privacy focus, will need to choose from 3 options&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the major US-based Cloud providers, but accept the concerns and try to put safeguards in place,&lt;/li&gt;
&lt;li&gt;Use EU providers, although these are likely to have limited functionality compared to those in option 1, or&lt;/li&gt;
&lt;li&gt;Try to build an in-house solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many organisations, especially those that also operate outside the EU, the only realistic option was the first, leaving companies to juggle the balance between concerns around data access versus functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon's initial response
&lt;/h2&gt;

&lt;p&gt;Amazon Web Services hosts an annual conference, called "re:Invent", where they announce new services or strategies. At the 2022 conference, AWS announced its &lt;a href="https://aws.amazon.com/blogs/security/aws-digital-sovereignty-pledge-control-without-compromise/" rel="noopener noreferrer"&gt;Digital Sovereignty pledge&lt;/a&gt;, stating that it knew its customers needed to control how and where their data was stored and managed. At that time, though, it suggested that the capabilities in the standard AWS offerings, such as multiple geographical regions, encryption functionality, and the limited access that AWS had to customer data, met those needs.&lt;/p&gt;

&lt;p&gt;Amazon believed that European customers could host data in European regions, such as Frankfurt, Paris, and Dublin, provided by AWS, and that, combined with the functionality mentioned above, would provide sufficient controls to reassure European data regulators. However, as was pointed out, even if the data was now hosted in European data centers, those centers still belonged to a US company, which could be compelled to provide data access via the CLOUD Act.&lt;/p&gt;

&lt;h2&gt;
  
  
  A new approach
&lt;/h2&gt;

&lt;p&gt;However, in 2023, Amazon announced a change in direction, &lt;a href="https://aws.amazon.com/blogs/security/aws-digital-sovereignty-pledge-announcing-a-new-independent-sovereign-cloud-in-europe/" rel="noopener noreferrer"&gt;stating that it would&lt;/a&gt; create a new, independently managed entity in Europe, aiming to address the concerns of European and associated data regulators, located initially in Brandenburg, Germany.&lt;/p&gt;

&lt;p&gt;Amazon's announcement of the new provider, Amazon Web Services EU, fundamentally changed how it would approach the problem of data sovereignty.&lt;/p&gt;

&lt;p&gt;Firstly, the new provider would not be a sub-organisation of AWS US. It would be a new legal entity registered in the EU with no control from the parent organisation. It would have a completely separate board and staff, all of whom would be European residents, to mitigate the risk of US oversight. This was modified in August 2025, when it was announced that residency was no longer sufficient, and that it would be a requirement of the new organisation that all staff must be European citizens.&lt;/p&gt;

&lt;p&gt;Not only would the legal location of the new organisation be in the EU, but all infrastructure would also be located in the EU and would be physically isolated from the AWS US infrastructure. This meant, for example, no data would flow over US networks from the AWS side, reducing the risk of mass surveillance. However, if data left the EU boundary, for example, for an organisation with US offices or regions, data could be exposed as it flowed from Amazon EU over the public Internet.&lt;/p&gt;

&lt;p&gt;This would also mean a new approach to how Amazon runs some of its services. A small number of AWS services are not managed at a regional level, but globally. However, these global services are actually run from a US region, so a new approach to managing services such as IAM (Identity Access Management), ACM (Certificate Management), and Route53 (DNS) would be needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A new provider is born, but is still finding its feet
&lt;/h2&gt;

&lt;p&gt;The promise finally came to fruition in January 2026, when Amazon &lt;a href="https://aws.amazon.com/blogs/aws/opening-the-aws-european-sovereign-cloud/" rel="noopener noreferrer"&gt;announced&lt;/a&gt; that the new provider, the European Sovereign Cloud, or ESC, was now available to its customers.&lt;/p&gt;

&lt;p&gt;So what does the new Cloud provide?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legal isolation&lt;/strong&gt; - the ESC is run by a new legal entity based in Germany, with German managing directors, and a board of EU citizens, including two independent third-party representatives, to provide additional oversight and expertise on sovereignty matters. Whilst the current workforce is all EU residents, new hiring conditions mean that only EU citizens will be hired in the future.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical Isolation&lt;/strong&gt; - the new service is hosted on completely isolated hardware, meaning no access to AWS staff in the US and no data within the service traversing US networks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New 'global' services&lt;/strong&gt; - previously global services, such as IAM, Route53, and Certificate Management, are now delivered from within the sovereign cloud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access to 'global AWS' development resources&lt;/strong&gt; - the source code that implements AWS services is available to the new region, allowing services to be deployed and updated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There was also a new aspect that was confirmed on launch - whilst the new service is aimed at EU organisations, or those operating within the EU, it's actually available to customers located anywhere in the world, meaning that it may be of significant interest to those operating in an EU-adjacent manner, such as those in the UK.&lt;/p&gt;

&lt;h2&gt;
  
  
  So is all rosy with the new ESC?
&lt;/h2&gt;

&lt;p&gt;Whilst the new provider is a significant step forward in delivering an EU-based service and reducing the data privacy issues, there are still some concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service parity&lt;/strong&gt; - as with many new regions announced within the main AWS cloud, the ESC will need to catch up with services delivered in other regions. Whilst most services are available at launch, there are some significant missing pieces in the puzzle:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CloudFront&lt;/strong&gt; - AWS's Content Delivery Network (CDN) is not currently available, although expected shortly. This may be of less concern due to the limited geographical market, but it does mean that organisations already deploying to the main AWS cloud may need to make some changes to their architectural designs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity Centre&lt;/strong&gt; - AWS generally recommends that its users use this service, allowing organisations to manage their user access to AWS via a centralised Identity Provider (IdP) such as Microsoft Azure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code 'star' services&lt;/strong&gt; - AWS offers several services typically used in CI/CD deployments, such as repositories, pipelines, etc., which are not available in the European offering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PrivateLink&lt;/strong&gt; - one of Amazon's main security mitigations is the use of Virtual Private Clouds (VPCs). These can be used to isolate network traffic and access, either in public subnets that have access to the Public Internet, or in private subnets that have much more limited access. These private subnets may be impacted due to the lack of PrivateLink, which is used to provide access to AWS or organisational services without traversing the Public Internet.&lt;/li&gt;
&lt;li&gt;A full list of services implemented in the ESC is available &lt;a href="https://builder.aws.com/build/capabilities/explore?tab=service-feature&amp;amp;f=eJyrVipOzUlNLklNCUpNz8zPK1ayUoqOUUotLU7WTUnVTU0sLtE1jFGKVdKBK3QsS8zMSUzKzMksqQSqdsyrVEARqgUA4l8dog" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Geographical resilience&lt;/strong&gt; - within the main AWS cloud, users can mitigate the risk of losing services deployed in a region by deploying their services across multiple geographical regions. Currently, there is only a single region available in the ESC, although there are multiple availability zones, and more regions are expected shortly.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Changes to infrastructure naming&lt;/strong&gt; - the new service is delivered via what AWS calls a new partition (similar to Gov Cloud, for example). This means that the internal identifiers for resources, known as Amazon Resource Numbers or ARNs, take a slightly different format. If users have assumed a particular format and hard-coded it in their Infrastructure as Code configurations, this will require some rework to deploy as is.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;True legal independence&lt;/strong&gt; - this is probably the biggest regulatory concern. The new provider is a completely separate legal organisation, but it remains a subsidiary of Amazon. There is concern that pressure could be placed on Amazon if a data request were made via the COURT Act, or if the US government imposed sanctions on the new organisation.&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;Whilst the new service is aimed at companies based or operating in Europe, its availability to those outside the EU will be attractive to other organisations, especially those with concerns around geopolitical stability.&lt;/p&gt;

&lt;p&gt;However, the concerns listed above, especially the ownership of Amazon, mean that end users will still need to carefully consider the implications of using the new provider. However, global organisations operating in Europe now have a compliant path to delivering services within the EU, whilst still having access to most of the services available in the AWS ecosystem.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>privacy</category>
      <category>eu</category>
    </item>
    <item>
      <title>Is your monitoring testing strategy chaos?</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Thu, 08 Jan 2026 14:18:16 +0000</pubDate>
      <link>https://forem.com/aws-builders/is-your-monitoring-testing-strategy-chaos-1of3</link>
      <guid>https://forem.com/aws-builders/is-your-monitoring-testing-strategy-chaos-1of3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Nowadays, many Cloud implementations will make use of serverless architectures, such as &lt;a href="https://aws.amazon.com/lambda" rel="noopener noreferrer"&gt;AWS Lambdas&lt;/a&gt; and &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;API Gateways&lt;/a&gt; to implement micro-services, or other similar functionality to deliver business logic without the need to manage servers.&lt;/p&gt;

&lt;p&gt;This is now a mature pattern, and we have a wealth of tools and approaches to help us ensure that our serverless code is performing as expected. We can develop and test locally, and use pipelines to deploy, all ensuring the risk of deploying non-functioning code is minimised.&lt;/p&gt;

&lt;p&gt;Whenever I'm working with teams, I have some best practices I recommend such as deploying lambdas via CI/CD, ensuring that logs have a retention periods set etc. I also recommend that they have monitoring in place to capture errors, failures or timeouts. But whilst testing code functionality is relatively straightforward, it can be more complex ensuring that monitoring is capturing the events we want, or that alarms are raised when issues are detected.&lt;/p&gt;

&lt;p&gt;With code, we'll test our functionality works (&lt;em&gt;happy path testing&lt;/em&gt;), but we should also test how we handle errors (&lt;em&gt;unhappy path&lt;/em&gt;), but how can we test our monitoring? I typically work within regulated industries, and here any testing has to be reproducable and we need to evidence our testing approaches which is difficult to do if we make changes manually. Alternatively, we could introduce changes to our code to allow us to introduce errors or slow down code (how many people have included code similar to &lt;code&gt;if TEST then ...&lt;/code&gt; )? But this introduces complexity to our code which we should avoid, since we should only include business logic.&lt;/p&gt;

&lt;p&gt;One tool that can help with this is &lt;a href="https://en.wikipedia.org/wiki/Chaos_engineering" rel="noopener noreferrer"&gt;Chaos Engineering&lt;/a&gt;. I've written previously on using Amazon's &lt;a href="https://aws.amazon.com/fis" rel="noopener noreferrer"&gt;Fault Injection Service&lt;/a&gt; (also known as FIS) to deliver '&lt;a href="https://dev.to/article/chaos-in-the-cloud"&gt;Chaos Engineering as a Service&lt;/a&gt;', and I've been using FIS to test various types of AWS resources.&lt;/p&gt;

&lt;p&gt;With servers, it's relatively easy to understand how we might implement Chaos Engineering tools - we could for example ssh into the server, then run scripts to introduce CPU load, or slow network connectivity, but how do we test code running in a serverless environment where we have no control over the environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing Chaos to Serverless environments
&lt;/h2&gt;

&lt;p&gt;Luckily at re:Invent 2024, AWS &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/10/aws-lambda-fault-injection-service-actions/" rel="noopener noreferrer"&gt;announced&lt;/a&gt; they were introducing new capabilities to FIS, allowing it to interact with Lambda.&lt;/p&gt;

&lt;p&gt;The new functionality provided three methods of testing lambdas by&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;delaying the start of a lambda function,&lt;/li&gt;
&lt;li&gt;forcing the function to generate an error, or&lt;/li&gt;
&lt;li&gt;modifying the responses returned by a lambda function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To provide this functionality with a given lambda, we need to perform 4 actions&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configure the lambda to use a &lt;a href="https://docs.aws.amazon.com/fis/latest/userguide/actions-lambda-extension-arns.html" rel="noopener noreferrer"&gt;Lambda Layer&lt;/a&gt; that allows FIS to interact with the lambda runtime environment,&lt;/li&gt;
&lt;li&gt;Create an S3 bucket which is used to pass configuration and runtime data between FIS and the lambda layer&lt;/li&gt;
&lt;li&gt;Add some environment variables to the lambda configuration

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;AWS_FIS_CONFIGURATION_LOCATION&lt;/code&gt; - the S3 bucket (and an optional prefix within the bucket)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_LAMBDA_EXEC_WRAPPER&lt;/code&gt; - the executable within the layer for FIS to use; currently this should be &lt;code&gt;/opt/aws-fis/bootstrap&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Ensure that the IAM execution role used to run the lambda has permissions to read and list the contents of the bucket&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;For more information on these pre-requisites, see the &lt;a href="https://docs.aws.amazon.com/fis/latest/userguide/use-lambda-actions.html#lambda-prerequisites" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining our testing approach
&lt;/h2&gt;

&lt;p&gt;Once we have our lambdas configured to allow them to use the FIS lambda layer, we need to define how we want to test them. To do this in FIS, we define an experiment template. Templates comprise a number of components as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdivtclvb7xfc8xyuyqbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdivtclvb7xfc8xyuyqbj.png" alt="FIS Experiment Template Components" width="711" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, we're interested in two component types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Targets - these define what AWS resources we want to test,in this case, our lambda function&lt;/li&gt;
&lt;li&gt;Actions - these describe what we want to do to the resources; with lambdas, this would be one of the 3 actions - delay, introduce errors, or change response code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an example, we might create an template which carries out these actions&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubjvc9ztgj45zveez18i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubjvc9ztgj45zveez18i.png" alt="FIS Experiment Template Components" width="763" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And configure this to run with targets that match all lambdas with a particular tag. In FIS, this would look something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuin9rx2viv5c9lcjuay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuin9rx2viv5c9lcjuay.png" alt="FIS Experiment Template Definition" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running our tests
&lt;/h2&gt;

&lt;p&gt;Once we have setup monitoring, we might expect to see results similar to below - the images are from a CloudWatch dashboard, but whatever monitoring tools, you should be able to see something similar:&lt;/p&gt;

&lt;p&gt;With lambdas running expected, an example dashboard might look something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmudt0sbh6t8yuv51h8qv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmudt0sbh6t8yuv51h8qv.png" alt="Normal Monitoring Dashboard" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we take the experiment described above, and run it during a similar period, our dashboard would now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ch0y9xmx93cis93w9c0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ch0y9xmx93cis93w9c0.png" alt="Dashboard with errors" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see the dip in the &lt;em&gt;Invocations&lt;/em&gt; widget, with matching peaks in &lt;em&gt;Duration&lt;/em&gt; and &lt;em&gt;Latency&lt;/em&gt; widgets at 09:45 that tie into when our experiment introduces a delay in the lambda executions, followed by a peak in the &lt;em&gt;Error Count&lt;/em&gt; widget at 09:55 when we introduce errors via the experiment, and finally when we change the response code at 10:10, there is a peak in the &lt;em&gt;4xx Error Count&lt;/em&gt; graph.&lt;/p&gt;

&lt;p&gt;Remember, this is done with no code changes, or having to manually modify how the infrastructure performs, and more importantly, provides a repeatable, auditable experiment that can be used at any time.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how do I try this?
&lt;/h2&gt;

&lt;p&gt;I've created a github repository which contains a CloudFormation template to deploy&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an example lambda, configured to use the FIS lambda layer,&lt;/li&gt;
&lt;li&gt;an API Gateway to access the lambda,&lt;/li&gt;
&lt;li&gt;an example CloudWatch dashboard,&lt;/li&gt;
&lt;li&gt;a FIS experiment template which you can run to test the lambda.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository with instructions can be found at &lt;a href="https://github.com/headforthecloud/cloudformation-aws-fis-lambda-monitoring" rel="noopener noreferrer"&gt;https://github.com/headforthecloud/cloudformation-aws-fis-lambda-monitoring&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As part of the deployment, it will output the URL to access the API Gateway. For testing, and to produce the above dashboards, I used this simple &lt;code&gt;bash&lt;/code&gt; script to call the API Gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; :
&lt;span class="k"&gt;do
    &lt;/span&gt;curl _insert_gateway_url_here &amp;amp;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;0.5
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then after running this to establish a baseline, we can start the FIS experiment template which should create similar results in the dashboard to those seen above.&lt;/p&gt;

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

&lt;p&gt;When it comes to our monitoring, we should have a formal, defined approach to testing, rather than a '&lt;em&gt;it'll be ok&lt;/em&gt;' mindset. Using AWS FIS in conjunction with the Lambda specific tests, we can move away from manual tinkering with a configuration, or intrusive &lt;code&gt;if TEST then ...&lt;/code&gt; code blocks and move to an approach where chaos engineering is an integral part of our testing process.&lt;/p&gt;

&lt;p&gt;Taking this approach means we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validate our monitoring&lt;/strong&gt;: Ensure that your dashboard and alerts show us when real issues occur,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit our resilience&lt;/strong&gt;: Provide stakeholders with repeatable, documented evidence that our monitoring approach is robust and fit for purpose,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamline our code&lt;/strong&gt;: Ensure that your code is focused on business value, and reduce our unit testing overheads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Embracing chaos lets us demonstrate that our monitoring approach works and provides our teams with the overview they need when they need it, rather than 3:00 AM on a Sunday morning.&lt;/p&gt;

&lt;p&gt;So go ahead, introduce chaos to your testing - your team will thank you for it!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on my blog at: &lt;a href="https://headforthe.cloud/article/is-your-testing-strategy-chaos/" rel="noopener noreferrer"&gt;https://headforthe.cloud/article/is-your-testing-strategy-chaos/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>monitoring</category>
      <category>chaosengineering</category>
      <category>fis</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Is your monitoring testing strategy chaos?</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Thu, 08 Jan 2026 11:28:25 +0000</pubDate>
      <link>https://forem.com/simon_hanmer/is-your-monitoring-testing-strategy-chaos-1knj</link>
      <guid>https://forem.com/simon_hanmer/is-your-monitoring-testing-strategy-chaos-1knj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Nowadays, many Cloud implementations will make use of serverless architectures, such as &lt;a href="https://aws.amazon.com/lambda" rel="noopener noreferrer"&gt;AWS Lambdas&lt;/a&gt; and &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;API Gateways&lt;/a&gt; to implement micro-services, or other similar functionality to deliver business logic without the need to manage servers.&lt;/p&gt;

&lt;p&gt;This is now a mature pattern, and we have a wealth of tools and approaches to help us ensure that our serverless code is performing as expected. We can develop and test locally, and use pipelines to deploy, all ensuring the risk of deploying non-functioning code is minimised.&lt;/p&gt;

&lt;p&gt;Whenever I'm working with teams, I have some best practices I recommend such as deploying lambdas via CI/CD, ensuring that logs have a retention periods set etc. I also recommend that they have monitoring in place to capture errors, failures or timeouts. But whilst testing code functionality is relatively straightforward, it can be more complex ensuring that monitoring is capturing the events we want, or that alarms are raised when issues are detected.&lt;/p&gt;

&lt;p&gt;With code, we'll test our functionality works (&lt;em&gt;happy path testing&lt;/em&gt;), but we should also test how we handle errors (&lt;em&gt;unhappy path&lt;/em&gt;), but how can we test our monitoring? I typically work within regulated industries, and here any testing has to be reproducable and we need to evidence our testing approaches which is difficult to do if we make changes manually. Alternatively, we could introduce changes to our code to allow us to introduce errors or slow down code (how many people have included code similar to &lt;code&gt;if TEST then ...&lt;/code&gt; )? But this introduces complexity to our code which we should avoid, since we should only include business logic.&lt;/p&gt;

&lt;p&gt;One tool that can help with this is &lt;a href="https://en.wikipedia.org/wiki/Chaos_engineering" rel="noopener noreferrer"&gt;Chaos Engineering&lt;/a&gt;. I've written previously on using Amazon's &lt;a href="https://aws.amazon.com/fis" rel="noopener noreferrer"&gt;Fault Injection Service&lt;/a&gt; (also known as FIS) to deliver '&lt;a href="https://dev.to/article/chaos-in-the-cloud"&gt;Chaos Engineering as a Service&lt;/a&gt;', and I've been using FIS to test various types of AWS resources.&lt;/p&gt;

&lt;p&gt;With servers, it's relatively easy to understand how we might implement Chaos Engineering tools - we could for example ssh into the server, then run scripts to introduce CPU load, or slow network connectivity, but how do we test code running in a serverless environment where we have no control over the environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing Chaos to Serverless environments
&lt;/h2&gt;

&lt;p&gt;Luckily at re:Invent 2024, AWS &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/10/aws-lambda-fault-injection-service-actions/" rel="noopener noreferrer"&gt;announced&lt;/a&gt; they were introducing new capabilities to FIS, allowing it to interact with Lambda.&lt;/p&gt;

&lt;p&gt;The new functionality provided three methods of testing lambdas by&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;delaying the start of a lambda function,&lt;/li&gt;
&lt;li&gt;forcing the function to generate an error, or&lt;/li&gt;
&lt;li&gt;modifying the responses returned by a lambda function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To provide this functionality with a given lambda, we need to perform 4 actions&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configure the lambda to use a &lt;a href="https://docs.aws.amazon.com/fis/latest/userguide/actions-lambda-extension-arns.html" rel="noopener noreferrer"&gt;Lambda Layer&lt;/a&gt; that allows FIS to interact with the lambda runtime environment,&lt;/li&gt;
&lt;li&gt;Create an S3 bucket which is used to pass configuration and runtime data between FIS and the lambda layer&lt;/li&gt;
&lt;li&gt;Add some environment variables to the lambda configuration

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;AWS_FIS_CONFIGURATION_LOCATION&lt;/code&gt; - the S3 bucket (and an optional prefix within the bucket)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_LAMBDA_EXEC_WRAPPER&lt;/code&gt; - the executable within the layer for FIS to use; currently this should be &lt;code&gt;/opt/aws-fis/bootstrap&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Ensure that the IAM execution role used to run the lambda has permissions to read and list the contents of the bucket&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;For more information on these pre-requisites, see the &lt;a href="https://docs.aws.amazon.com/fis/latest/userguide/use-lambda-actions.html#lambda-prerequisites" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining our testing approach
&lt;/h2&gt;

&lt;p&gt;Once we have our lambdas configured to allow them to use the FIS lambda layer, we need to define how we want to test them. To do this in FIS, we define an experiment template. Templates comprise a number of components as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdivtclvb7xfc8xyuyqbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdivtclvb7xfc8xyuyqbj.png" alt="FIS Experiment Template Components" width="711" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, we're interested in two component types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Targets - these define what AWS resources we want to test,in this case, our lambda function&lt;/li&gt;
&lt;li&gt;Actions - these describe what we want to do to the resources; with lambdas, this would be one of the 3 actions - delay, introduce errors, or change response code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an example, we might create an template which carries out these actions&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubjvc9ztgj45zveez18i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubjvc9ztgj45zveez18i.png" alt="FIS Experiment Template Components" width="763" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And configure this to run with targets that match all lambdas with a particular tag. In FIS, this would look something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuin9rx2viv5c9lcjuay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuin9rx2viv5c9lcjuay.png" alt="FIS Experiment Template Definition" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running our tests
&lt;/h2&gt;

&lt;p&gt;Once we have setup monitoring, we might expect to see results similar to below - the images are from a CloudWatch dashboard, but whatever monitoring tools, you should be able to see something similar:&lt;/p&gt;

&lt;p&gt;With lambdas running expected, an example dashboard might look something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmudt0sbh6t8yuv51h8qv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmudt0sbh6t8yuv51h8qv.png" alt="Normal Monitoring Dashboard" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we take the experiment described above, and run it during a similar period, our dashboard would now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ch0y9xmx93cis93w9c0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ch0y9xmx93cis93w9c0.png" alt="Dashboard with errors" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see the dip in the &lt;em&gt;Invocations&lt;/em&gt; widget, with matching peaks in &lt;em&gt;Duration&lt;/em&gt; and &lt;em&gt;Latency&lt;/em&gt; widgets at 09:45 that tie into when our experiment introduces a delay in the lambda executions, followed by a peak in the &lt;em&gt;Error Count&lt;/em&gt; widget at 09:55 when we introduce errors via the experiment, and finally when we change the response code at 10:10, there is a peak in the &lt;em&gt;4xx Error Count&lt;/em&gt; graph.&lt;/p&gt;

&lt;p&gt;Remember, this is done with no code changes, or having to manually modify how the infrastructure performs, and more importantly, provides a repeatable, auditable experiment that can be used at any time.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how do I try this?
&lt;/h2&gt;

&lt;p&gt;I've created a github repository which contains a CloudFormation template to deploy&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an example lambda, configured to use the FIS lambda layer,&lt;/li&gt;
&lt;li&gt;an API Gateway to access the lambda,&lt;/li&gt;
&lt;li&gt;an example CloudWatch dashboard,&lt;/li&gt;
&lt;li&gt;a FIS experiment template which you can run to test the lambda.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository with instructions can be found at &lt;a href="https://github.com/headforthecloud/cloudformation-aws-fis-lambda-monitoring" rel="noopener noreferrer"&gt;https://github.com/headforthecloud/cloudformation-aws-fis-lambda-monitoring&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As part of the deployment, it will output the URL to access the API Gateway. For testing, and to produce the above dashboards, I used this simple &lt;code&gt;bash&lt;/code&gt; script to call the API Gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; :
&lt;span class="k"&gt;do
    &lt;/span&gt;curl _insert_gateway_url_here &amp;amp;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;0.5
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then after running this to establish a baseline, we can start the FIS experiment template which should create similar results in the dashboard to those seen above.&lt;/p&gt;

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

&lt;p&gt;When it comes to our monitoring, we should have a formal, defined approach to testing, rather than a '&lt;em&gt;it'll be ok&lt;/em&gt;' mindset. Using AWS FIS in conjunction with the Lambda specific tests, we can move away from manual tinkering with a configuration, or intrusive &lt;code&gt;if TEST then ...&lt;/code&gt; code blocks and move to an approach where chaos engineering is an integral part of our testing process.&lt;/p&gt;

&lt;p&gt;Taking this approach means we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validate our monitoring&lt;/strong&gt;: Ensure that your dashboard and alerts show us when real issues occur,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit our resilience&lt;/strong&gt;: Provide stakeholders with repeatable, documented evidence that our monitoring approach is robust and fit for purpose,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamline our code&lt;/strong&gt;: Ensure that your code is focused on business value, and reduce our unit testing overheads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Embracing chaos lets us demonstrate that our monitoring approach works and provides our teams with the overview they need when they need it, rather than 3:00 AM on a Sunday morning.&lt;/p&gt;

&lt;p&gt;So go ahead, introduce chaos to your testing - your team will thank you for it!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on my blog at: &lt;a href="https://headforthe.cloud/article/is-your-testing-strategy-chaos/" rel="noopener noreferrer"&gt;https://headforthe.cloud/article/is-your-testing-strategy-chaos/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>chaosengineering</category>
      <category>serverless</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Chaos in the Cloud: A look at chaos engineering and Amazon's Fault Injection Service</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Tue, 24 Dec 2024 14:37:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/chaos-in-the-cloud-a-look-at-chaos-engineering-and-amazons-fault-injection-service-3hf8</link>
      <guid>https://forem.com/aws-builders/chaos-in-the-cloud-a-look-at-chaos-engineering-and-amazons-fault-injection-service-3hf8</guid>
      <description>&lt;p&gt;When I first started developing, we wrote huge, monolithic applications either running locally on our desktops, or in our datacenters. We'd write applications that had tens or even hundreds of thousands of lines of code. However, the applications we wrote usually consisted of a single component, maybe two if we used a database, handling all of the logic and functionality within a single application. Whilst this meant that we usually had complex, hard to navigate, code bases, it did mean that in terms of architecture, our applications were relatively simple.&lt;/p&gt;

&lt;p&gt;Today, we build systems with multiple components such as databases, message queues, application servers, all communicating across networks. The functionality for the systems is often broken down into microservices, each handling small subsets of the overall problem space. We usually deploy to cloud, which allows us to&lt;br&gt;
build more scaleable, resilient systems. This approach also means we can accelerate our development processes as we can allocate multiple individuals or teams to work on different parts of the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The evolution of testing approaches
&lt;/h2&gt;

&lt;p&gt;Looking back at those monolithic applications, testing tended to be a very manual process. We'd have test writers who'd review the requirements and write test specifications. They'd outline the steps to be carried out to test parts of the system, and then the testers would work through the list of steps, noting whether the system behaved as expected. If there were discrepancies, they'd  raise defects, and the developers would fix the issues.&lt;/p&gt;

&lt;p&gt;As software development has matured, we've moved on and added new approaches to our testing. We've introduced unit tests which allow us to demonstrate that small, isolated pieces of code work as we expect. Some of us even look to a Test-Driven Development (TDD) approach, where the first code we write is tests. Often these unit tests are then executed automatically as part of a pipeline, stopping deployment of new code until all tests pass successfully. &lt;/p&gt;

&lt;p&gt;We've also introduced integration testing, to ensure that our code works with other systems and applications and doesn't introduce issues. We've also introduced performance testing, where we look to see if our code behaves in a performant manner under load. &lt;/p&gt;

&lt;p&gt;However, one area that we've not been able to address until recently is thinking about how the infrastructure running our code behaves. We've not been very good at looking at how all of our separate components work together, especially if they don't behave as expected - how many times have projects been delayed,or even cancelled, because when using the application in anger, there were unexpected delays in communications, or parts of our code couldn't handle failures elsewhere in the system?&lt;/p&gt;

&lt;h2&gt;
  
  
  Using chaos to bring confidence
&lt;/h2&gt;

&lt;p&gt;As more companies started to build complex, distributed systems, people started to consider how they could test how resilient their systems were.&lt;/p&gt;

&lt;p&gt;It's probably not surprising that Amazon was one of the earliest companies to think about this, and in 2003, Jesse Robbins, introduced the idea of "Game Day"&lt;sup id="fnref1"&gt;1&lt;/sup&gt;, an attempt to increase reliability by regularly introducing failures in their systems.&lt;/p&gt;

&lt;p&gt;However, it wasn't until 2011, when Netflix started to migrate to the cloud, that the idea of 'Chaos Engineering' started to become widespread, primarily due to a set of tools introduced by Netflix. &lt;br&gt;
In a blog post &lt;sup id="fnref2"&gt;2&lt;/sup&gt;, engineers at Netflix introduced the idea of a toolkit, the so-called 'Simian Army' that could be used to generate failures in their systems.&lt;/p&gt;

&lt;p&gt;The 'Simian Army' was an open-source set of tools that could be used to introduce different types of failures into Netflix's systems. For example, 'Chaos Monkey' would randomly terminate instances in their production environment, 'Latency Monkey' would introduce delays in network communications, and 'Chaos Gorilla' would simulate the loss of an entire AWS region.&lt;/p&gt;

&lt;p&gt;Over time, this approach became known as 'Chaos Engineering', an approach that can be defined as &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The discipline of experimenting on a distributed system in order to build confidence in the system's capability to withstand turbulent conditions in production.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many people adopted the toolset, and started to use it directly, or to build their own scripts to introduce failures in a managed way to test the resilience of their systems. However, this all needed a lot of effort and resources to run and manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Undifferentiated heavy lifting to the rescue
&lt;/h2&gt;

&lt;p&gt;Amazon Web Services has a concept called 'undifferentiated heavy lifting'. This is the idea that when many of it's customers are expending a lot of effort to solve the same problem, that AWS should look to solve the problem for them, allowing them to focus on their core business.&lt;/p&gt;

&lt;p&gt;Amazon was aware that many of it's customers were looking at the approach of Chaos Engineering, and in 2021&lt;sup id="fnref4"&gt;4&lt;/sup&gt;, they introduced a new service, initially called the 'Fault Injection Simulator', but quickly renamed to the 'Fault Injection Service' (FIS).&lt;/p&gt;

&lt;p&gt;FIS was designed to allow customers to perform controlled, repeatable experiments on their workloads; introducing errors and issues and reviewing how their systems responded. &lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the perfect recipe for chaos
&lt;/h2&gt;

&lt;p&gt;The core of FIS is the concept of an 'experiment'. You can think of this as a recipe - you'll need ingredients (or &lt;em&gt;targets&lt;/em&gt; as they're known in FIS) which are the things you want to test, such as EC2 instances, RDS databases, lambdas or even the underlying network. Once you have your list of ingredients, you then need to understand the steps to combine them - these are the &lt;em&gt;actions&lt;/em&gt; that you'll take, such as introducing latency, or killing an instance.&lt;/p&gt;

&lt;p&gt;In the same way that we gather recipes in a cookbook, we can store our experiments in &lt;em&gt;templates&lt;/em&gt; so that we can run them again and again, knowing we can re-create our testing masterpieces perfectly each time.&lt;/p&gt;

&lt;p&gt;These templates are made up of a number of different components with the first 3 being required, and the remainder being optional. Let's look at these below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3l6qijunrt9bej34dgm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3l6qijunrt9bej34dgm.png" alt="Experiment template components" width="711" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Targets&lt;/strong&gt; - as mentioned above, these define the resources that you want to test with a full list being available at &lt;a href="https://docs.aws.amazon.com/fis/latest/userguide/targets.html#resource-types" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/fis/latest/userguide/targets.html#resource-types&lt;/a&gt;. Once you specify which type of resource you want to test, you can then filter the resources you want to test by tags, or by specifying the identifier such as an EC2 instance id.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actions&lt;/strong&gt; - these describe what you'd like to do to your targets. There are a number of different actions available, depending on what type of target you're testing - the full list is available at &lt;a href="https://docs.aws.amazon.com/fis/latest/userguide/fis-actions-reference.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/fis/latest/userguide/fis-actions-reference.html&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Role&lt;/strong&gt; - when you run an experiment, FIS will use a role you define to perform the experiment. This means that the role will need the appropriate permissions to interact with the resources AND also FIS itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stop Conditions&lt;/strong&gt; - there might be times when you want to halt an experiment, for example if it starts to impact on a running environment. &lt;em&gt;Stop Conditions&lt;/em&gt; allow you to link a pre-defined CloudWatch alarm to your template. Then, if that alarm is triggered during the experiment run, it will halt the experiment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt; - when you come to run your experiments, you may want to capture logs to understand what happened during the experiment. FIS allows you to capture logs to an S3 bucket or to CloudWatch, and they'll capture information such as the start and end of the experiment, the resources targeted, and the actions taken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reports&lt;/strong&gt; - just before re:Invent 2024, new functionality was announced which allows FIS to generate a report that can be shared with others to document the experiment results. FIS allows you to generate a report in PDF, optionally including graphs from CloudWatch in a format that can be shared with others - for example a change review board, or during a code review.&lt;/li&gt;
&lt;li&gt;Other options - in addition to the above items, there are other options available in templates, such as defining how long an experiment should run, how many items should be targetted (for example, 75% of an autoscaling group).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing to bear in mind is that you're not limited to a single set of targets and actions in a template. You could add multiple sets, for example, increasing the CPU usage of a set of servers, at the same time as rebooting an RDS database.&lt;/p&gt;

&lt;p&gt;Once you've defined a template, these can be run multiple times, either manually, part of a pipeline or at scheduled times. The results of these runs are then stored and can be reviewed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;As with most AWS services, you'll only pay for what you use. With FIS, the basic charge is $0.10 per minute per action per account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;&lt;br&gt;
Be aware that if you want to use the experiment reports that generate a PDF, you'll be charged an additional fee of $5 per report.&lt;/p&gt;



&lt;h2&gt;
  
  
  Coming soon!
&lt;/h2&gt;

&lt;p&gt;I hope you find this overview of Chaos Engineering and Amazon's Fault Injection Service useful. In the next post, coming soon, we'll look at how we create a template to test an EC2 autoscaling group, and then test how well the group responds to a failure.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://dl.acm.org/doi/10.1145/2367376.2371297" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/2367376.2371297&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://netflixtechblog.com/the-netflix-simian-army-16e57fbab116" rel="noopener noreferrer"&gt;https://netflixtechblog.com/the-netflix-simian-army-16e57fbab116&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://principlesofchaos.org/" rel="noopener noreferrer"&gt;https://principlesofchaos.org/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/aws/aws-fault-injection-simulator-use-controlled-experiments-to-boost-resilience/" rel="noopener noreferrer"&gt;https://aws.amazon.com/blogs/aws/aws-fault-injection-simulator-use-controlled-experiments-to-boost-resilience/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>chaosengineering</category>
      <category>simianarmy</category>
      <category>testing</category>
      <category>fis</category>
    </item>
    <item>
      <title>Chaos Engineering the AWS Way: 101</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Mon, 25 Mar 2024 17:29:16 +0000</pubDate>
      <link>https://forem.com/aws-builders/chaos-engineering-the-aws-way-101-1acb</link>
      <guid>https://forem.com/aws-builders/chaos-engineering-the-aws-way-101-1acb</guid>
      <description>&lt;p&gt;Over the last decade or so, we’ve seen a switch from the era of monolithic applications to a modern, more efficient, microservice-based development model. This had meant that we need to consider a wider landscape around testing to ensure our applications are resilient and perform as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build me a monolith.
&lt;/h2&gt;

&lt;p&gt;In the ‘good old days’ of software development, applications were designed and written as monoliths. These large, self-contained codebases simplified initial development but led to challenges as the scope of the applications grew. Scaling was difficult as the application would often have to be duplicated, rather than being able to scale individual components as needed. Also deploying updates or new functionality was a complex process, often needing extended downtime for the entire application.&lt;/p&gt;

&lt;p&gt;Testing of these applications would often be carried out manually, by test teams who would concentrate on the functional requirements. If non-functional requirements were tested, often this would be limited to performance-related tasks such as ensuring that a defined hardware configuration could handle a specific level of user traffic and respond within a given timeframe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transition to microservices.
&lt;/h2&gt;

&lt;p&gt;As the use of Cloud providers became more prevalent, new serverless functionality allowed us to change our approach to development, leading to the spread in the use of microservices. These allowed us to break down the monolithic functionality into smaller, independently developed and deployed services. Each microservice tended to focus on a specific piece of business functionality, allowing teams to work in parallel in a more agile manner.&lt;/p&gt;

&lt;p&gt;Applications now often have a distributed architecture with code running on servers, serverless or container-based systems or even client-side in browsers. We’re using databases with multiple read (or even write) hosts, caching, load balancers and other components, all coming together to create what is known as a distributed system, typically communicating via network links. This enables us to scale individual services as needed, leading to a more efficient use of resources and increased fault-tolerance but introduces new challenges related to communications, consistency and the need for more observability.&lt;/p&gt;

&lt;p&gt;This new paradigm has also enabled us to improve our development and QA practices, through the use of automated deployments, often including automated testing covering unit tests, through to behavioural testing. But again, these have tended to concentrate on functional requirements – and this means that whilst the complexity of our application landscapes has grown, our non-functional testing hasn’t kept pace with the now complex architectures.&lt;/p&gt;

&lt;p&gt;This challenge in managing distributed systems led to Peter Deutsch and James O. Coplient, articulating what are known as the ‘8 fallacies of distributed computing’ &lt;sup id="fnref1"&gt;1&lt;/sup&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The network is reliable.&lt;/li&gt;
&lt;li&gt;Latency is zero.&lt;/li&gt;
&lt;li&gt;Bandwidth is infinite.&lt;/li&gt;
&lt;li&gt;The network is secure.&lt;/li&gt;
&lt;li&gt;Topology doesn't change.&lt;/li&gt;
&lt;li&gt;There is one administrator.&lt;/li&gt;
&lt;li&gt;Transport cost is zero.&lt;/li&gt;
&lt;li&gt;The network is homogeneous.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Acknowledging these fallacies is essential for designing robust, resilient distributed systems and their associated applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release the apes of chaos.
&lt;/h2&gt;

&lt;p&gt;To counter this lack of testing around complexity, a new discipline started to emerge in the early 2000s - chaos engineering.&lt;/p&gt;

&lt;p&gt;Chaos engineering can be considered as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the discipline of experimenting on a system in order to build confidence in the system's capability to withstand turbulent conditions in production.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whilst there were some early, basic attempts by Amazon and Google, Netflix is generally considered as having defined this new approach by considering the 8 fallacies and trying to develop engineering solutions that would test against them to ensure reliability within their systems.&lt;/p&gt;

&lt;p&gt;In 2011, Netflix developed a tool known as ‘Chaos Monkey’, which intentionally disabled servers, databases and applications at random in the production environment to test the resilience of their worldwide video-streaming network. The name came from the idea that you could measure what might happen if a wild monkey ran through their data centres and Cloud environments with a weapon, smashing servers and chewing through cabling&lt;sup id="fnref3"&gt;3&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt; They quickly realised the value that this provided to their engineers, allowing them to design more highly available services, and expanded the tooling to create what is known as the ‘Simian Army’, with a variety of tools such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latency Monkey – a tool that introduced artificial delays in the network layer between different services. It allowed Netflix to simulate a server becoming unavailable, or even losing an entire service.&lt;/li&gt;
&lt;li&gt;Conformity Monkey – looks for servers not configured according to their best practices and shuts them down. For example, it would look for servers that weren’t part of an auto-scaling group, and so had limited resilience to unplanned shutdown.&lt;/li&gt;
&lt;li&gt;Chaos Gorilla – whilst Chaos Monkey looked for individual servers to target, Chaos Gorilla looked to test the outage of an entire availability zone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These, and many other, approaches to testing Netflix’s resilience soon began to gain recognition within the wider engineering community, and others tried to re-use or re-engineer similar tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS enter the Fray.
&lt;/h2&gt;

&lt;p&gt;As the major player in the Cloud engineering space, it’s safe to say that Netflix’s approach would have caught the eye of Amazon Web Services. After all, for many years, they were the biggest single customer using the Cloud provider’s services.&lt;/p&gt;

&lt;p&gt;One of AWS’s oft-quoted ideals is the aim of reducing ‘undifferentiated heavy lifting’, where they look for tasks that are being widely adopted by their customers, and which they could provide as a managed service, providing them with the opportunity to reduce workload and complexity (whilst at the same time, no doubt, providing an income stream). However, AWS’s announcement&lt;sup id="fnref4"&gt;4&lt;/sup&gt; at their 2020 re:Invent conference, that they would provide a managed service providing chaos engineering tools still came as a surprise to some.&lt;/p&gt;

&lt;p&gt;Amazon’s Fault Injection Service, or FIS&lt;sup id="fnref5"&gt;5&lt;/sup&gt;, offered a set of scenarios that could be deployed in their customers’ accounts, initially allowing testing against Amazon’s EC2, ECS, EKS and RDS services. Since that time, the offering has expanded and now includes cross-account testing, simulating errors at the control plane and network layers, allowing simulation of availability zone failures, API throttling and a wide range of other failure scenarios.&lt;/p&gt;

&lt;p&gt;FIS and associated AWS services, allow engineering teams to follow what is now seen as a standard set of practices within chaos engineering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Baseline performance – services such as AWS CloudWatch allow a deep understanding of applications’ operating parameters, allowing them to measure and track interactions, dependencies and various service metrics.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hypothesise – once there is an understanding of the components of a service, architects and engineers can start to think about ‘what if’ – would their availability continue under increased network latency, API throttling or the unplanned termination of components?&lt;/p&gt;

&lt;p&gt;FIS enables these hypotheses to be codified using what are known as ‘experiment templates’, describing tests to be carried out along with which components should be stressed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Experiment – FIS allows the experiment templates to be deployed and executed in an AWS environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Blast Radius – any chaos engineering tool should have the option to terminate an experiment if it starts to affect service in an unplanned way. FIS allows CloudWatch Alarms to be configured to halt experiments and roll back the effects that had been put in place.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measurement – once again, CloudWatch provides services such as metrics, logs and alarms, allowing application designers to understand how their services reacted to the experiments put in place.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FIS also brings an important element to the world of chaos engineering – control. While it’s good to test in development environments to understand how services will react to unplanned scenarios, one of chaos engineering’s tenets is that the most valuable insights will be gained by testing against production services. However, AWS customers will want to control how and when these experiments are deployed – this is achieved using IAM permissions to control who can define and execute FIS scenarios.&lt;/p&gt;

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

&lt;p&gt;Organisations and engineers working with complex, distributed systems within Cloud environments should look to adopt the principles of chaos engineering, ensuring that it becomes not just a best practice but a strategic imperative.&lt;/p&gt;

&lt;p&gt;Amazon’s FIS empowers engineering teams to proactively address the challenges of distributed systems, ensuring the robustness and resilience of applications in the dynamic and unpredictable cloud environment. Working to their principle of undifferentiated heavy lifting, AWS has positioned chaos engineering as a managed service, aligning with their commitment to reducing complexity and empowering customers to navigate the intricacies of modern cloud-based architectures.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing"&gt;https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://principlesofchaos.org/"&gt;https://principlesofchaos.org/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://netflixtechblog.com/the-netflix-simian-army-16e57fbab116"&gt;https://netflixtechblog.com/the-netflix-simian-army-16e57fbab116&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=VndV2j8fulo"&gt;https://www.youtube.com/watch?v=VndV2j8fulo&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;a href="http://aws.amazon.com/fis"&gt;http://aws.amazon.com/fis&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>chaos</category>
      <category>aws</category>
    </item>
    <item>
      <title>Validating Python code with a CodeCatalyst Pipeline.</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Thu, 18 Jan 2024 18:04:53 +0000</pubDate>
      <link>https://forem.com/aws-builders/validating-python-code-with-a-codecatalyst-pipeline-331f</link>
      <guid>https://forem.com/aws-builders/validating-python-code-with-a-codecatalyst-pipeline-331f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This article will form part of a series that will explore how to validate Python code, deploy it as a Lambda Function via Terraform, through to defining a standard template that can be used to create Lambdas.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/eg7nlpwEpnA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our example Python Code&lt;/li&gt;
&lt;li&gt;What will the pipeline do?&lt;/li&gt;
&lt;li&gt;
Creating a Pipeline

&lt;ul&gt;
&lt;li&gt;General Configuration&lt;/li&gt;
&lt;li&gt;Running Actions&lt;/li&gt;
&lt;li&gt;
Linting our code

&lt;ul&gt;
&lt;li&gt;PyLint configuration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Vulnerability scanning&lt;/li&gt;

&lt;li&gt;

Automated testing

&lt;ul&gt;
&lt;li&gt;PyTest configuration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Action ordering&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Running the pipeline

&lt;ul&gt;
&lt;li&gt;CodeCoverage details.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://codecatalyst.aws/explore" rel="noopener noreferrer"&gt;CodeCatalyst&lt;/a&gt; is a unified development environment created by AWS.&lt;/p&gt;

&lt;p&gt;It has many features such as blueprints to assist in writing code, integrated Git repositories, dev environments which can be pre-defined and now AI integration. However, for myself, one of the most useful things is being able to define and use pipelines stored in the code repository.&lt;/p&gt;

&lt;p&gt;Pipelines are one of the most commonly used tools that many of us working with code and Cloud use, allowing us to automate tasks to be carried out when we make changes to our code, whether that's checking that our code works, building artefacts and packages, and deploying to our environments.&lt;/p&gt;

&lt;p&gt;In this post, I'll share an example pipeline that we can use to validate some Python code as we work on it. Whilst this post doesn't cover all options available when working with pipelines, it should be enough to explain how the pipeline works and how you can modify them in your workflows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ To streamline the post, I'll assume you understand how CodeCatalyst works and can create and work with code in repositories.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Our example Python Code
&lt;/h2&gt;

&lt;p&gt;For this post, I'm using some example code from &lt;a href="https://github.com/headforthecloud/example-python-lambda" rel="noopener noreferrer"&gt;https://github.com/headforthecloud/example-python-lambda&lt;/a&gt; which defines a simple function that could be used as an &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda function&lt;/a&gt;. The code is structured like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aht6o0idvj43jkipxu7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aht6o0idvj43jkipxu7.png" alt="Screenshot showing repository structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code for the main function &lt;code&gt;lambda_function.py&lt;/code&gt;, shown below, just sets up some logging, outputs a message and returns a status code to indicate it ran successfully. It also includes a couple of functions that could be used to demonstrate testing:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 python
#! /usr/bin/env python
""" An example lambda function """

import os
import json
import logging


# define a logger using logging library. If LOG_LEVEL is not set, default to INFO.
# otherwise use value of LOG_LEVEL
logger = logging.getLogger()
logger.setLevel(os.getenv('LOG_LEVEL', 'INFO'))


def lambda_handler(event, context):
    """ define a lambda_handler function that takes in an event and a context """
    logger.info("Hello from Lambda!")

    return {
        "statusCode": 200,
        "body": json.dumps(event)
    }


def add_x_y(x, y):
    """ This is a simple function that adds two numbers together and returns the result. """
    return x + y


def multiply_x_y(x, y):
    """ This is a simple function that multiplies two numbers together and returns the result. """
    return x * y


# if this file is run directly, run the lambda_handler function with dummy event and context
if __name__ == '__main__':
    lambda_handler(None, None)


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

&lt;/div&gt;

&lt;p&gt;We also have some code written using the &lt;a href="https://pytest.org" rel="noopener noreferrer"&gt;PyTest&lt;/a&gt; framework, in a folder called &lt;code&gt;tests&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What will the pipeline do?
&lt;/h2&gt;

&lt;p&gt;In this example, we're going to perform a set of actions which are typical of a pipeline used with Python:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linting&lt;/strong&gt; - we do this to make sure our code meets general best practices in terms of code, and that it should at least run. For this, we'll use a well-known tool called &lt;a href="https://pypi.org/project/pylint/" rel="noopener noreferrer"&gt;PyLint&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vulnerability Scanning&lt;/strong&gt; - we do this to try and make sure our code doesn't contain any security issues such as secret values, possible SQL injection routes etc. We will use a tool called &lt;a href="https://github.com/PyCQA/bandit" rel="noopener noreferrer"&gt;Bandit&lt;/a&gt; for this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automated Testing&lt;/strong&gt; - we want to make sure that our code performs as expected. To this end, we'll use the &lt;a href="https://pytest.org" rel="noopener noreferrer"&gt;PyTest&lt;/a&gt; framework and check that our tests work and that we test an appropriate amount of our code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reporting&lt;/strong&gt; - for each step, we will use the CodeCatalyst functionality to generate reports showing the outcome of each step and whether it was successful.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating a Pipeline
&lt;/h2&gt;

&lt;p&gt;There are two approaches to generating or modifying a pipeline with CodeCatalyst - either via a visual editor built into &lt;a href="https://codecatalyst.aws" rel="noopener noreferrer"&gt;https://codecatalyst.aws&lt;/a&gt; or working in the repository and defining a pipeline using a YAML file in the &lt;code&gt;.codecatalyst/workflows&lt;/code&gt; file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 The full definition for the pipelines can be found &lt;a href="https://docs.aws.amazon.com/codecatalyst/latest/userguide/workflow-reference.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this example, I'll use the latter approach, working with a file called &lt;code&gt;.codecatalyst/workflows/python-testing-pipeline.yaml&lt;/code&gt;. If you'd like to see the full file, it's available in &lt;a href="https://github.com/headforthecloud/codecatalyst-python-pipeline/blob/main/.codecatalyst/workflows/python-testing-pipeline.yaml" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  General Configuration
&lt;/h3&gt;

&lt;p&gt;Firstly, we're going to define where and when the pipeline will run with this code:&lt;/p&gt;

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

&lt;span class="na"&gt;SchemaVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;

&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python-testing-pipeline&lt;/span&gt;

&lt;span class="na"&gt;Compute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EC2&lt;/span&gt;

&lt;span class="na"&gt;Triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;With this, we're saying that the pipeline will be called &lt;strong&gt;python-testing-pipeline&lt;/strong&gt;,  and that it will be executed using EC2 (we could also use lambdas).&lt;/p&gt;

&lt;p&gt;We're also going to define that the pipeline should be triggered every time changes are pushed to the repository. We could also have workflows triggered when working with a pull request, or even on a scheduled basis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Actions
&lt;/h3&gt;

&lt;p&gt;Once we've defined when and where the pipeline runs, we need to tell it what steps to carry out - to do this we'll use an &lt;code&gt;Actions&lt;/code&gt; section, which will have a number of these items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A name&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identifier&lt;/strong&gt; - these are equivalent to GitHub actions - in fact we can use some GitHub actions (see &lt;a href="https://docs.aws.amazon.com/codecatalyst/latest/userguide/integrations-github-actions.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more info.). In our examples, we'll use the &lt;code&gt;aws/build@v1&lt;/code&gt; and &lt;code&gt;aws/managed-test@v1&lt;/code&gt; actions (these are functionally equivalent and interchangeable).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inputs&lt;/strong&gt; - in this case, we're going to use these to specify that we want to retrieve our code from the &lt;code&gt;WorkflowSource&lt;/code&gt; i.e. the repository containing the pipeline, but we could also specify that we want to use artefacts that might contain saved files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration steps&lt;/strong&gt; - we'll use these to list the specific actions we want to perform in the pipeline. With the  &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;managed-test&lt;/code&gt; actions, we provide a list of &lt;code&gt;Run&lt;/code&gt; steps which use the Linux shell &lt;code&gt;bash&lt;/code&gt; to execute the provided commands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outputs&lt;/strong&gt; - in our example pipeline, we'll use these to define &lt;code&gt;Reports&lt;/code&gt; that will feedback on the results of our actions in CodeCatalyst.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Linting our code
&lt;/h3&gt;

&lt;p&gt;For our first action, we're going to check that our code meets the best practices for Python. In this case, as mentioned earlier we're going to use &lt;strong&gt;&lt;em&gt;PyLint&lt;/em&gt;&lt;/strong&gt; and our pipeline will carry out the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specify that we want to use the code from our repository&lt;/li&gt;
&lt;li&gt;Install Pylint using &lt;code&gt;pip&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ensure that we have a location we can use to store the results of our linting&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;pylint&lt;/code&gt; and capture the results in the folder created in the previous step&lt;/li&gt;
&lt;li&gt;Upload the results as a report to CodeCatalyst, using the &lt;code&gt;PYLINTJSON&lt;/code&gt; format and defining our success criteria which will control if this pipeline step is successful. In this example, we can specify what level of issues are allowed within a set of categories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To perform the above, we can use this code:&lt;/p&gt;

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

&lt;span class="na"&gt;Actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Linting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Identifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws/build@v1.0.0&lt;/span&gt;
    &lt;span class="na"&gt;Inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WorkflowSource&lt;/span&gt;
    &lt;span class="na"&gt;Configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Installing pylint"&lt;/span&gt;
            &lt;span class="s"&gt;pip install --user pylint&lt;/span&gt;
            &lt;span class="s"&gt;export PATH=$PATH:~/.local/bin&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Check testresults folder exists"&lt;/span&gt;
            &lt;span class="s"&gt;if [ ! -d tests/testresults ]&lt;/span&gt;
            &lt;span class="s"&gt;then&lt;/span&gt;
              &lt;span class="s"&gt;mkdir tests/testresults&lt;/span&gt;
            &lt;span class="s"&gt;fi&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Linting Python"&lt;/span&gt;
            &lt;span class="s"&gt;pylint *py tests/*py &amp;gt; tests/testresults/pylint-output.py&lt;/span&gt;
    &lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Reports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;PyLintResults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PYLINTJSON&lt;/span&gt;
          &lt;span class="na"&gt;IncludePaths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tests/testresults/pylint-output.py&lt;/span&gt;
          &lt;span class="na"&gt;SuccessCriteria&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;StaticAnalysisQuality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HIGH&lt;/span&gt;
              &lt;span class="na"&gt;Number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;StaticAnalysisSecurity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MEDIUM&lt;/span&gt;
              &lt;span class="na"&gt;Number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;StaticAnalysisBug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MEDIUM&lt;/span&gt;
              &lt;span class="na"&gt;Number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  PyLint configuration
&lt;/h4&gt;

&lt;p&gt;We have control over what checks PyLint will carry out by using a configuration file &lt;code&gt;.pylintrc&lt;/code&gt;. In our example, we'll use this setup&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;

&lt;span class="nn"&gt;[BASIC]&lt;/span&gt;
&lt;span class="py"&gt;good-names&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i,j,k,x,y,ex,Run,_&lt;/span&gt;
&lt;span class="py"&gt;fail-under&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0.1&lt;/span&gt;

&lt;span class="nn"&gt;[FORMAT]&lt;/span&gt;
&lt;span class="py"&gt;max-line-length&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;120&lt;/span&gt;
&lt;span class="py"&gt;indent-string&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'    '&lt;/span&gt;

&lt;span class="nn"&gt;[REPORTS]&lt;/span&gt;
&lt;span class="py"&gt;output-format&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;json&lt;/span&gt;



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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Vulnerability scanning
&lt;/h3&gt;

&lt;p&gt;We're also going to add a section to our actions to check that we don't have any security issues in our code such as including secrets, allowing SQL injection etc. To do this, we're going to use a tool called &lt;strong&gt;&lt;em&gt;Bandit&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The steps are very similar to those from the linting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specify that we want to use the code from our repository&lt;/li&gt;
&lt;li&gt;Install Bandit using &lt;code&gt;pip&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ensure that we have a location we can use to store the results of our scans&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;bandit&lt;/code&gt; and capture the results in the folder created in the previous step. We'll output the results in a standard format used by scanning tools called &lt;code&gt;sarif&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Upload the results as a report to CodeCatalyst, using the &lt;code&gt;SARIFSA&lt;/code&gt; format and defining our success criteria which will control if this pipeline step is successful. Again, we'll specify what criteria are needed for a successful run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To perform the above, we can use this code:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="na"&gt;vuln_scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Identifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws/build@v1.0.0&lt;/span&gt;
    &lt;span class="na"&gt;Inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WorkflowSource&lt;/span&gt;
    &lt;span class="na"&gt;Configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Installing bandit"&lt;/span&gt;
            &lt;span class="s"&gt;pip install --user bandit bandit-sarif-formatter&lt;/span&gt;
            &lt;span class="s"&gt;export PATH=$PATH:~/.local/bin&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Check testresults folder exists"&lt;/span&gt;
            &lt;span class="s"&gt;if [ ! -d tests/testresults ]&lt;/span&gt;
            &lt;span class="s"&gt;then&lt;/span&gt;
              &lt;span class="s"&gt;mkdir tests/testresults&lt;/span&gt;
            &lt;span class="s"&gt;fi&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Running Bandit"&lt;/span&gt;
            &lt;span class="s"&gt;bandit -r . --format sarif --output tests/testresults/bandit-output.sarif --exit-zero&lt;/span&gt;
    &lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Reports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BanditResults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SARIFSA&lt;/span&gt;
          &lt;span class="na"&gt;IncludePaths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tests/testresults/bandit-output.sarif&lt;/span&gt;
          &lt;span class="na"&gt;SuccessCriteria&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;StaticAnalysisFinding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MEDIUM&lt;/span&gt;
              &lt;span class="na"&gt;Number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;



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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Automated testing
&lt;/h3&gt;

&lt;p&gt;Whilst our other steps check our code from a static viewpoint, we want to be sure that our code works as we expect, so we'll have a step included in most pipelines - using automated testing to validate that our code works in the way we want.&lt;/p&gt;

&lt;p&gt;In our example, we're going to use the popular &lt;strong&gt;&lt;em&gt;PyTest&lt;/em&gt;&lt;/strong&gt; framework, which will use code stored in the &lt;code&gt;tests&lt;/code&gt; folder to check functionality - for this example, we're going to have a single, simple test to demonstrate how this can be done.&lt;/p&gt;

&lt;p&gt;As well as understanding whether our code passes the provided tests, we want to understand how much of our code has been tested, so we'll also capture what is known as &lt;em&gt;code coverage&lt;/em&gt; which records which lines of our code have been tested.&lt;/p&gt;

&lt;p&gt;Again our steps will follow the now familiar process of installing any required tools, executing them, and then capturing the results as a report within CodeCatalyst using the following code:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="na"&gt;unit_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Identifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws/managed-test@v1.0.0&lt;/span&gt;
    &lt;span class="na"&gt;Inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WorkflowSource&lt;/span&gt;
    &lt;span class="na"&gt;Configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Installing pytest"&lt;/span&gt;
            &lt;span class="s"&gt;pip install --user pytest pytest-cov&lt;/span&gt;
            &lt;span class="s"&gt;export PATH=$PATH:~/.local/bin&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Check testresults folder exists"&lt;/span&gt;
            &lt;span class="s"&gt;if [ ! -d tests/testresults ]&lt;/span&gt;
            &lt;span class="s"&gt;then&lt;/span&gt;
              &lt;span class="s"&gt;mkdir tests/testresults&lt;/span&gt;
            &lt;span class="s"&gt;fi&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Check for requirements"&lt;/span&gt;
            &lt;span class="s"&gt;if [ ! -r requirements.txt ]&lt;/span&gt;
            &lt;span class="s"&gt;then&lt;/span&gt;
              &lt;span class="s"&gt;pip install --user -r requirements.txt&lt;/span&gt;
            &lt;span class="s"&gt;fi&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo "Running PyTest"&lt;/span&gt;
            &lt;span class="s"&gt;python -m pytest&lt;/span&gt;
    &lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Reports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;PyTestResults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JUNITXML&lt;/span&gt;
          &lt;span class="na"&gt;IncludePaths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tests/testresults/junit.xml&lt;/span&gt;
          &lt;span class="na"&gt;SuccessCriteria&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;PassRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
        &lt;span class="na"&gt;CodeCoverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;COBERTURAXML&lt;/span&gt;
          &lt;span class="na"&gt;IncludePaths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tests/testresults/coverage.xml&lt;/span&gt;
          &lt;span class="na"&gt;SuccessCriteria&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;LineCoverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  PyTest configuration
&lt;/h4&gt;

&lt;p&gt;With PyTest, we're going to use two configuration files.&lt;br&gt;
 &lt;code&gt;.pytest.ini&lt;/code&gt; is used to define where our tests are and what output we'll generate from the tests - our example looks like:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;

&lt;span class="nn"&gt;[pytest]&lt;/span&gt;
&lt;span class="py"&gt;log_level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;INFO&lt;/span&gt;
&lt;span class="py"&gt;addopts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
    &lt;span class="s"&gt;-v --no-header --cov=.&lt;/span&gt;
    &lt;span class="py"&gt;--junitxml&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;tests/testresults/junit.xml&lt;/span&gt;
    &lt;span class="py"&gt;--cov-report&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;xml:tests/testresults/coverage.xml&lt;/span&gt;
    &lt;span class="py"&gt;--cov-report&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;term-missing&lt;/span&gt;

&lt;span class="py"&gt;testpaths&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;tests&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;and we'll also use a &lt;code&gt;.coveragerc&lt;/code&gt; file to tell PyTest not to include our test files when calculating code coverage via:&lt;/p&gt;


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

&lt;p&gt;&lt;span class="nn"&gt;[run]&lt;/span&gt;&lt;br&gt;
&lt;span class="py"&gt;omit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;./tests/*&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Action ordering&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;As defined here, there are no constraints on the ordering of the linting, scanning and testing steps, so they will run in parallel.&lt;/p&gt;

&lt;p&gt;However, if we want to ensure that a step will only run if a previous step is completed, we can use a &lt;code&gt;DependsOn&lt;/code&gt; clause in each action, so for example if we wanted our &lt;code&gt;unit_tests&lt;/code&gt; action to only run if the &lt;code&gt;linting&lt;/code&gt; step worked, we could change our action definition to include the following lines:&lt;/p&gt;


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

&lt;p&gt;&lt;span class="na"&gt;unit_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Linting&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;Identifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws/&lt;a href="mailto:managed-test@v1.0.0"&gt;managed-test@v1.0.0&lt;/a&gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Running the pipeline&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Once we've created our pipeline and committed it to the code repository in CodeCatalyst along with our Python code, we should have a file structure that looks something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2i3465dif9qqi47n7tgn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2i3465dif9qqi47n7tgn.png" alt="Complete repository structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With all of this in place, CodeCatalyst should recognise that it needs to run the pipeline anytime there are changes to the code in the repository, including the pipeline configuration file. These runs are visible in the CodeCatalyst console under &lt;code&gt;CI/CD &amp;gt; Workflows&lt;/code&gt; as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6czah65lwqnjt05ki5ka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6czah65lwqnjt05ki5ka.png" alt="Screenshot showing example workflow runs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each pipeline will be listed using the name defined at the start of the configuration, along with each run, showing the status of the run, a run ID, a commit ID that triggered the run, and which repository and branch were used.&lt;/p&gt;

&lt;p&gt;Clicking on the ID of the run, will take us to the details for that particular run, looking something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9egr7nfe03kcrd59rpaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9egr7nfe03kcrd59rpaj.png" alt="Screenshot of run details"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As mentioned earlier, you can see that because we didn't define any dependencies between the steps, they ran in parallel. We can also see whether the run was successful, the commit ID that triggered the run, along with when the run started, and how long it took.&lt;/p&gt;

&lt;p&gt;We can also click on any of the steps to see the details of each step, including the output from any commands:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubo6ur7we2p4u9weyuh7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubo6ur7we2p4u9weyuh7.png" alt="Screenshot of run details"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
   Reporting
&lt;/h2&gt;

&lt;p&gt;As well as being able to see whether a workflow run was successful, we can see any reports generated by clicking on &lt;code&gt;Reports&lt;/code&gt;, either in the sidebar or the details of the run screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jdj6potypw0feb22zco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jdj6potypw0feb22zco.png" alt="Screenshot of workflow reports"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screen above shows the reports generated by our workflow, when they were generated, if they were successful (as long as we defined criteria to specify what success means), along with repository details, the workflow action step that created the report and the type of data the report contains.&lt;/p&gt;

&lt;p&gt;These reports are, in my view, one of the items that helps CodeCatalyst stand out - it's very simple to define what reports are being generated, what type of data they contain, and what constitutes a successful report.&lt;/p&gt;

&lt;p&gt;Clicking through on the report name takes you to the detailed report data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo5t1yje578u25ipfuo4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo5t1yje578u25ipfuo4.png" alt="Screenshot of example Code Coverage report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example above, showing code coverage i.e. how much of the code has been tested, we can see what the success criteria were, how much of the code we've tested both as a summary, and on a per file basis.&lt;/p&gt;

&lt;h3&gt;
  
  
  CodeCoverage details.
&lt;/h3&gt;

&lt;p&gt;In the example, we could see a summary of the overall coverage, as well as a per file basis. We're also able to click through to the individual files to see which lines were tested or not:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxtiur61g6y8cull2ixu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxtiur61g6y8cull2ixu.png" alt="Screenshot showing which lines were tested in a file"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In my opinion, CodeCatalyst is a useful development tool - by integrating many of the tools required in the SDLC (Software Development Life Cycle), it can provide a very functional working space.&lt;/p&gt;

&lt;p&gt;In this example, we've concentrated on how we can define and perform pipelines when we make changes to our code, and how we can report on the outcomes from those changes - an area that I think CodeCatalyst is particularly strong in.&lt;/p&gt;

&lt;p&gt;If you have any questions, comments or suggestions for other tasks we could use in the pipelines, use the comment box below.&lt;/p&gt;

</description>
      <category>codecatalyst</category>
      <category>cicd</category>
      <category>python</category>
      <category>testing</category>
    </item>
    <item>
      <title>Generating cloudwatch alarms using 'metric math' via CloudFormation and Terraform.</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Thu, 04 Jan 2024 17:37:35 +0000</pubDate>
      <link>https://forem.com/aws-builders/generating-cloudwatch-alarms-using-metric-math-via-cloudformation-and-terraform-60l</link>
      <guid>https://forem.com/aws-builders/generating-cloudwatch-alarms-using-metric-math-via-cloudformation-and-terraform-60l</guid>
      <description>&lt;p&gt;I spend a lot of time working as a consultant with &lt;a href="https://globallogic.com/uk/" rel="noopener noreferrer"&gt;GlobalLogic UK&amp;amp;I&lt;/a&gt; with different client teams to deploy AWS infrastructure, and not surprisingly, I see differing levels of maturity and experience within these teams.&lt;/p&gt;

&lt;p&gt;While we work with teams with a lot of knowledge, often they concentrate on deploying the applications and infrastructure, but they won't think about how they can understand how well an application is working. This is an important aspect of working within the Cloud, usually termed &lt;em&gt;monitoring&lt;/em&gt; or &lt;em&gt;observability.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are several well-known 3rd party tools, such as Splunk or DataDog, but people often forget there is a handy set of tools provided by AWS under the &lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;CloudWatch&lt;/a&gt; banner. CloudWatch provides a range of services, such as exposing metrics, linking them to alarms, and X-ray services, to allow us to understand the performance of services in more depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudWatch metrics
&lt;/h2&gt;

&lt;p&gt;Most services that we can use within AWS, such as EC2, Lambdas, DynamoDB, etc., will emit metrics that we can use to understand how well a resource is performing.&lt;/p&gt;

&lt;p&gt;For example, if we're deploying EC2 instances, we can monitor many aspects of the instance, such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU utilisation,&lt;/li&gt;
&lt;li&gt;Disc read and write metrics,&lt;/li&gt;
&lt;li&gt;Network traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While we're working with lambda functions, we can monitor&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Number of invocations, including how many concurrent invocations,&lt;/li&gt;
&lt;li&gt;The duration of a function run,&lt;/li&gt;
&lt;li&gt;How many errors are generated?&lt;/li&gt;
&lt;li&gt;How many invocations were throttled (for example, if we exceeded the allowed number of lambda invocations in an account)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the console, we can view historical graphs of our metrics, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc14yi3xfl47dd4xjxed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc14yi3xfl47dd4xjxed.png" alt="Cloudwatch metrics graph for a lambda function"&gt;&lt;/a&gt;&lt;/p&gt;
CloudWatch metrics graph for a lambda function





&lt;p&gt;This graph shows the metrics for a lambda function &lt;code&gt;MyLambdaFunction&lt;/code&gt;, specifically how many times the function has run (invocations), along with how long the lambda takes to run (duration).&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudWatch Alarms
&lt;/h2&gt;

&lt;p&gt;Once we're aware of the metrics available for a particular resource, we can use them to set alarms. For example, we might want to be notified if the CPU usage on one of our EC2 instances went above 75% or if one of our lambdas was being throttled. Alarms have associated actions that are triggered when a metric goes into an alarm state or when it returns to normal, and we can, for example, send a message to a SNS topic, trigger a lambda to resolve something, or if we're using an EC2 metric, perform EC2 actions such as stopping or rebooting the instance.&lt;/p&gt;

&lt;p&gt;If we're graphing a metric through CloudWatch, we'll have the option to create an alarm directly or create one from the console. When we create the alarm, we'll be asked for the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A name and description&lt;/li&gt;
&lt;li&gt;Which namespace we are interested in, such as &lt;code&gt;EC2&lt;/code&gt; or &lt;code&gt;Lambda&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Which metrics do we want to monitor, i.e., CPU utilisation, throttle, etc.?&lt;/li&gt;
&lt;li&gt;What is the dimension for the metric, i.e., which instance, DynamoDB table, function name, etc.?&lt;/li&gt;
&lt;li&gt;How we'll measure the metric, such as using averages, sums, or minimum or maximum values. The AWS documentation for each service will explain the best statistics to use; for example, the Lambda documentation can be found &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;What period do we want to monitor, and how many anomalies do we need to trigger the alarm?&lt;/li&gt;
&lt;li&gt;What the threshold is for triggering the alarm—this is a value and condition such as &lt;code&gt;GreaterThanThreshold&lt;/code&gt; or &lt;code&gt;LessThanThreshold&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;What actions should be triggered when an alarm occurs or when it returns to its normal state?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Calculated alarms using metric math.
&lt;/h2&gt;

&lt;p&gt;Generally, we want to alarm on the absolute value of a metric, for example, when CPU utilisation exceeds a certain amount or if a lambda is being throttled. However, sometimes we'll be more interested in a relative value—maybe the percentage of runs that generate errors. A single error is important if a lambda runs once or twice, but much less so if the lambda runs thousands of times a day.&lt;/p&gt;

&lt;p&gt;Where this isn't available as a metric, we can often calculate them using what is known as &lt;code&gt;metric math&lt;/code&gt;. So for example, with the example above, we can calculate the number of errors as a percentage of invocations&lt;/p&gt;

&lt;p&gt;Rather than looking at the absolute value of the &lt;code&gt;Error&lt;/code&gt; metric as in&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvcn1b7ug6xskclsjwjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvcn1b7ug6xskclsjwjd.png" alt="Alarm configuration using absolute values"&gt;&lt;/a&gt;&lt;/p&gt;
Alarm configuration using absolute values





&lt;p&gt;we can use expressions to calculate a value&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnl6xy1timpauteujpydd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnl6xy1timpauteujpydd.png" alt="Alarm configuration using expressions"&gt;&lt;/a&gt;&lt;/p&gt;
Alarm configuration using expressions





&lt;h2&gt;
  
  
  Deploying Alarms via Infrastructure as Code.
&lt;/h2&gt;

&lt;p&gt;Of course, best practices today dictate that we should be deploying our infrastructure as code, using tools such as &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;CloudFormation&lt;/a&gt; or &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The examples below are not full deployments, they only show how to create alarms. &lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying an alarm with absolute values using CloudFormation
&lt;/h3&gt;

&lt;p&gt;The yaml CloudFormation below will deploy an alarm which will trigger when a lambda is throttled.&lt;/p&gt;

&lt;p&gt;In this case, the definition is reasonably simple as we are looking at a single metric, in this case the &lt;code&gt;Throttles&lt;/code&gt; metric in the &lt;code&gt;AWS/Lambda&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;It depends on the following references&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LambdaFunctionName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The name of the deployed Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LambdaThrottlePeriod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What period should we use when assessing if a throttle has occurred&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LambdaThrottleThreshold&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How many throttles do we need to see before triggering the alarm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws_sns_topic.ErrorAlarmTopic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The SNS Topic to send alarms to&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


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

&lt;p&gt;&lt;span class="na"&gt;rLambdaThrottledAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;AlarmName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda-function-${pLambdaFunctionName}-throttled"&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda-function-${pLambdaFunctionName}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;throttled"&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Throttles&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/Lambda&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FunctionName&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;pLambdaFunctionName&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;pLambdaThrottlePeriod&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sum&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;pLambdaThrottleThreshold&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanThreshold&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;TreatMissingData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ignore&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;rAlarmTopic&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;OKActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;rAlarmTopic&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Deploying an alarm with absolute values using Terraform&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;The Terraform below will deploy an alarm which will trigger when a lambda is throttled. It depends on the following variables and resources:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LambdaFunctionName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The name of the deployed Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LambdaThrottlePeriod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What period should we use when assessing if a throttle has occurred&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LambdaThrottleThreshold&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How many throttles do we need to see before triggering the alarm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AlarmTopic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The SNS Topic to send alarms to&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


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

&lt;p&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_metric_alarm"&lt;/span&gt; &lt;span class="s2"&gt;"LambdaThrottledAlarm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;alarm_name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda-function-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaFunctionName&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-throttled"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;alarm_description&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda-function-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaFunctionName&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is throttled"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;comparison_operator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GreaterThanThreshold"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;evaluation_periods&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;metric_name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Throttles"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;namespace&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS/Lambda"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;period&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaThrottlePeriod&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;treat_missing_data&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ignore"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;statistic&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sum"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;threshold&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaThrottleThreshold&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;alarm_actions&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AlarmTopic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;ok_actions&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AlarmTopic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nx"&gt;dimensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;FunctionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pLambdaFunctionName&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Deploying an alarm with calculated values using CloudFormation&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;This time, the template is more complex. We cannot simply add the metrics to be measured, but we also need to include the expression that will be calculated and use that as the trigger for the alarm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: We use two intermediate metrics, &lt;code&gt;errors&lt;/code&gt; and &lt;code&gt;invocations&lt;/code&gt; which we then reference in the expression. Both of the intermediate metrics are tagged with &lt;code&gt;ReturnData: false&lt;/code&gt; to indicate they are intermediates.&lt;/p&gt;


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

&lt;p&gt;&lt;span class="na"&gt;rMyLambdaErrorsAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;AlarmName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda-function-${pLambdaFunctionName}-errors"&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda-function-${pLambdaFunctionName}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;errors&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exceed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${pLambdaErrorThreshold}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;%"&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;Metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error_percentage&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;Label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Errors&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;Expression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;(errors/invocations)*100&lt;/span&gt;&lt;br&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;errors&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;Label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;input&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;ReturnData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;MetricStat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;Metric&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
              &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/Lambda&lt;/span&gt;&lt;br&gt;
              &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Errors&lt;/span&gt;&lt;br&gt;
              &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FunctionName&lt;/span&gt;&lt;br&gt;
                  &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;pLambdaFunctionName&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;pLambdaErrorPeriod&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;Stat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sum&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;Unit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Count&lt;/span&gt;&lt;br&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invocations&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;Label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;input&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;ReturnData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;MetricStat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;Metric&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
              &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/Lambda&lt;/span&gt;&lt;br&gt;
              &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Invocations&lt;/span&gt;&lt;br&gt;
              &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FunctionName&lt;/span&gt;&lt;br&gt;
                  &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;pLambdaFunctionName&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;pLambdaErrorPeriod&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;Stat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sum&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;Unit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Count&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;pLambdaErrorThreshold&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanThreshold&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;TreatMissingData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ignore&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;rErrorAlarmTopic&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;OKActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;rErrorAlarmTopic&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Deploying an alarm with calculated values using Terraform&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Again, this is more complex than the Terraform used to deploy the alarm based on an absolute value, because again we need to use an expression to calculate the value for the alarm to monitor.&lt;/p&gt;


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

&lt;p&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_metric_alarm"&lt;/span&gt; &lt;span class="s2"&gt;"MyLambdaErrorsAlarm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;alarm_name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda-function-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaFunctionName&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-errors"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;alarm_description&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda-function-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaFunctionName&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; errors exceed &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaErrorThreshold&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; %"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;comparison_operator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GreaterThanThreshold"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;evaluation_periods&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;threshold&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaErrorThreshold&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;treat_missing_data&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ignore"&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;alarm_actions&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AlarmTopic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;ok_actions&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AlarmTopic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nx"&gt;metric_query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"error_percentage"&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;expression&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"(errors/invocations)*100"&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;label&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Errors"&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;return_data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nx"&gt;metric_query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"errors"&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;metric&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;metric_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Errors"&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;namespace&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS/Lambda"&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;period&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaErrorPeriod&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;stat&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sum"&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;unit&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Count"&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;span class="nx"&amp;gt;dimensions&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="nx"&amp;gt;FunctionName&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="kd"&amp;gt;var&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;LambdaFunctionName&amp;lt;/span&amp;gt;
  &amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span class="nx"&amp;gt;return_data&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="kc"&amp;gt;false&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nx"&gt;metric_query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"invocations"&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;metric&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;metric_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Invocations"&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;namespace&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS/Lambda"&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;period&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaErrorPeriod&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;stat&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sum"&lt;/span&gt;&lt;br&gt;
      &lt;span class="nx"&gt;unit&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Count"&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;span class="nx"&amp;gt;dimensions&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="nx"&amp;gt;FunctionName&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="kd"&amp;gt;var&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;LambdaFunctionName&amp;lt;/span&amp;gt;
  &amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span class="nx"&amp;gt;return_data&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="kc"&amp;gt;false&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Metric and alarm pricing.&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;In this post, we're using standard metrics provided by AWS which are provided by AWS without charge.&lt;/p&gt;

&lt;p&gt;Alarms are priced per metric, with each metric monitored costing $0.10 per month (depending on region). So our initial, absolute alarm would cost $0.10 per month as it monitors a single metric, whilst the calculated alarm would cost $0.20 per month. There is a free tier where the first 10 alarms per month are free.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Use VSCode to write Terraform? AWS AI can now help you write your code!</title>
      <dc:creator>Simon Hanmer</dc:creator>
      <pubDate>Mon, 27 Nov 2023 18:37:52 +0000</pubDate>
      <link>https://forem.com/aws-builders/use-vscode-to-write-terraform-aws-ai-can-now-help-you-write-your-code-59nn</link>
      <guid>https://forem.com/aws-builders/use-vscode-to-write-terraform-aws-ai-can-now-help-you-write-your-code-59nn</guid>
      <description>&lt;p&gt;As I sit here writing this article, AWS's annual re:Invent is just starting in Las Vegas. This huge event pulls together cloud enthusiasts from around the world to learn about the largest cloud providers offerings.&lt;/p&gt;

&lt;p&gt;Not surprisingly, AWS use this time to announce new and improved services, and we'll see hundreds of articles over the next week - in fact, there are so many announcements, they have to start drip-feeding them out in advance of the main event.&lt;/p&gt;

&lt;p&gt;One of the announcements released in advance of re:Invent this year, was that &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/11/amazon-codewhisperer-new-enhancements/" rel="noopener noreferrer"&gt;new capabilities had been added to AWS CodeWhisperer&lt;/a&gt;, AWS's AI powered coding bot - including the ability to generate Terraform code. In this article, I'll show how you can configure VSCode to use CodeWhisperer and how you can start to use CodeWhisperer to produce Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Assuming you have VSCode installed, we'll need to go through a few steps to let us use CodeWhisperer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the AWS Toolkit extension for VSCode
&lt;/h3&gt;

&lt;p&gt;If you're a regular user, you'll not be surprised to learn that the integration between VSCode and CodeWhisperer is via an extension. Extensions are VSCode's plugins that are used to add additional functionality, such as check code formats, linting, syntax checks etc.&lt;/p&gt;

&lt;p&gt;To install an extension, click on the extensions icon in the taskbar on the left-hand side of VSCode which is shown below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjmwjlt6eu67b2v5g2ed2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjmwjlt6eu67b2v5g2ed2.png" alt="Extension bar of VSCode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the search bar that appears, enter the name &lt;code&gt;amazonwebservices.aws-toolkit-vscode&lt;/code&gt; or &lt;code&gt;AWS Toolkit&lt;/code&gt;and you'll see an icon appear along with a brief description. Click on this to show information about the extension, and you can then click on the blue &lt;code&gt;Install&lt;/code&gt; button. After installation, you may be asked to reload VSCode to activate the extension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdii4du3ght45938t4y7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdii4du3ght45938t4y7.png" alt="AWS Toolkit extension screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the extension is properly installed, you will see a new AWS icon in the task-bar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckypansye0v2xv1hytb9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckypansye0v2xv1hytb9.png" alt="VSCode taskbar with new AWS Icon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Who goes there?
&lt;/h3&gt;

&lt;p&gt;With the extension in place, we need to authenticate our VSCode session to AWS. If your organisation is paying for the CodeWhisperer Professional tier, you might have access to it through IAM's SSO Identity Center. However, for most of us the easiest option is to sign up for an AWS Builder Id via &lt;a href="https://profile.aws.amazon.com/" rel="noopener noreferrer"&gt;https://profile.aws.amazon.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Builder Ids are a relatively new way of authenticating with AWS, and they are not associated with a particular AWS account, so these identities are free.&lt;/p&gt;

&lt;h3&gt;
  
  
  You're not coming in, if your name's not on the list.
&lt;/h3&gt;

&lt;p&gt;Once you have have a builder Id, signing into the extension is fairly pain-free. Click on the new AWS icon in the VSCode taskbar, and you'll be asked to &lt;code&gt;Select a connection&lt;/code&gt;. Click on this and you'll get a popup, giving you the change to &lt;code&gt;Add New Connection&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feaape9iu8875yxza7x9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feaape9iu8875yxza7x9i.png" alt="Asking to add new connection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;code&gt;Add new connection&lt;/code&gt;, and you'll be shown a screen showing the different ways you can connect with the AWS Toolkit. Scroll down under CodeWhisperer and you'll see a button, asking you to sign up or sign in for an AWS Builder Id. Click on this and you'll be prompted through a series of popups asking you to connect via your browser. Confirm these connections and you'll be taken through the sign-in process before being prompted to confirm you allow the extension to access your builder Id and associated data. Confirm this and you'll get confirmation in the browser that you have allowed this, and in VSCode the screen will change to show you're connected with your builder Id.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqba1u6ppf1x9f1j47jk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqba1u6ppf1x9f1j47jk.png" alt="Confirmed Connection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get the party started.
&lt;/h2&gt;

&lt;p&gt;With the confirmed connection we can finally get down to using CodeWhisperer to create some code.&lt;/p&gt;

&lt;p&gt;After closing down any open windows in VSCode, create a new text file. You should be prompted to &lt;code&gt;Select a language&lt;/code&gt; - click on this and choose &lt;code&gt;Terraform&lt;/code&gt;. (If you don't see Terraform in the list of options, you may need to install the official Hashicorp Terraform extension.)&lt;/p&gt;

&lt;p&gt;With our new Terraform file, we can get down to work. We normally interact with CodeWhisperer via comments in the code, so enter the following text&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# configure aws s3 as backend to store terraform state&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fs3.eu-west-2.amazonaws.com%2Fheadforthe.cloud%2Fimages%2Fvscode-1701108540806.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fs3.eu-west-2.amazonaws.com%2Fheadforthe.cloud%2Fimages%2Fvscode-1701108540806.gif" alt="Screen capture of editing code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you get to the end of the text, CodeWhisperer will insert text into the editing window. At this point you have a number of options available - after a few seconds, a popup will appear at the top of the window, offering the following choices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Press &lt;code&gt;TAB&lt;/code&gt; to accept the suggestion,&lt;/li&gt;
&lt;li&gt;Press left and right arrows to access alternative suggestions&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Esc&lt;/code&gt; to reject the suggestions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Another example
&lt;/h2&gt;

&lt;p&gt;Let's consider a different prompt &lt;br&gt;
&lt;code&gt;# write a terraform iam policy allowing a user to get and put objects to an s3 bucket.&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When I submit this, I get the response&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 terraform
resource "aws_iam_policy" "s3_policy" {
  name        = "s3_policy"
  description = "A test policy"

  policy = &amp;lt;&amp;lt;EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Effect": "Allow",
      "Resource": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    }
  ]
}
EOF
} 


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

&lt;/div&gt;

&lt;p&gt;However, I might be working with a team that prefers to use &lt;code&gt;jsonencode&lt;/code&gt; - so I can be more specific in my prompt, changing it to &lt;code&gt;# write a terraform iam policy using jsonencode allowing a user to get and put objects to an s3 bucket&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This time, I get an appropriate set of code as below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 terraform
resource "aws_iam_policy" "s3_policy" {
  name        = "s3_policy"
  description = "s3_policy"
  policy      = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "s3:GetObject",
          "s3:PutObject"
        ]
        Effect   = "Allow"
        Resource = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
      },
    ]
  })
}


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

&lt;/div&gt;

&lt;p&gt;`&lt;/p&gt;

&lt;h2&gt;
  
  
  Is CodeWhisperer infallible
&lt;/h2&gt;

&lt;p&gt;Like most AI tools currently available, CodeWhisperer will generally offer good suggestions but sometimes you will need to alter your prompt to get the best suggestion, so called &lt;a href="https://en.wikipedia.org/wiki/Prompt_engineering" rel="noopener noreferrer"&gt;prompt engineering&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Personally, I would have liked the IAM examples above to use &lt;code&gt;[ "s3://************/" ]&lt;/code&gt; for the s3 resources, but it can be argued that this is down to the data used to train the AI model. &lt;/p&gt;

&lt;p&gt;I think it's safe to say that we're not at the point where we can deploy the generated code straight to production, but I can see that it would be very useful for a less experienced engineer.&lt;/p&gt;

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

&lt;p&gt;In this article, I've given a brief overview of how we can now use CodeWhisperer in VSCode to help us write our Terraform. The new capabilities also support other Infrastructure as Code languages such as CloudFormation and CDK, and no doubt the list of supported languages will increase.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>terraform</category>
      <category>codewhisperer</category>
      <category>reinvent2023</category>
    </item>
  </channel>
</rss>
