<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Michael Walmsley (@walmsles)]]></title><description><![CDATA[I am a Serverless Architect and AWS Community Builder who is passionate about Cloud Native Solutions and Serverless technologies.]]></description><link>https://blog.walmsles.io</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 02:15:06 GMT</lastBuildDate><atom:link href="https://blog.walmsles.io/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Web Sockets Don't Pub-Sub]]></title><description><![CDATA[Web Sockets have become a fundamental technology in modern web development, but their true function is often misunderstood. The killer use-case for Web Sockets has been in the development and proliferation of chat apps where users jump into a channel...]]></description><link>https://blog.walmsles.io/web-sockets-dont-pub-sub</link><guid isPermaLink="true">https://blog.walmsles.io/web-sockets-dont-pub-sub</guid><category><![CDATA[websockets]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Sun, 02 Mar 2025 12:29:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740918567599/dd8275fb-2afe-4f55-a8c4-1a0ee387112e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Web Sockets have become a fundamental technology in modern web development, but their true function is often misunderstood. The killer use-case for Web Sockets has been in the development and proliferation of chat apps where users jump into a channel and write messages to one another. This has led to a wide spread understanding that Web Sockets provide publish and subscribe capability for your application - but this is a myth - Web Sockets do not Pub-Sub. In this article you will learn about Web Sockets, what they do and how they fit into your application architecture and common uses for Web Sockets in web application development.</p>
<h2 id="heading-what-are-web-sockets">What are Web Sockets?</h2>
<p>Web Sockets represent a communication protocol that creates a persistent connection between a client (typically a web browser) and a server. Unlike traditional HTTP, which follows a request-response pattern, Web Sockets allow:</p>
<ul>
<li><p><strong>Two-way communication</strong>: Both server and client can send messages at any time</p>
</li>
<li><p><strong>Persistent connection</strong>: One connection stays open instead of creating new ones for each interaction.</p>
</li>
<li><p><strong>Low latency</strong>: Messages transmit immediately without the overhead of establishing new connections</p>
</li>
</ul>
<p>Before Web Sockets, developers used techniques like HTTP polling, where browsers would repeatedly ask servers "Do you have anything new for me?". This is inefficient and causes delays. Web Sockets solved this problem by creating a direct communication channel that stays open enabling servers to send data directly to clients as soon as it is available which enables powerful real-time bi-directional communication.</p>
<h2 id="heading-how-web-sockets-work">How Web Sockets Work?</h2>
<p>WebSocket connections are an instance of an upgraded standard HTTP connection over TCP. When connecting to a web server with the “wss” protocol - the server attempts to upgrade the connection from a standard HTTP web connection to a persistent websocket one.</p>
<p>This process happens in two main phases:</p>
<h3 id="heading-the-handshake">The Handshake</h3>
<p>When a client wants to establish a WebSocket connection:</p>
<ol>
<li><p><strong>Initial Request</strong>: The client sends an HTTP request that says "I'd like to upgrade this connection to Web Sockets" and ”I support these sub-protocols”.</p>
</li>
<li><p><strong>Server Verification</strong>: The server responds with a special code confirming it accepts the WebSocket connection and also confirms the sub-protocol it prefers the client to use.</p>
</li>
<li><p><strong>Connection Established</strong>: Once the handshake completes successfully, the HTTP connection "upgrades" to a WebSocket connection.</p>
</li>
</ol>
<p>This handshake is designed to be compatible with HTTP infrastructure, so Web Sockets can work on standard web ports (80 and 443).</p>
<h3 id="heading-data-transfer">Data Transfer</h3>
<p>After the connection is established:</p>
<ul>
<li><p>Data is sent in units called "frames".</p>
</li>
<li><p>Messages can be text (UTF-8) or binary data.</p>
</li>
<li><p>Either side can send messages at any time and messages must conform to the agreed sub-protocol.</p>
</li>
<li><p>For security, all client-to-server messages are encrypted over the TSL channel created when connecting.</p>
</li>
<li><p>The connection stays open until either side decides to close it.</p>
</li>
</ul>
<p>It is important to understand that with a Web Socket connection both the client and server are able to send messages to the other at anytime. Any data written to the Web Socket will be transported to the other party. This is where sub-protocols become important and are a critical component of the design process for an application leveraging this technology. The way clients and servers communicate over Web Sockets is defined by the sub-protocol.</p>
<h2 id="heading-what-are-subprotocols">What are SubProtocols?</h2>
<p>Subprotocols are agreed-upon conventions for how messages should be formatted and interpreted. Think of them as languages that both client and server agree to speak. Sub-protocols are important for:</p>
<ul>
<li><p><strong>Standardization</strong>: Provide consistent ways to format messages (like JSON, XML, or custom formats)</p>
</li>
<li><p><strong>Messaging Syntax</strong>: provides the order and meaning of messages and defines any request/response style interactions and how these are tracked. If you are using web-sockets to transfer request/response interactions these need to be carefully thought out and designed so the client and server can correlate related messages.</p>
</li>
<li><p><strong>Versioning</strong>: Allows protocols to evolve while maintaining backward compatibility</p>
</li>
<li><p><strong>Interoperability</strong>: Different implementations can work together if they follow the same subprotocol</p>
</li>
</ul>
<p>During the handshake, the client can say "I can speak these sub-protocols" and the server responds with "Let's use this one." For example:</p>
<ul>
<li><p>A chat application might use a chat-specific sub-protocol.</p>
</li>
<li><p>A financial application might use a different sub-protocol optimized for market data.</p>
</li>
<li><p>A game might use yet another sub-protocol designed for real-time player movements.</p>
</li>
</ul>
<p>By eliminating the constant request-response cycle and providing a direct communication channel, Web Sockets make interactive web applications more responsive and efficient, especially when real-time updates are essential.</p>
<h2 id="heading-where-is-the-pub-sub">Where is the Pub-Sub?</h2>
<p>The Web Sockets layer itself doesn’t provide any capability or functionality other than handling the client connections and transferring data between the client and server applications that sit at either end of the data pipe connection (Web Socket).</p>
<p>The connections starts as a normal HTTP connection with DNS Lookup and TLS handshake for a secure connection. Once the HTTP connection is complete the client will send a standard HTTP get asking to upgrade the connection to a websocket. It is at this point the client will include a list of sub-protocols it understands. The server provides the client sub-protocols and asks the backend server to authorize the connection. The backend server will respond with the sub-protocol is prefers to use from those provided by the client and whether the connection is authorized to continue.</p>
<p><img src="https://serverlessdna.com/strands/websockets/do-not-pub-sub/ws-connect.png" alt="Web Socket Sequence diagram showing client web socket server and backend application as actors" /></p>
<p>It is at this point that the connection is now established and either the client or server are able to send data to each other using the selected sub-protocol. The client is able to send a message conforming to the sub-protocol syntax to the server for processing. Periodically the Web Socket server may send keep-alive messages which are documented as part of the Web Socket protocol - these "Ping"-"Pong" messages are a way to ensure the connection is healthy.</p>
<p><img src="https://serverlessdna.com/strands/websockets/do-not-pub-sub/ws-client-message.png" alt="Web Socket Sequence diagram showing client web socket server and backend application as actors" /></p>
<p>Web Sockets, being bi-directional, enables the server to also send notifications to the client. These can occur at anytime and the client decides how to handle these. The sub-protocol determines the structure of data to be sent between the client and server and it also dictates how each actor will respond to messages being sent.</p>
<p><img src="https://serverlessdna.com/strands/websockets/do-not-pub-sub/ws-server-message.png" alt="Web Socket Sequence diagram showing client web socket server and backend application as actors" /></p>
<p>When the client and server communications are complete, either may request to close down the connection and disconnect.</p>
<p>Web Sockets do not provide any functionality other than transporting data between client and server applications, it is the backend server implementation which provides the Publish and Subscribe capability. For this reason it is essential to think about the sub-protocol your applications will use when communicating over web-sockets, without this you will have applications sending garbage to one another and not achieving anything useful.</p>
<h2 id="heading-considerations-for-architects-and-developers">Considerations for Architects and Developers</h2>
<p>Web Sockets provide the core communications capability for realtime data transfer, they handle the establishment of the connection and transfer of data. From a client-server application perspective there are a number of challenges that Architects and Developers need to be aware of when designing and building solutions around Web Sockets.</p>
<h3 id="heading-connection-management">Connection Management</h3>
<p>Unlike stateless HTTP, WebSocket connections create long-lived connections that must be actively managed. Networks are often unreliable with interruptions causing connections to drop. When you build with Web Sockets both the client and server developers must be mindful of the connections and their state - the management required is dependent on the actual features and function of the application sub-protocol being implemented. You will need to consider the following for managing connections:</p>
<ul>
<li><p>Implementing reconnection logic with exponential backoff on both client and server</p>
</li>
<li><p>Design for graceful handling of unexpected disconnections.</p>
</li>
<li><p>Consider connection monitoring with Ping/Pong frames to detect "zombie" connections.</p>
</li>
<li><p>Ensure both client and server can detect "half-open" connections where TCP remains connected but the application level communication has failed.</p>
</li>
<li><p>Handle abrupt disconnect from clients which can occur through application crashes or browser tab closure or refresh scenarios to ensure you have clean disconnection logic.</p>
</li>
</ul>
<h3 id="heading-message-ordering-and-delivery-guarantees">Message Ordering and Delivery Guarantees</h3>
<p>Web Sockets guarantee in-order delivery on a single connection but lack mechanisms for handling reconnection scenarios. When clients are reconnecting you need to consider the following:</p>
<ul>
<li><p>Implement application-level sequence numbers for all messages to detect missed updates.</p>
</li>
<li><p>Build acknowledgment protocols for critical messages with timeout-based retransmission.</p>
</li>
<li><p>Design idempotency into message handling to safely process potential duplicates during reconnection.</p>
</li>
<li><p>Consider queuing unacknowledged messages for retransmission after re-establishing the Web Socket connection.</p>
</li>
</ul>
<h3 id="heading-state-synchronization-after-disruption">State Synchronization after Disruption</h3>
<p>Web Sockets do not provide delivery guarantees or a way to rebuild state following a disconnection. When connections drop, there is no built-in way to determine what state was lost. You will need to consider designing a mechanism to deal with these scenarios:</p>
<ul>
<li><p>Design efficient delta synchronization protocols to minimize data transfer after reconnection.</p>
</li>
<li><p>Consider event sourcing approaches where clients can request specific missed events.</p>
</li>
<li><p>Build "catchup" mechanisms that determine precisely what updates a reconnecting client needs.</p>
</li>
</ul>
<h3 id="heading-resource-management-and-throttling">Resource Management and Throttling</h3>
<p>Web Sockets lack built-in flow control beyond TCP's mechanisms. This places the burden of resource management and throttling on you as a service provider. You need to consider the following:</p>
<ul>
<li><p>Implement application-level back pressure signals when receivers can't keep up with message volume</p>
</li>
<li><p>Design rate-limiting systems that account for both message frequency and payload size</p>
</li>
<li><p>Consider prioritization schemes for different message types during high-load scenarios</p>
</li>
<li><p>Monitor per-connection memory usage, especially when handling large message fragments</p>
</li>
<li><p>Implement circuit-breakers that degrade service gracefully under extreme load</p>
</li>
</ul>
<h3 id="heading-frame-size-and-message-fragmentation">Frame Size and Message Fragmentation</h3>
<p>Large messages create special challenges with Web Sockets. Web Sockets typically handle a smaller message frame size (e.g. 32kb), which means larger messages are broken up into smaller chunks which need to be reconstituted on the receiving end. You will need to:</p>
<ul>
<li><p>Design protocols that properly handle multi-frame messages, especially during connection instability</p>
</li>
<li><p>Implement timeout mechanisms for partially received fragmented messages.</p>
</li>
<li><p>Consider streaming approaches for large data transfers instead of single large messages.</p>
</li>
<li><p>Test fragmentation edge cases including control frames interleaved with fragmented messages.</p>
</li>
</ul>
<h3 id="heading-standardized-keep-alive-and-health-monitoring">Standardized Keep-Alive and Health Monitoring</h3>
<p>Since Web Sockets are a bi-directional communication channel, both client and server are responsible for detecting the connection stability and health. Your client and server applications will need to consider some or all of the following:</p>
<ul>
<li><p>Establish consistent Ping/Pong usage patterns across your system with clear timing parameters.</p>
</li>
<li><p>Implement application-level heartbeats separate from WebSocket protocol mechanisms.</p>
</li>
<li><p>Design escalating health check procedures that can distinguish between different failure modes.</p>
</li>
<li><p>Consider how firewalls and proxies may affect long-lived connections with timeout policies.</p>
</li>
</ul>
<h3 id="heading-request-response-correlation">Request-Response Correlation</h3>
<p>Web Sockets provide a message stream with no built-in request-response syntax. These are left to the underlying sub-protocol to design for and consider. In order to ensure your protocol and messaging design is robust you should consider:</p>
<ul>
<li><p>Implement message correlation IDs to match responses with their originating requests.</p>
</li>
<li><p>Design timeout and retry mechanisms for requests that don't receive timely responses.</p>
</li>
<li><p>Consider how concurrent operations will be managed without native request multiplexing.</p>
</li>
</ul>
<h3 id="heading-security-implications">Security Implications</h3>
<p>Long-lived connections introduce unique security concerns which must be considered in your design. Think about some or all of the following:</p>
<ul>
<li><p>Implement periodic re-authentication without disrupting connections - tokens expire and you need to refresh them while keeping the connection alive.</p>
</li>
<li><p>Consider rate limiting to prevent DoS attacks - both connection attempts and message frequency need monitoring.</p>
</li>
<li><p>Define clear message validation rules in your sub-protocol - validate all incoming messages before processing them.</p>
</li>
<li><p>Use secure WebSocket connections (wss://) in production to protect data in transit.</p>
</li>
<li><p>Verify the Origin header to prevent cross-site WebSocket hijacking attempts.</p>
</li>
<li><p>Implement timeouts for inactive connections to free server resources.</p>
</li>
</ul>
<h3 id="heading-testing-strategy">Testing Strategy</h3>
<p>Web Sockets require more complex testing approaches due to the challenges already discussed. Think about and consider the following testing strategies to validate your solution:</p>
<ul>
<li><p>Simulate connection failures and recoveries to ensure your designs function as expected, particularly consider the failure scenarios which is where problems and edge cases will occur.</p>
</li>
<li><p>Test partial message delivery and fragmentation to ensure your application is resilient to network interruptions. Web Socket data occurs in frames which are smaller in size to the overall message - this adds to the testing complexities.</p>
</li>
<li><p>Verify correct behavior under high message volumes to ensure your sub-protocol design functions correctly.</p>
</li>
</ul>
<h3 id="heading-protocol-design">Protocol Design</h3>
<p>The sub-protocol you design will impact on the functioning and reliability of your application. When thinking about the sub-protocol design you need to consider:</p>
<ul>
<li><p>Message acknowledgment mechanisms for critical updates. If you need to guarantee a message delivery - this needs to be a specific design choice in your architecture.</p>
</li>
<li><p>Define clear message structures with versioning support so that you can support backwards compatibility.</p>
</li>
<li><p>Consider binary formats (Protocol Buffers, MessagePack) for high-throughput applications.</p>
</li>
</ul>
<p>Designing messaging protocols is not easy, and must be a carefully considered part of your solution design. The sub-protocol should consider many of the elements we have discussed throughout this section, some of them are protocol specific which define the semantics of how the client and server communicate.</p>
<h2 id="heading-remember-web-sockets-dont-pub-sub">Remember Web Sockets don't Pub-Sub!</h2>
<p>Web Sockets provide the communication channel, the responsibility for building reliable, scalable systems on top of this communication channel requires significant additional design and development effort. In my opinion success or failure of your application design comes down to the underlying sub-protocol. When implementing Web Sockets as your underlying communication mechanism make sure that you fully consider the impact of HOW your systems will talk - this will determine whether you succeed or fail because Web Sockets don't do pub-sub, your client and server applications do that - Web Sockets is your data pipe between them and everything else you have to build or buy!</p>
]]></content:encoded></item><item><title><![CDATA[Introducing ServerlessDNA.com - A New Site dedicated to Serverless!]]></title><description><![CDATA[One thing I have learned over the past year while writing blog articles on Serverless to help people learn is that it is not straightforward, and the skills you need are more than just writing software or deploying infrastructure. You need to underst...]]></description><link>https://blog.walmsles.io/introducing-serverlessdnacom-a-new-site-dedicated-to-serverless</link><guid isPermaLink="true">https://blog.walmsles.io/introducing-serverlessdnacom-a-new-site-dedicated-to-serverless</guid><category><![CDATA[serverless]]></category><category><![CDATA[AWS]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Sun, 29 Jan 2023 01:13:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/tZc3vjPCk-Q/upload/88b66a4533e4908170c0818ce869399b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One thing I have learned over the past year while writing blog articles on Serverless to help people learn is that it is not straightforward, and the skills you need are more than just writing software or deploying infrastructure. You need to understand a broad range of topics to design and build solutions with Serverless successfully. This makes blog articles that go deep really long and difficult to read and understand because there is so much to cover!</p>
<p>To be successful with Serverless, you need to have some understanding of the following:</p>
<ul>
<li><p>Infrastructure as Code</p>
</li>
<li><p>Security</p>
</li>
<li><p>Event Driven Architectures</p>
</li>
<li><p>Available Managed Services</p>
</li>
<li><p>Code Frameworks</p>
</li>
<li><p>Software Engineering</p>
</li>
<li><p>and the List goes on ... !</p>
</li>
</ul>
<p>So I have created a new blog site for all my future writing based around smaller, focused articles that are interlinked and weave together to create a complete picture of each topic. The articles are organised into focused groups and will be linked so people can browse through each topic and learn information in smaller doses which will be easier to read, understand and share.</p>
<h1 id="heading-what-is-serverless-dna">What is Serverless DNA?</h1>
<p>The "DNA" stands for "Digital Native Architecture", which is how I see Serverless fitting into the broad world of cloud technology.</p>
<p><a target="_blank" href="https://serverlessdna.com"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674953748491/756bf754-1d1f-4021-aff5-4da4d7cb2fe7.png" alt class="image--center mx-auto" /></a></p>
<p><a target="_blank" href="https://serverlessdna.com">Serverless DNA</a> takes the form of Small Blog articles, "Mini-blogs" to create knowledge about a specific Strand of Serverless Technology. Each Strand of knowledge may be linked to one or more other Strands which are related and should also be looked at to help expand and grow your knowledge of Serverless.</p>
<p>This first release on <a target="_blank" href="https://serverlessdna.com">serverlessdna.com</a> is the MVP of the DNA concept for Serverless knowledge - I want to grow this into more than just my blog site but also enable it as a community resource linking in content from around the globe from other contributors to help categories, link and collate the body of Serverless knowledge out there today. Find out more by browsing the <a target="_blank" href="https://serverlessdna.com/strands/about">About Strand</a> - which is where I will be sharing my public roadmap for the site.</p>
<p>I hope you find this new site useful, and I really look forward to your thoughts and comments on this new beginning for my writing in 2023.</p>
]]></content:encoded></item><item><title><![CDATA[How SAM CLI and AWS Lambda Powertools enable University Students to Start building solutions in less than 60 minutes]]></title><description><![CDATA[I am an Advanced Application Engineering Manager at Accenture in Australia and work hard advocating for Serverless Technology, accelerating teams new to building Serverless solutions on AWS. I design and build Serverless solutions using Event-Driven ...]]></description><link>https://blog.walmsles.io/how-sam-cli-and-aws-lambda-powertools-enable-university-students-to-start-building-solutions-in-less-than-60-minutes</link><guid isPermaLink="true">https://blog.walmsles.io/how-sam-cli-and-aws-lambda-powertools-enable-university-students-to-start-building-solutions-in-less-than-60-minutes</guid><category><![CDATA[serverless]]></category><category><![CDATA[AWS]]></category><category><![CDATA[lambda]]></category><category><![CDATA[Devops]]></category><category><![CDATA[getting started]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Sat, 14 Jan 2023 08:17:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/340c04358f2ba3fccd5c657b9a3a978d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am an Advanced Application Engineering Manager at Accenture in Australia and work hard advocating for Serverless Technology, accelerating teams new to building Serverless solutions on AWS. I design and build Serverless solutions using Event-Driven Architecture (EDA) and have been running a regular workshop called "Serverless Works!" internally at Accenture for the past two years. Another fun thing I do is run a 60-minute "Serverless Works!" session at the Accenture Technology Boot camps in Australia and New Zealand.</p>
<p>Accenture's technology boot camps are run throughout the year and bring together University students who are interested in technology careers and put them through a 4-day immersive boot camp experience with mentors from the Accenture MyWizard technology team to guide and support them. Students are put together in small project teams (4-6 students) and are assigned a project. Throughout the 4 days workshops are run on Design thinking, Project management and other technology-related topics to help them with their learning and to enable them to instantly apply what they learn in building their projects. To assist with the short timeframe, boot camps since 2021 have encouraged using AWS Cloud and Serverless technology. This is where I come in at the start of day 2 and do a "Serverless Works!" presentation to show them all how easy and fast it is to get started building with AWS Managed Services.</p>
<p>Most students who attend Accenture boot camps have no exposure to any cloud provider, so this workshop is really important to enable them to make a start. I introduce Serverless as a term and why it's such an important technology when wanting to build solutions quickly to deliver instant business value and provide instant scale meaning anything you build quickly is capable of running live with thousands of users with little to no effort or additional knowledge. Following this, I start demystifying the AWS Console - introducing what the cloud looks like and pointing out important services they will likely want to learn more about over the next four days - CloudFormation, CloudWatch, DynamoDB, API Gateway and Lambda. Next, I talk about the tools I use for my live coding demo to show them how easy it is to get started.</p>
<p>This is my list of getting-started tools and utilities for the boot camps:</p>
<ol>
<li><p>An AWS Cloud Account</p>
</li>
<li><p>Leapp application from <a target="_blank" href="https://www.leapp.cloud/index.html">Leapp.cloud</a> to secure access to their AWS account without needing to store Access Key and Secret Key on their computer unencrypted.</p>
</li>
<li><p><a target="_blank" href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started.html">SAM CLI</a> for deploying their application to the cloud</p>
</li>
<li><p><a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> (or another code editor of their choice for editing files)</p>
</li>
<li><p><a target="_blank" href="https://www.python.org/downloads/">Python</a> installed and working on their computer</p>
</li>
<li><p><a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python">AWS Lambda Powertools for Python</a> to make writing AWS Lambda code faster and safer.</p>
</li>
</ol>
<p>The above sounds like a daunting list, and by this time, many students are likely to wonder what it is I am talking about, and that's when I kick into a live coding demo and bare my developer soul to them. The solution I build is a simple REST API with AWS Lambda performing CRUD operations on DynamoDB - this is my target architecture, and I strive to get it finished in my workshop timeframe but do not always get there as I do talk a lot, and I get excited by Serverless!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672555436970/9c364e53-962f-4ba8-91b9-a43bcfd36337.jpeg" alt class="image--center mx-auto" /></p>
<p>It is amazing how fast it is to build a REST API these days with SAM CLI using the sample project wizard via <strong><em>sam init</em></strong>. With SAM CLI, I always start with the hello world sample application, which provides a nice project structure.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672639771632/a404466b-dcde-44f5-a825-8c98e51edf53.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-1-execute-sam-init">1. Execute: sam init</h4>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="abb9eb12f8dd1ad13eec2e1b3d140085"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/abb9eb12f8dd1ad13eec2e1b3d140085" class="embed-card">https://gist.github.com/walmsles/abb9eb12f8dd1ad13eec2e1b3d140085</a></div><p> </p>
<p>Within minutes a demo python project using the "Hello World" python template, and I can build and deploy it to the cloud.</p>
<h4 id="heading-2-execute-sam-build">2. Execute: sam build</h4>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="be0ec4a92a05d2e298b2234bd80c11c1"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/be0ec4a92a05d2e298b2234bd80c11c1" class="embed-card">https://gist.github.com/walmsles/be0ec4a92a05d2e298b2234bd80c11c1</a></div><p> </p>
<p>I do this from an empty folder screen sharing my terminal so they can see first-hand how quick and easy it is to use the tools.</p>
<h4 id="heading-3-execute-sam-deploy-guided">3. Execute: sam deploy -guided</h4>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="b4c1ba7f3ad59c3e8ad3e368d2ab2ccd"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/b4c1ba7f3ad59c3e8ad3e368d2ab2ccd" class="embed-card">https://gist.github.com/walmsles/b4c1ba7f3ad59c3e8ad3e368d2ab2ccd</a></div><p> </p>
<p>While it deploys, I open the AWS Console and show the Cloudformation console and the stack creation, expanding the details to show the resources. I click the quick links to open the resources in their respective consoles to show how to find what is being built and show them in the console so they can build a mental model of a development workflow and visually start linking the consoles together, so they are less mysterious. Let's be honest - the AWS Console is an abstract, mysterious place that is not easy to get your head around when building a solution with so many different services.</p>
<h2 id="heading-introducing-aws-lambda-powertools-for-python">Introducing AWS Lambda Powertools for Python</h2>
<p>The last part of my live coding is refactoring the "Hello World" API lambda code using AWS Lambda Powertools for Python. I start by quickly importing AWS Lambda Powertools for Python and work through the following steps to refactor:</p>
<ol>
<li><p>Import Logger and Tracer</p>
</li>
<li><p>create instances of these</p>
</li>
<li><p>Import APIGatewayRestResolver - While doing this, I explain how similar the utility is to python fast-API and node express libraries since students may be familiar with these from less serverless projects they have built.</p>
</li>
<li><p>Rework the Lambda handler to use the APIGatewatRestResolver class</p>
</li>
</ol>
<p>Pretty quickly, we end up with code like this with the API route changed slightly to enable some dynamic responses based on input with a little fun thrown in.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="463d2a953cdfaa351af6dc3c473ad8bb"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/463d2a953cdfaa351af6dc3c473ad8bb" class="embed-card">https://gist.github.com/walmsles/463d2a953cdfaa351af6dc3c473ad8bb</a></div><p> </p>
<p>The refactored code can be easily updated and re-deployed:</p>
<ol>
<li><p><strong>sam build</strong></p>
</li>
<li><p><strong>sam deploy</strong></p>
</li>
</ol>
<p>Once deployed, the demo now changes to show how the dynamic routes work, and I start to talk about how in a lot of cases, boot camp projects often involve Web or mobile app front-ends and dredge up everyone's dreaded API pain - Cross Origin Resource Sharing (CORS). So we add in Lambda Powertools CORS handling option (2 minor changes) and, at the same time, throw in how we can add in some AWS Lambda event logging to make debugging applications easier when they get building.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="4015e161247de0553f932b0a9d84f137"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/4015e161247de0553f932b0a9d84f137" class="embed-card">https://gist.github.com/walmsles/4015e161247de0553f932b0a9d84f137</a></div><p> </p>
<p>At this point in my demo, students have enough to get started with building and depending on the time I have left, I will keep going to add in DynamoDB and CRUD API methods or stop and open the floor for questions - the detail of building out a CRUD API is less important since by this time I have achieved the goal of my workshop. It is often far more important to engage with the students at this point and enable them to ask questions so I can address any specific concerns from the group</p>
<p>The final slide before I complete my workshop is a complete list of links for all the tools, demos, AWS online workshops, tutorials and sample code links. I do offer up a complete <a target="_blank" href="https://github.com/walmsles/bootcamp_demo">working boot camp demo repo</a> on my GitHub with the complete CRUD API built and ready to deploy to help accelerate the project teams. I don't feel this is going too far because the students have never used AWS before, and this is the first time they are hearing about Serverless. The sample code provides a nice basis for them to get started in something more real, and they can achieve instant success which is important for a 4-day immersive experience.</p>
<h2 id="heading-why-choose-sam-cli">Why Choose SAM CLI?</h2>
<p>I choose SAM CLI because I find this an easy and approachable tool which has a nice wizard-style approach to getting started and makes building and deploying straightforward to get going. I also provide links to AWS Workshops and the first workshop in the list is <a target="_blank" href="https://aws.amazon.com/getting-started/tutorials/run-serverless-code/">Create A Hello World Lambda F</a>unction (using SAM CLI) so it ties in well and reinforces my live coding demo!</p>
<h2 id="heading-why-aws-lambda-powertools">Why AWS Lambda Powertools?</h2>
<p>This is simple - because every Serverless project that writes code for Lambda should use this library. It provides AWS Serverless best practice principles right out of the box and with very little effort. I encourage every serverless team I work with to use AWS Lambda Powertools, and I also strongly push Python as the language they should be building with. This utility library does enable Serverless teams to run faster by providing standard approaches to Logging, Tracing, Efficient Metrics, Resolvers for APIs (all types) and Stream batch handlers which make writing SQS handler functions safer and more robust - so many AWS Well-Architected principles are embodied in AWS Lambda Powertools. It simply makes coding faster.</p>
<p>Seeing project teams embracing Serverless technology with a single workshop presentation is refreshing to experience. I love presenting my <em>Serverless Works! Boot Camp</em> edition for University students attending Accenture's Technology boot camps. I am also addicted to writing code in front of developers to showcase new techniques or ways of building. Sharing my knowledge and love of Serverless building and doing it immediately with instant effect is very satisfying.</p>
]]></content:encoded></item><item><title><![CDATA[Serverless Testing, Part 3: Simplify Testing with SOLID Architecture]]></title><description><![CDATA[Serverless Testing, Part 1: What I forgot at the beginning starts with what I forgot when I started building serverless solutions. It encourages bringing back software architecture principles to your Serverless code and focuses on the Dependency Inve...]]></description><link>https://blog.walmsles.io/serverless-testing-part-3-simplify-testing-with-solid-architecture</link><guid isPermaLink="true">https://blog.walmsles.io/serverless-testing-part-3-simplify-testing-with-solid-architecture</guid><category><![CDATA[serverless]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[Testing]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Fri, 30 Dec 2022 05:08:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/fd643b978b2d8985fc082751aadbd901.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><a target="_blank" href="https://blog.walmsles.io/serverless-testing-part-1-what-i-forgot-at-the-beginning"><strong>Serverless Testing, Part 1: What I forgot at the beginning</strong></a> starts with what I forgot when I started building serverless solutions. It encourages bringing back software architecture principles to your Serverless code and focuses on the Dependency Inversion Principle - something all developers should understand.</p>
<p><a target="_blank" href="https://blog.walmsles.io/serverless-testing-part-2-solid-architecture-for-lambda"><strong>Serverless Testing, Part 2: SOLID Architecture for Lambda</strong></a> covers SOLID software architecture principles and how it applies to Serverless. It also introduces how to implement Hexagonal Architecture using software interfaces and adapters as a software abstraction to isolate your business domain code from the details of interacting with the cloud.</p>
</blockquote>
<p>In this final piece of the puzzle, I will cover how implementing your business domain logic with Hexagonal SOLID software architecture principles will simplify testing of your code and do away entirely with mocking AWS SDK calls to the cloud. This article focuses on unit testing, integration testing and end-to-end tests for the example architecture presented.</p>
<blockquote>
<p>Testing Serverless systems is not a "now we have to simulate the cloud problem".</p>
</blockquote>
<p>Testing of code destined for the cloud is a confusing space with many opinions and tools being built to support all manner of local, remote, semi-remote and local-remote testing of your code. This article is born from my continued frustration with developers wanting to replicate the cloud locally so they can run their entire solution end to end on their desktop. This is more a desire to continue developing the way they have always developed rather than looking to change development practices to suit the changing deployment landscape that Serverless brings to us.</p>
<h2 id="heading-a-quick-word-on-mocking">A Quick word on "Mocking"</h2>
<p>A frustration of mine with Serverless developers is watching them waste time mocking AWS SDK Calls. Mocking AWS SDK calls is never straightforward and usually requires several additional libraries to make it an effective technique. Combined with this you may have junior or less cloud-experienced developers on your team who struggle with mocking AWS Calls, mainly because they don't understand the nuances of the API call responses.</p>
<p>Please do not mistake this to mean that I think mocking is something we should eliminate. Mocking is vital for good unit testing and is something I strongly encourage and is needed. However, not for AWS SDK Calls - we can use better software architecture to remove the need for mocking AWS SDK calls. In the remainder of this article, I will explain how better software architecture will improve and simplify your Serverless testing activities and enable your team to move faster.</p>
<h2 id="heading-the-github-example-project">The GitHub Example Project</h2>
<p>This article is supported by an example python lambda project that applies the testing techniques discussed in this article; You can find it on my GitHub <a target="_blank" href="https://github.com/walmsles/aws-lambda-python-cdk">here</a>. This article does not go into every aspect of this project, it is used only as a reference to the testing strategies introduced within this article.</p>
<h4 id="heading-a-few-notes-on-the-project">A Few Notes on the Project</h4>
<ul>
<li><p>The sample project is focused on general Serverless testing for synchronous API services with some asynchronous components. It does not cover all the possible testing scenarios for serverless and is intended as a general example to get started with Serverless testing more quickly and for a majority of Serverless use cases.</p>
</li>
<li><p>The README.MD file has notes on the project structure and elaborates on the directory structure and how the project is built.</p>
</li>
<li><p>AWS CDK is used to deploy the code to your Cloud using the Python language</p>
</li>
<li><p>The Python folders are structured in a very specific way to ensure the lambda code directory supports the <strong>PythonFunction</strong> CDK construct which uses docker to package the code to ensure binary packaged dependencies will function correctly when deployed.</p>
</li>
<li><p>Minimal scripting magic for building and deploying the project working within the existing folder structures at all times. This minimises the need for build magic where some developers build scripts to move source files around for lambda packaging.</p>
</li>
<li><p>Relies on <a target="_blank" href="https://python-poetry.org/">poetry</a> for packaging and uses a specific convention for installing dependencies in groups for each of the lambda functions present in the project. This will ensure dependencies are packaged only where required and will ensure faster start-up times which is important for AWS Lambda.</p>
</li>
<li><p>Uses the <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python">AWS Lambda Powertools for Python</a>. No other reason for using this library other than all projects should. If you don't know what it is click the link or check out my talk from Melbourne Serverless Meetup: <a target="_blank" href="https://www.youtube.com/watch?v=M1lxr_sNgd4">What I love about AWS Lambda PowerTools for Python</a>.</p>
</li>
<li><p>Pytest is used for unit testing and for integration and end-to-end test modules tear up/down mechanisms are used to create ephemeral stacks for testing. I borrowed this testing technique from the <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python">AWS Lambda Powertools for Python</a> project - I do think you should check it out if you haven't already!</p>
</li>
<li><p>The code within the GitHub project is not a production-ready sample, so use it with caution and modify it to meet your production system requirements.</p>
</li>
</ul>
<h2 id="heading-better-architecture-with-hexagons">Better Architecture with Hexagons</h2>
<p>I wrote about the Dependency Inversion Principle, SOLID Principles and Hexagonal architecture in the previous parts of this series. Hexagonal architecture is an architectural pattern that splits your system into distinct components (not layers) and introduces a combination of Ports and Adapters which separate your business domain logic from external resources such as databases and cloud services like API gateway, Eventbridge, SQS and DynamoDB. The hexagon shape itself is unimportant and is used to define where the adapters exist within the software. In Alistair Cockburn's <a target="_blank" href="https://alistair.cockburn.us/hexagonal-architecture/">original article</a>, the primary motivation for this pattern was to remove the bleeding of functionality between software layers in more traditional, monolithic solutions which countered all the advantages of cleanly layered software with isolation boundaries. By introducing smaller, more specific interfaces used by business logic to achieve a business outcome, the abstraction to external services is more clearly defined and less ambiguous using the Hexagonal architecture idea.</p>
<p>I like the way this architecture is laid out as it helps to quickly understand what are the inputs, where the processing is and the outputs which maps very clearly to how AWS Lambda code works in the cloud.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668623384458/M0weUQTLA.png" alt="serverless-testing-hexagonal-arch-background.png" /></p>
<p>On the left are the driving adapters for the AWS event sources that drive the event data into our business domain logic through the defined ports (interfaces). On the right are the external cloud resources driven by our business domain logic - where data is stored or pushed for further processing. Our business domain logic interacts with these driven services through interfaces where the adapter implementation makes AWS SDK calls to interact with the cloud resources.</p>
<h2 id="heading-testing-unit-integration-and-end-to-end">Testing: Unit, Integration and End to End</h2>
<p>Combining all these elements can simplify the testing of business domain logic and remove the need to create Mocks for AWS SDK calls. Testing is split into three main areas - Unit, Integration and End to End testing. Unit testing covers the central Business Domain logic, integration testing covers the driven ports side of the hexagonal architecture while the end-to-end tests start with the driving side and continue across the entire architecture.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672375804752/3d7724d0-3cb1-48fd-a2e4-3f3569500a0a.jpeg" alt class="image--center mx-auto" /></p>
<p>Your business domain or Service logic is covered by <strong>unit tests</strong>. With hexagonal architecture, any AWS SDK calls will be isolated by the adapters, behind the ports meaning there will be no AWS SDK calls within your business domain code, so there is no need to introduce AWS SDK mocking libraries. This will simplify your testing and give your developers more time to focus on delivering more value.</p>
<p>The interfaces and adapters are tested using automated integration and end-to-end tests run when your code is deployed to the cloud using CI/CD automation tooling. With tests running in the cloud, we don't need to mock AWS SDK calls since we will be testing with real services. Combining these activities also removes the notion that you need to run the entire solution locally in a mock cloud environment on your desktop so you can set break points in your IDE to help catch errors or see the state of variables.</p>
<p>You can achieve local debugging by running your unit tests in debug mode, fully exercising your business logic, and this is a workflow I encourage all serverless teams to adopt. All you need to do is craft one or more test fixtures for specific test cases and add these as unit tests to your existing test cases. You can execute tests using your IDE in debug mode and set up breakpoints in the exact spot you want to step through. This not only enables local testing with no mocking and no cloud simulation but also ensures you expand your test cases with additional cases that are more real and not just there because we need test coverage. With SOLID Hexagonal architecture in place if you interact with reading data from a database - this will happen in a Port and Adapter. So you can create a fake adapter to return the data your test needs to create the correct outcome.</p>
<h2 id="heading-a-reminder-the-core-principle-of-lambda">A Reminder: The Core Principle of Lambda</h2>
<p>As we gain more confidence in designing and building serverless systems with more mature managed services on the cloud, we are starting to create complex, distributed systems that scale, run fast, and efficiently. As we travelled to this point, learning about how each of the AWS primitive services work; we have forgotten the principle of how Lambda functions are intended to work.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663939514903/1y-TvBEdV.jpg" alt="serverless-testing-part3-lambda-principle.jpg" class="image--center mx-auto" /></p>
<p>In a nutshell, Lambda functions exist to add value to data events entering our system and then forward the result to the next component in the solution chain. This next component could be anything and may involve other managed services such as AWS SQS, SNS, EventBridge or API gateway. At the end of the day, to unit test our business logic, we need to pass a test data event and check the data forwarded to the next component in our solution chain after our business domain code has run. Doing this does not involve mocking AWS SDK calls, nor does it require us to run a mock SQS service locally so we can test end to end.</p>
<h2 id="heading-testing-serverless-architectures">Testing Serverless Architectures</h2>
<p>Let's look at an API gateway interface that stores events into an S3 bucket and then pushes a transformed event into an SQS Queue. The data pushed to the SQS queue is processed by another Lambda function and is inserted into a DynamoDB table. A classic serverless pattern that is widely used as a store and forward mechanism.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663939366309/8LrKrFIC5.jpg" alt="serverless-testing-part3-Simple Architecture.jpg" class="image--center mx-auto" /></p>
<p>Taking the above architecture, we can isolate and identify the testing boundaries to simplify the creation of unit tests and eliminate mocking of AWS SDK calls altogether. Let's overlay the hexagonal architecture concepts of ports, adapters and business logic to show how our Service logic is encapsulated.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672192591657/1e98996e-7874-46d3-a2c9-7006ebbd1e54.png" alt class="image--center mx-auto" /></p>
<p>From the architecture overlay, everything within the Lambda symbol represents the unit test boundary; The outbound arrows from each lambda box represent the integration test boundary (2, 3 &amp; 5) and the entire numbered flow (1 - 5) represents the end-to-end test boundary. The overlay also emphasises the core principle use of lambda: Data Event in (1 &amp; 4) + Lambda Processing Logic = Valuable Business Outcome (2, 3 &amp; 5).</p>
<h3 id="heading-unit-testing-lambda-code">Unit Testing Lambda Code</h3>
<p>Using the above boundaries enables you to define where code should live. If we adhere to the hexagonal architecture principles outlined in my second article - Our Service logic will be contained in a service class (or function) which is called by the code in your actual lambda handler. The lambda handler function represents the adapter portion of data event input at <strong>step 1</strong> and all it will be doing is validating the input and mapping it to the <strong>ports</strong> (interfaces) of your service class.</p>
<p>The following code snippet is taken from the GitHub project and represents the first lambda handler function which is triggered by an AWS API REST Gateway event. The project uses the AWS Lambda Powertools for python to assist with handling the AWS Lambda event (I did add a few comments in the code and moved the handler for clarity).</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="3274fd25a1e0bcd62589ded8dad7d2a8"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/3274fd25a1e0bcd62589ded8dad7d2a8" class="embed-card">https://gist.github.com/walmsles/3274fd25a1e0bcd62589ded8dad7d2a8</a></div><p> </p>
<p>The <strong>post_event</strong> function which handles the API route for the inbound data maps the API body details to the <strong>process_event</strong> method of the <strong>event_service</strong> class. This is a nice way of isolating the different software components that make up your lambda handler and will ensure each of the components are individually testable. This ensures unit tests are small, concise, and easy to understand. This also enables your service logic to be re-used in other AWS Serverless Service contexts without making any changes and means faster development time and less change from a unit test perspective if your solution needs to pivot or change, which is often the case as requirements and volumes change.</p>
<blockquote>
<p>Using better software architecture will simplify your Lambda handler function code - don't put everything in your lambda handler unless it is super simple. Any specific business logic should be isolated within a service class or function so that it can be transportable, easier to test and quicker to understand.</p>
</blockquote>
<p>The unit tests for this API service are focused on the <strong>EventService</strong> class which is defined in the <em>adapters</em> folder in the <a target="_blank" href="https://github.com/walmsles/aws-lambda-python-cdk/blob/main/services/event_api/runtime/adapters/service.py"><em>service.py</em></a> file and represents the central blue section on the hexagonal architecture diagram earlier. Unit tests are about testing our business domain service logic, not testing the Lambda handler function. The EventService is responsible for 2 actions - storing the event in an AWS S3 object and pushing the event data into an SQS queue for processing by another Lambda function. The Event Service uses Hexagonal architectural techniques and uses 2 ports - <a target="_blank" href="https://github.com/walmsles/aws-lambda-python-cdk/blob/main/services/event_api/runtime/adapters/ports/file_port.py">FileStoragePort</a> and <a target="_blank" href="https://github.com/walmsles/aws-lambda-python-cdk/blob/main/services/event_api/runtime/adapters/ports/message_port.py">MessagePort</a>. To unit test the <em>EventService</em> we will need to provide fake or mock implementations for these two ports, mocks are still used but we are using specific mocks which do not require AWS knowledge or external mocking libraries.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672230750992/1eeebfe3-7b86-4c03-9f71-6fa786cae085.png" alt class="image--center mx-auto" /></p>
<p>This diagram shows how our EventService logic calls the FileStoragePort (Fake S3 adapter) and MessagePort (Fake SQS Adapter) which are both replaced by fake implementations to support unit testing. The complete unit test is shown in the <a target="_blank" href="https://github.com/walmsles/aws-lambda-python-cdk/blob/main/tests/unit/test_event_service.py"><em>test_event_service.py</em></a> file in the sample project. The constructor of the EventService allows for overriding the adapter implementations that are used which enables fakes to be used for testing purposes where needed. This is a classic Dependency Injection technique where dependencies of a class (or function) are provided during creation so that implementation can be changed when desired.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="00880f29affe464dfc80a0064f07a8ed"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/00880f29affe464dfc80a0064f07a8ed" class="embed-card">https://gist.github.com/walmsles/00880f29affe464dfc80a0064f07a8ed</a></div><p> </p>
<p>Some people will be thinking - what about unit testing the Lambda handler function? In this project have opted not to do that, sure it added to unit test coverage but the lambda handler code is very simple and will be tested as a part of the integration tests we go through next. I have some strong opinions when it comes to test coverage and feel that many hours of developer time are wasted on creating tests that deliver no purposeful quality improvements. If the code is critical to your business function you should be writing unit tests. If the code adds no real business value and is acting as an enabler or bridge to your business function then you need to decide as a development team if testing is useful and meaningful.</p>
<h3 id="heading-a-word-on-code-coverage-metrics">A Word on Code Coverage Metrics</h3>
<p>Code coverage percentage metrics matter for some projects, but not all. In my experience, attaining a goal metric for code coverage with automated testing adds very little to the overall quality of the software you deliver and does not add to an equitable increase in confidence that the code is correct. I believe it wastes valuable developer time and reduces quality because the team are no longer focused on delivering your business value, they are focused on making the code coverage percentage get to the right value or exceed it. In some cases, they will create tests that are invalid just to achieve the metric goal or will focus on the low-hanging fruit and miss out on tests on critical business functions. This is why I do not like mandatory code quality metric targets - they do not add value or increase quality, they just consume development time for no business value.</p>
<blockquote>
<p>Automatic tests need to add to your overall product quality and be valuable.</p>
</blockquote>
<p>I believe code coverage is vital when creating shared code libraries that are used by more than just your project or that are shared publically or within your organisation, these should strive for as much test coverage as possible since the testing adds value and confidence for all users of your library.</p>
<h3 id="heading-integration-testing-example">Integration Testing Example</h3>
<p>In the sample project on GitHub all of the integration tests can be found in the <a target="_blank" href="https://github.com/walmsles/aws-lambda-python-cdk/tree/main/tests/integration"><em>tests/integration</em></a> folder with subfolders for each service to be tested. We will remain focused on the <a target="_blank" href="https://github.com/walmsles/aws-lambda-python-cdk/tree/main/tests/integration/event_api"><em>event_api</em></a> service for integration testing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672364917488/6cf810a2-80a0-4368-97c5-a24ebec08627.png" alt="Integration Testing example architecture with hexagonal overlays" class="image--center mx-auto" /></p>
<p>For integration testing, we are deploying actual AWS Cloud service components which will be tested in the cloud to ensure they integrate correctly. The actual deployment is handled by the pytest module <em>conftest.py</em> which will ensure the components are deployed before executing the integration tests and remove the Cloud Formation stack when the test execution is complete. The Integration test code will use one or more test fixtures representing the real AWS API Gateway event the lambda code will receive when integrated with the API gateway. The test we are writing is focused on the outbound arrows in our architecture (2 &amp; 3) and nothing else. The test infrastructure will deploy the Lambda, S3 bucket and SQS queue and then test the lambda through a synchronous execution using the AWS Lambda execution API call.</p>
<p>The idea is to break down your integration tests into small chunks that can be completed synchronously, which makes writing tests for asynchronously triggered Lambda calls (e.g. triggered by AWS SQS) much simpler and easier to deal with since we no longer need to poll for results, this is why we set up integration tests in this way. The following example shows the complete integration tests for the Event API component triggered by the AWS API gateway. With our lambda and resources deployed to the cloud we are now testing our AWS SDK api calls within our adapters and will be sure that if these tests pass, they will continue working as we expect.</p>
<blockquote>
<p>Testing AWS SDK API calls in the cloud means you are testing all the cloud integration including configurations such as IAM Roles and Policies along with your actual Adapter code.</p>
</blockquote>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="0320db67ff12fafea0785b4bf8265924"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/0320db67ff12fafea0785b4bf8265924" class="embed-card">https://gist.github.com/walmsles/0320db67ff12fafea0785b4bf8265924</a></div><p> </p>
<p>Lines 17 - 30 outline test fixtures which retrieve actual cloud resource details required for the AWS SDK calls we will make to interact with these services. When the test calls the AWS Lambda execution API passing the AWS API Gateway test fixture (line 39) it is a synchronous call so when we receive the response we can be sure all integrated actions have been completed so we will be able to immediately check the S3 bucket for the desired outcome (lines 53 - 56) and check the message pushed to the AWS SQS queue is correctly formatted for the next service to consume (lines 58 - 67). This makes testing simpler and more easily understood and reasoned about by developers of all levels. The idea of running tests in this way is to enable your team to move faster and write smaller, more concise integration tests.</p>
<h3 id="heading-end-to-end-testing-example">End-to-End Testing Example</h3>
<p>Testing Serverless Architectures end-to-end is probably the most difficult part and has the most obstacles - especially when one or more asynchronous services make up your solution. In the sample project and testing, I have tried to keep all testing straightforward using synchronous mechanisms to minimise testing complexity and developer cognitive load. Given we have tested the <em>event API</em> Lambda code in our integration testing using a real AWS Gateway event and we know it correctly interacted with downstream services the end-to-end test shouldn't need to test these components again.</p>
<blockquote>
<p>Designing tests to execute synchronously allows your developers to move faster and your testing is less complicated and easier to understand and reduces false positive results for asynchronous polling</p>
</blockquote>
<p>So the end-to-end component is all about deploying the API gateway and the API lambda code along with dependent downstream services. This means we can limit the testing to the API boundary and ensure the API gateway integration works correctly by giving us the response we are expecting according to the API contract. You could consider this an API contract test as well and that is really what we are doing in this example and is what the end-to-end component is really about - does the system behave correctly for external users.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672375552659/c57b3d29-02a2-4207-8bc1-ae197b536219.png" alt class="image--center mx-auto" /></p>
<p>Applying the End-to-End as shown in the above architecture diagram means we progressively test each of our components in the cloud in a synchronous fashion. The final part is to test the API gateway triggering Lambda and that we get the correct response for each of our test cases. In our example, we have a fairly simple API and only a single test to illustrate the strategy.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="981cb19aa916367b0f4d24f9de47cb28"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/walmsles/981cb19aa916367b0f4d24f9de47cb28" class="embed-card">https://gist.github.com/walmsles/981cb19aa916367b0f4d24f9de47cb28</a></div><p> </p>
<p>The end-to-end test uses the same fixture interface to get the real API gateway address for the deployed API gateway infrastructure.</p>
<p>I have focused my testing examples around an API gateway-bound service design which is one of the more common serverless system implementations. It is by no means the only type of implementation and trying to go into more detail about every aspect of Serverless system testing would take far more than I have written here. I have focused on a specific example as a means to help you understand how you can achieve automated testing of a Serverless Architecture in a synchronous fashion and without mocking AWS SDK Calls. The idea is to be able to apply testing that makes sense for your projects and enable your developers to move quickly and empower new developers to also get going fast!</p>
<p>Through the use of SOLID Hexagonal software architecture and synchronous testing techniques in the cloud for integration and API Contract testing, you can remove the need for using AWS SDK development mocking libraries and empower new developers to get started and understand your business code faster and easier.</p>
<h4 id="heading-github-code-disclaimer">GitHub Code Disclaimer</h4>
<p>The examples presented are focused on the Python programming language and the code presented uses object-oriented programming. Python is not the only language for coding with AWS Lambda and the solution samples may not suit your preferred language.</p>
<p>The code presented is not considered production ready and should not be used in production without ensuring it meets your development standards and code quality requirements.</p>
<h3 id="heading-further-reading">Further Reading</h3>
<p>I strongly recommend reading <a class="user-mention" href="https://hashnode.com/@theburningmonk">Yan Cui</a>'s blogs and also checking out his new course on Serverless testing which is in development (links below).</p>
<ul>
<li><p><a target="_blank" href="https://testserverlessapps.com/">Testing Serverless Architectures</a></p>
</li>
<li><p><a target="_blank" href="https://serverlessfirst.com/emails/talking-testing-with-yan-cui-podcast/">Talking testing with yan Cui - Serverlessfirst podcast</a></p>
</li>
<li><p>Yan's blog - <a target="_blank" href="https://theburningmonk.com/">theburningmonk.com</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Understanding Lambda Execution Models]]></title><description><![CDATA[Know the Services you are using; really know them. Understanding how they work and fail is essential for constructing a resilient serverless architecture.

This is the number one piece of advice I share with new Serverless Engineers. Knowing the AWS ...]]></description><link>https://blog.walmsles.io/understanding-lambda-execution-models</link><guid isPermaLink="true">https://blog.walmsles.io/understanding-lambda-execution-models</guid><category><![CDATA[serverless]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Sat, 30 Apr 2022 06:15:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/0BZcsD8UVmM/upload/v1651299193827/kljj850Cv.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Know the Services you are using; really know them. Understanding how they work and fail is essential for constructing a resilient serverless architecture.</p>
</blockquote>
<p>This is the number one piece of advice I share with new Serverless Engineers. Knowing the AWS Services you plan to build with is essential. A vital aspect of this is how your Lambda is invoked and what this means. Lambda execution models can be broken down into three groups, Synchronous, Asynchronous and Event Source Mappings.</p>
<h2 id="heading-synchronous-invocations">Synchronous Invocations</h2>
<p>This is the simplest execution model - this is where all parties in the lambda execution chain are involved end to end in the transaction and wait for a response. In this model, error handling falls back to your consumers since failures are communicated directly via the lambda or service response. This means you consumers will be responsible for any retry behaviour on failure.</p>
<p>A common pitfall with synchronous invocations is your lambda timeout configuration which can be up to 15 minutes. You need to be careful setting this limit since there is no guarantee your clients will wait this long for the lambda execution to complete. The most common timeout error for synchronous invocations is when lambda is triggered by the API gateway which has a 29-second hard timeout limit. You need to make sure your lambda timeout configuration makes sense within your overall solution.</p>
<p>Since error handling falls back to consumers, the synchronous lambda invoke model is the simplest to code for. Being invoked synchronously means you must carefully consider rate limiting and throttling within your overall solution so that your lambda does not get over-run and cause problems for downstream services.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651291487470/YRMxM1jOh.jpg" alt="Lambda Syncrhonous Invoke Sources" /></p>
<h2 id="heading-asynchronous-invocations">Asynchronous Invocations</h2>
<p>Asynchronous invocations are where the caller of your Lambda does not wait for the outcome. They trigger the Lambda and then go on with whatever else they need to do; all they care about is whether the triggering of the Lambda was successful and nothing else about what your Lambda code is doing. Any errors from your Lambda invocation go nowhere since nobody is waiting for the result. Asynchronous invokes are great for enabling greater scale within your solutions at the expense of adding additional complexity when it comes to handling errors. With this invocation model, it is the responsibility of your Lambda code or configuration to take care of all error handling.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651295704115/Vx8Y6Mg6A.jpg" alt="invoke models-asynchronous invoke.jpg" /></p>
<p>A common mistake serverless developers make with AWS Lambda and the asynchronous invoke model is expecting when their Lambda fails that the service performing the invocation will actually know something about the failure. This will never be the case - the service performing the invoke will only know about the success or failure of the actual invocation action, which returns immediately the Lambda function is triggered successfully. This is a common misunderstanding of how the Eventbridge Dead Letter Queues work - these queues will only capture the Eventbridge events where an asynchronous invoke of the target from the Eventbridge rule failed (after 24 hours of retries) not when the lambda code invoked returns an error response - subtle difference but an essential lesson in knowing how the AWS services you are using work!</p>
<p>AWS Lambda has introduced several nice features around the asynchronous event model to assist with lambda execution failures. By default, an asynchronous Lambda invocation allows for up to two retries on Lambda execution failure with some backoff and delay mechanisms you can configure. This gives you a very simple retry mechanism out of the box. You also have the option to configure a Dead Letter Queue for asynchronous invocation failures when the two retries ultimately fail to execute your code successfully. If this small out of the box retry and dead letter queuing is not enough, the Lambda service also introduced Lambda destinations for the asynchronous invocation model only.</p>
<h3 id="heading-lambda-destinations">Lambda Destinations</h3>
<p>Lambda Destinations is a great feature that allows you to configure several destination types on either success or failure of the asynchronous invocation. You can forward success or failure events to multiple destinations: SNS, SQS, Lambda or Eventbridge Eventbus. This provides you with many options for dealing with failures for your asynchronously invoked lambdas. With Lambda Destinations the event content provided to the destination contains details about the request and response in JSON format, this differs from Dead Letter Queues, where Lambda only sends the content of the event, without details of the response.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651293058176/x04AUx1mD.png" alt="Lambda Destinations Console" /></p>
<h2 id="heading-event-source-mappings">Event Source Mappings</h2>
<p>The Event Source Mapping is an AWS provided polling mechanism for triggering Lambda from a stream or queue which are not capable of invoking Lambda directly. This mechanism causes Lambda functions to be invoked synchronously with batches of records which can be configurable in size depending on the event source. As we have learned, the caller in the synchronous invoke model is responsible for handling any error response, and the Event Source mapping is no exception. The Event Source Mapping will handle errors in specific ways depending on the AWS Managed service being used - I recommend you read up on how each service behaves with Event Source Mapping failures - another example of knowing your services!</p>
<blockquote>
<p>So the funny thing with Event Source mappings is that the lambda execution mode is a synchronous call, even though from a serverless solution perspective the interaction between the AWS service and callers is asynchronous in nature, i.e. my Lambda execution response returns to the AWS managed service and not to the caller or consumer. This to me behaves in an identical fashion to the asynchronous invocation model and I really wish that Lambda destinations were an option for this "asynchronous solution execution model", I get the invocation is synchronous, but from an overall serverless solution perspective, it isn't. I really think that Lambda Destinations for this invocation model being available would open up more flexibility for handling events.</p>
</blockquote>
<p>AWS recently introduced filtering to Event Source mappings enabling messages from the source to be specifically filtered for your Lambda function - this is an amazing addition and I encourage you to check it out!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651298300746/fAjBGuCfk.jpg" alt="invoke models-Event Source Mapping.jpg" /></p>
<p>In this article, I have summarised the Lambda invocation models that every Lambda developer must know and understand to be able to successfully develop and deploy to the cloud.</p>
<h2 id="heading-other-useful-references">Other Useful References</h2>
<ol>
<li><p><a target="_blank" href="https://aws.amazon.com/blogs/architecture/understanding-the-different-ways-to-invoke-lambda-functions/">Understanding Different ways to Invoke Lambda Functions</a></p>
</li>
<li><p><a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html">Invoking Lambda Functions</a></p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Serverless Testing, Part 2: SOLID Architecture for Lambda]]></title><description><![CDATA[Serverless Testing, Part 1: What I forgot at the beginning describes the start of my serverless story where I forget about plain old Software Engineering Principles, and I re-introduced the Dependency Inversion Principle, which is essential when writ...]]></description><link>https://blog.walmsles.io/serverless-testing-part-2-solid-architecture-for-lambda</link><guid isPermaLink="true">https://blog.walmsles.io/serverless-testing-part-2-solid-architecture-for-lambda</guid><category><![CDATA[serverless]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Sun, 10 Apr 2022 08:40:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1646823045017/vnCU1JhrM.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><a target="_blank" href="https://blog.walmsles.io/serverless-testing-part-1-what-i-forgot-at-the-beginning">Serverless Testing, Part 1: What I forgot at the beginning</a></strong> describes the start of my serverless story where I forget about plain old Software Engineering Principles, and I re-introduced the Dependency Inversion Principle, which is essential when writing software!  I want to explore software architecture for serverless functions in this second instalment.  You will learn about SOLID principles and we will break down Hexagonal Architectures and how these ideas work together to simplify building Serverless code.</p>
</blockquote>
<p>In my experience, many teams who are moving into Serverless development are from a heavy infrastructure background and are less experienced in application development using software architecture principles.  So I like to start by going back to basics and talking about SOLID principles of building software.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646822624417/_-FNZFO3o.png" alt="solid_principles.png" /></p>
<h2 id="heading-single-responsibility-principle">Single Responsibility Principle</h2>
<blockquote>
<p>A function should have one reason to change</p>
</blockquote>
<p>Functions should have a single responsibility which is the only reason it should change.  The more responsibilities your code has, the more reasons you will have to change it, and the more complex your functions will become over time.  Keeping functions simple has several key advantages - it lowers code maintenance costs and makes them faster to execute.  However, keeping functions simple is not as easy as it sounds and is part of the distributed design that causes friction in some teams who feel that serverless systems are complex to build or have too many moving parts.  This aspect of serverless is the hardest to deal with and needs a disciplined approach to keeping your solutions simple.</p>
<h2 id="heading-open-closed-principle">Open / Closed Principle</h2>
<blockquote>
<p>Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.</p>
</blockquote>
<p>This software principle means that classes should be open for extension but closed for modification meaning the inner working of a class or function should not need changing when a dependent object or class changes.   You can achieve this through interfaces that extend your code's inner working by introducing a new implementation.  Using interfaces in this way enhances the loose coupling of dependencies.</p>
<p>I also like to apply this to serverless functions when you need to write code.  I always try and make my functions data-driven and configurable so that the processing relies on only the event triggering the function.  This principle applied to serverless is about not changing your original service to deal with specific behaviour in a specialised context.  Instead, look to make these exceptional cases an option or feature flag and encourage composability to add additional processing.</p>
<h2 id="heading-liskovs-substitution-principle">Liskov's Substitution Principle</h2>
<blockquote>
<p>Overridden class methods must have the same input parameters as their superclass.  So, if it looks like a duck and behaves like a duck, it is a duck, and if you want a new duck, make sure it quacks the same as all the others.</p>
</blockquote>
<p>Liskov's principle states that if you use a subclass in place of its parent class anywhere in your software, nothing will break.   In simple terms, parent classes define a contract that sub-classes should never alter - violating this principle means clients who try to use this sub-class will no longer work as expected without being modified.</p>
<p>In serverless, a new version of your service should always be able to replace a previous version without breaking anything.  This also puts our design focus on service consumers or customers who do not need to change their code when a new version is deployed, which would be considered a breaking change for the Interface.</p>
<h2 id="heading-interface-segregation-principle">Interface Segregation Principle</h2>
<blockquote>
<p>No code should be forced to depend on methods it does not use</p>
</blockquote>
<p>The Interface Segregation principle keeps your defined interfaces simple and specific to their purpose.  Keeping interfaces clean is essential; otherwise, every implementation is forced to implement functions rarely used, which is a waste of effort and increases software complexity.</p>
<p>This principle also helps in serverless designs by ensuring that we design simple, single-purpose interfaces for our services.  A service that does too much will expose too many details and may leak internal implementation details to consumers, which is always a mistake for any microservice architecture.  Keeping interfaces simple and to the point is what this principle is about, and we should be strong in applying it to every service boundary we design.</p>
<h2 id="heading-dependency-inversion-principle">Dependency Inversion Principle</h2>
<blockquote>
<ol>
<li>High-level modules should not depend on low-level modules.  Both should depend on abstractions.</li>
<li>Abstractions should not depend on details.  Details should depend on abstractions.</li>
</ol>
</blockquote>
<p>Dependency Inversion is all about creating functional abstractions between different layers of our code.  This principle enforces the use of interfaces for every dependency, enabling the simple replacement of any component in our code quickly and easily.  To embrace this principle means when designing an interaction between a high-level module and a lower level, one should be created with the interactions in mind, which will dictate the overall interface design.  In this way, the coupling of our software components is reduced, and we can introduce new implementations at any time, which is ideal when we have embraced all the SOLID principles explained here.</p>
<p>The serverless use-case for this principle is really in building out strong software domain abstractions around cloud services to isolate business logic from the complexity of the cloud services it uses.  This was the main point I introduced in Part 1 of this series. </p>
<h2 id="heading-hexagonal-software-architecture">Hexagonal Software Architecture</h2>
<p>Hexagonal Architecture is all the rage in the Serverless world and has been written about by many people:</p>
<blockquote>
<p><a target="_blank" href="https://netflixtechblog.com/ready-for-changes-with-hexagonal-architecture-b315ec967749">Ready for changes with Hexagonal Architecture</a> on the Netflix TechBlog by Damir Svrtan and Sergii Makagon</p>
<p><a target="_blank" href="https://aws.amazon.com/blogs/compute/developing-evolutionary-architecture-with-aws-lambda/">Developing evolutionary architecture with AWS Lambda</a> on the AWS Blog by Luca Mezzalira</p>
<p><a target="_blank" href="https://medium.com/serverless-transformation/service-ports-finding-a-loosely-coupled-utopia-with-event-driven-serverless-6964aacd1487">Service Ports: Finding a Loosely Coupled Utopia with Event-Driven Serverless</a> on Serverless Transformations by Ben Ellerby</p>
</blockquote>
<p>These articles describe the benefits and tell you why you should be using hexagonal architecture.  They all talk about Ports and Adapters as the magic you need to isolate your business domain logic from the more specific lower-level code that deals with writing data to databases, file systems or API interfaces.  I don't like to use "hexagonal" or "ports". Instead, I prefer to talk about software patterns. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649578983067/QCMlp2bne.png" alt="hex_arch.png" /></p>
<p>Building your software using this concept is nothing more than embracing the Dependency Inversion Principle from the SOLID principles we have already discussed.  In hexagonal architectures, Ports are nothing more than well-defined Interfaces clearly defining your business domain interactions with external dependencies.  Adapters are specific implementations of these business domain interfaces (Ports) to enable the de-coupling of your business domain logic from the cloud environment you deploy to.  Adding dependency injection into the mix at this point is the real magic;  Enabling injection of Adapters into your business domain logic allows a lot of freedom in building and testing your code.</p>
<p>I strongly feel there is a lot of effort focused on developers mocking SDK elements of AWS and other cloud providers.  A lot of time is wasted trying to work out how to mock SDK responses from SDK API calls.  By adopting business domain Interfaces and implementing them with Adapters, you can instantly create mocks using a mock implementation of the Interface for local testing purposes.  This means your business domain developers do not need to know about the AWS SDK dependencies.  This can be locked away within the Adapter implementations, which can be handled by framework developers who can be specialists for your cloud environment.  Combining Adapters with simple dependency injection allows your adapter dependencies to be injected, meaning business domain code does not need to change whether you are unit testing, integration testing, or deploying to the cloud.  You will have adapters for each of these environments and can drive the injection of these dependencies when needed so all your business domain logic can be quickly and simply unit tested.</p>
<p>A huge benefit of adopting a Hexagonal architecture is to isolate your business logic from your cloud and serverless components that make up your application.  This means you can now have some separation of responsibilities within your team - Experts on your cloud integration and experts on your business domain logic.  With this isolation, you can have less cloud experienced programmers focus on business logic and more deeply cloud experienced developers on Cloud integration - this separation of concerns is powerful.</p>
<h3 id="heading-the-road-to-hexagonal-applications-service-oriented-architecture-soa">The Road to Hexagonal Applications - Service Oriented Architecture (SOA)</h3>
<p>Clean hexagonal architecture comes down to software engineering principles we have discussed throughout this article, and I will throw in one more term - Service Oriented Architecture (SOA).  Wrapping our business domain logic into Service classes creates a complete separation between your cloud environment and application logic; it also becomes a natural break between cloud expert developers and business domain developers - something I have been searching for to create layered teams with specific skill-sets across cloud and the business domain.</p>
<p>Using Services as the core base also allows direct and straightforward dependency injection of the various dependent interface adapters (Ports) required for the service to function.  The following code snippet shows an example of a base abstract style python class for a Service Interface (Port).  It accepts several other adapters for core functionality required, including Logging, Notifications and Storage.  Through dependency injection for domain adapters - the business domain logic is now wholly isolated via the Dependency Inversion Principle I mentioned earlier.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649574798549/jZOH7dsZg.png" alt="python-service-sample.png" /></p>
<h3 id="heading-adding-business-functional-adapters-ports">Adding Business Functional Adapters (Ports)</h3>
<p>In the Service example, several adapters are injected as dependencies.  The following code sample shows how you can structure the notification adapter (Port), which has a business-focused method <strong>notify_new_user</strong> that is functional.  In this way, we separate the detail of the notification implementation from our domain programmers.  This also means we can pivot the actual implementation without changing business domain code - so from a SOLID perspective, we are now embracing the Open-Closed Principle and the Dependency Inversion Principle - two powerful software engineering techniques.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649575434174/uHOunD_ic.png" alt="python-notify-adapter-sample.png" /></p>
<h3 id="heading-bringing-it-all-together">Bringing it all together</h3>
<p>The following example shows how an AWS Lambda handler would apply the techniques discussed in this article.  An adapter class (UserEventMapper) translates the Lambda event into a User model, which can be passed into the Service instance to process the <strong>new_user</strong>.  The Service response is also mapped by an adapter class (NewUserResponse) to enable an actual AWS Lambda Service response based on the service used to trigger this function.  This creates a very clear Lambda function that anyone can look at and understand what is happening.  The adapter classes used can have their implementations changed to alter how this function is being triggered—all without making changes to the business domain logic.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649577778594/1YZIqBNYW.png" alt="python - lambda-example.png" /></p>
<p>We have explored the core of software engineering - SOLID principles, Dependency Inversion, Dependency Injection and Service-Oriented Architecture (SOA).  Combining these techniques can assist in simplifying your development team's journey to Serverless by abstracting away the cloud complexity that Serverless developers are traditionally confronted with.  Using these techniques discussed here, we can reduce the cognitive load developers have to contend with in creating serverless solutions.  This all does feel like it goes against the simplicity of serverless, but we must acknowledge that we are mostly building software solutions at the end of the day.  I say mostly as we should be embracing serverless services where possible, but like Jared Short says - "When you write Serverless code, you need to own it", so engineer it properly and take control.  </p>
<p>Own it is what I say. Own it with SOLID principles, Interfaces and Adapters and Service-Oriented Architecture.</p>
]]></content:encoded></item><item><title><![CDATA[Serverless Testing, Part 1: What I forgot at the beginning]]></title><description><![CDATA[This is the first in a series of articles about Serverless software architecture and testing. I am collating a summary of what I have learned on my journey to Serverless and am starting with this introduction, highlighting my thoughts on what I forgo...]]></description><link>https://blog.walmsles.io/serverless-testing-part-1-what-i-forgot-at-the-beginning</link><guid isPermaLink="true">https://blog.walmsles.io/serverless-testing-part-1-what-i-forgot-at-the-beginning</guid><category><![CDATA[serverless]]></category><category><![CDATA[AWS]]></category><category><![CDATA[lambda]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Mon, 10 Jan 2022 12:06:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1641630738011/aqXeuKHsK.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This is the first in a series of articles about Serverless software architecture and testing. I am collating a summary of what I have learned on my journey to Serverless and am starting with this introduction, highlighting my thoughts on what I forgot when I started learning about serverless. This series will be made up of three parts:</p>
<p>Part 1: What I forgot at the beginning</p>
<p>Part 2: SOLID Architecture for Lambda</p>
<p>Part 3: Simplify Testing with SOLID Architecture</p>
</blockquote>
<p>Serverless Testing has been a hot topic in recent times. Many of the articles focus on general approaches, tooling to automate the creation of temporary stacks, and tell us to stop emulating the cloud locally and test in the cloud! In this article, I won't be covering these tools and approaches. Instead, I want to focus on what I forgot on my Serverless journey, and hopefully, it will help you not make the same mistake.</p>
<p>I started my journey with Serverless over four years ago when Lambda was relatively new, and we were all trying to work out just how this new cloud technology worked, scaled and could be used to create real, working, resilient solutions. There are many things to learn about at first - Lambda, SQS, SNS, DynamoDB, and the list goes on. So many new things that we all get wrapped up in learning how these products work.</p>
<p>When I started building serverless, I started with simple code; they were just functions, after all. Then, I quickly moved to local emulation of SQS, SNS, etc., to do what we have always been encouraged as software engineers - do end-to-end local testing. With mixed success, I moved back to unit testing with event fixtures to test my core business logic and continued to refine my testing knowledge and skills. There are always complexities involved with testing lambda code;  There are interactions with cloud managed services such as S3, SNS, SQS or DynamoDB, which have AWS SDK clients to manipulate data being stored or processed. Now I spent a lot of time working out how to mock these AWS dependencies in my unit tests and make sure the SDK calls expected were made. Every new service seemed to introduce a new challenge; I spent more time writing mocks and getting unit tests working in this foreign land of AWS dependencies than building solutions and started to feel less productive. I have spent a lot of time the past few months reflecting on my journey since it began and how I have changed my thinking in many areas, particularly around testing serverless solutions. </p>
<p>I strongly feel I got wrapped up in the details of all the new services I had to deal with and coming to terms with how they all worked. The cognitive load on putting this all together at first is a lot! Combining this additional learning and the understanding that we are just writing functions means we expect to be writing fewer lines and simpler code. I was doing just this when I first started and looking back. I can see what I forgot about! I forgot all about plain old Software Engineering!</p>
<p>As a professional Software Engineer with a lot of experience, this is quite a confronting revelation. However, looking at conversations within the community over the past year, I feel I am not alone here. So what do I mean when I say I forgot about plain old Software Engineering? Three small words come to mind - Dependency Inversion Principle.</p>
<h2 id="heading-dependency-inversion-principle">Dependency Inversion Principle</h2>
<p>This core principle of Object-Oriented design sounds scarier and more confusing than it is. The following describes this design principle defined by Robert C. Martin:</p>
<ol>
<li>High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).</li>
<li>Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.</li>
</ol>
<p>Dependency Inversion is all about creating functional abstractions between different layers of our code. In a Serverless function context, this technique allows us to focus on more meaningful, descriptive functions to perform actions for our code rather than getting bogged down in the detail of an AWS SDK client for DynamoDB and its associated API calls. Creating these abstractions also removes the implementation detail and complexity of the cloud service mocking of the past. It allows quick and clean testing of each Interface since we can introduce a test stand-in implementation for any abstraction.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641727691064/WGXJ9u7FY.jpeg" alt="Image showing Dependency Inversion" /></p>
<blockquote>
<p><strong>Class Diagram - Dependency inversion applied to Lambda handler</strong></p>
</blockquote>
<p>So what we need to look at is creating functional abstractions for any call from our core business logic out to the cloud. For example, in the diagram above, you can see the <strong>EventHandler</strong> depends on the <strong>DomainServiceInterface</strong> and the <strong>DomainLogicImplementation</strong>, which implements the DomainServiceInterface, depends on the <strong>StorageInterface</strong>. This allows the replacement of any green implementation blocks with Testing Stubs enabling all the components to be tested without creating complex mocks and test assertions. This simple software engineering principle is what I forgot about during the start of my Serverless journey. With the state of confusion and commentary around Serverless testing recently, I felt compelled to share my thoughts and what I forgot about when I first started. Hopefully, this will help you find your way quicker in this often-confusing space.</p>
<p>This article is the first in a new series I am writing on Serverless Software Architecture and Testing. These concepts go hand in hand as good architecture and code organisation naturally lead to better testing. My next article will be about SOLID Architectures for Lambda, taking the Dependency Inversion principle mentioned above further and deeper.</p>
]]></content:encoded></item><item><title><![CDATA[AWS Lambda Powertools: Idempotency, A Deeper Dive]]></title><description><![CDATA[In an earlier article, I wrote about Making all your APIs Idempotent.  I shared why idempotency is essential and introduced the AWS Lambda Powertools for Python, which provides a utility for adding idempotency to your Python built lambdas in a quick ...]]></description><link>https://blog.walmsles.io/aws-lambda-powertools-idempotency-a-deeper-dive</link><guid isPermaLink="true">https://blog.walmsles.io/aws-lambda-powertools-idempotency-a-deeper-dive</guid><category><![CDATA[serverless]]></category><category><![CDATA[Python]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[#howtos]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Sat, 23 Oct 2021 22:39:41 GMT</pubDate><content:encoded><![CDATA[<p>In an earlier article, I wrote about <a target="_blank" href="https://blog.walmsles.io/making-all-your-apis-idempotent">Making all your APIs Idempotent</a>.  I shared why idempotency is essential and introduced the <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python/latest/">AWS Lambda Powertools for Python</a>, which provides a utility for adding idempotency to your Python built lambdas in a quick and easy to use way.  My article focused on an API as the reason for requiring idempotency and, while accurate, it is not the only use-case for the Powertools Idempotency utility.  In this article, I dive deeper into the Powertools utility and highlight more features and use cases.</p>
<h2 id="how-idempotency-works">How Idempotency Works</h2>
<p>The idempotency utility is quite a complex feature wrapped up into a simple package to make it easy and approachable to use.  The idempotent utility follows these steps:</p>
<ol>
<li>Calculate a unique idempotent token using the request payload and check the idempotent store to see if the transaction has been completed or is currently in flight.</li>
<li>If completed, the utility will return the stored result from the first execution; If in-flight, the utility will raise an <em><strong>IdempotencyAlreadyInProgressError</strong></em> exception signalling to the caller it is safe to retry the operation.</li>
<li>If no transaction exists, the utility will save an in-flight record into the store and call the function to process the transaction.  When the function completes, the utility will keep a copy of the response so that future invocations can receive the same result.</li>
</ol>
<p>At its core, the idempotent implementation for power tools derives a hash value (md5 by default) for the idempotent token of the transaction. Therefore, calculating this unique token is the most critical part that we need to understand to ensure idempotency works correctly.</p>
<h2 id="calculating-the-idempotent-token">Calculating the Idempotent Token</h2>
<p>AWS Powertools has your back in this department and will take on board sane, consistent defaults that will work in more straightforward use cases by calculating the idempotent token using the entire message body. However, if your use case is not simple, you must understand how your data changes between invocations.  When processing an AWS Lambda event, many of the fields within the event will contain data that changes between each request, for example, "requestId" and "requestTime" in the AWS API gateway Proxy event, which vary for each API invocation.</p>
<p>For non-default hash keys, you will want to use the <strong>IdempotencyConfig</strong> object and ensure you set the <strong>event_key_jmespath</strong> string to allow powertools to extract the unique idempotency token from the Lambda event for the hash key.
Understanding how JMESPath and the <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/jmespath_functions/">Powertools JMESPath</a> <strong>powertools_json</strong> functions decode strings to JSON objects is essential for the idempotency utility.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635024161822/88A8I40g4.png" alt="image2.png" /></p>
<h2 id="making-a-json-rest-api-idempotent">Making a JSON REST API Idempotent</h2>
<p>An excellent example is using the utility with API gateway proxy events for a JSON REST-based API. Unfortunately, the AWS API Gateway Event's body attribute is a string value that is not ideal when trying to assert the idempotency of your JSON Payload.
By definition, attributes in a JSON Payload for a REST API have no order associated with them, meaning the following examples are considered identical in terms of an API transaction:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"transaction_id"</span>: <span class="hljs-string">"bd784218-31cc-46be-b66a-a9c38b9a2fe5"</span>,
    <span class="hljs-attr">"user_id"</span>: <span class="hljs-string">"10000233220"</span>,
    <span class="hljs-attr">"email"</span>: <span class="hljs-string">"me@email.com"</span>
}
</code></pre>
<p>is the same as,</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"email"</span>: <span class="hljs-string">"me@email.com"</span>,
    <span class="hljs-attr">"user_id"</span>: <span class="hljs-string">"10000233220"</span>,
    <span class="hljs-attr">"transaction_id"</span>: <span class="hljs-string">"bd784218-31cc-46be-b66a-a9c38b9a2fe5"</span>
}
</code></pre>
<p>The Powertools idempotency implementation considers this detail, but only if you ensure the body is decoded into a JSON object using the <strong>powertools_json</strong> built-in JMESPath function. To achieve this outcome for the above example, you would need to set up the IdempotencyConfig as follows:</p>
<pre><code class="lang-Python">config = IdempotencyConfig(event_key_jmespath=<span class="hljs-string">"powertools_json(body)"</span>)
</code></pre>
<p>With the <strong>powertools_json</strong> built-in function, the data given to the Idempotent Utility's hash generation function will be transformed into a python Dictionary object, allowing the hash generator to convert the data into a sorted JSON serialized string.  In this way, your API will remain idempotent regardless of the attribute ordering in the API JSON body.</p>
<p>When choosing the parts of your message payload for the unique idempotency key, it is critical to realize that a string value can contain whitespace and newlines, affecting the hash outcome.
I strongly recommend reviewing the <a target="_blank" href="https://jmespath.org/tutorial.html">JMESPath tutorial page</a> to understand how the JMESPath utility works; it is a powerful tool in your python arsenal and is essential in ensuring the Powertools idempotency utility works for you correctly.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635023921151/uxaSP1cJt.png" alt="image1.png" /></p>
<h2 id="idempotent-decorators-for-everything">Idempotent Decorators for Everything!</h2>
<p>The core of the idempotency utility are the python function decorators (yes, there is more than one!).  A recent addition to AWS Lambda Powertools for Python is the <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/idempotency/#idempotent_function-decorator">idempotent_function</a> decorator, which provides idempotency to any synchronous Python function. So now you can add idempotency to absolutely anything, not just Lambda handlers!</p>
<h2 id="making-any-function-idempotent">Making any function Idempotent</h2>
<p>Using the standard idempotent_function decorator, you can tell Powertools how to calculate the idempotency key using any of the function parameters.  The IdempotencyConfig is also available for customizing how the utility will work utilizing the event_key_jmespath as mentioned above.  </p>
<p>The following code example showcases applying the idempotent_function decorator to the SQS batch processor function making your SQS queue processing completely safe from processing the same message multiple times.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635024242841/7pHkxTfWN.png" alt="image3.png" /></p>
<h2 id="payload-validation">Payload Validation</h2>
<p>The AWS Builder's Library article on <a target="_blank" href="https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/">Making  retries safe with Idempotent APIs</a> covers the need for payload validation when data that is not part of the idempotent token changes between requests.  You need to consider these scenarios carefully so you do not confuse your API consumers; Powertools has a <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/idempotency/#payload-validation">payload validation feature</a> you can leverage to cover this situation.</p>
<h2 id="testing-your-solution">Testing Your Solution</h2>
<p>Once you have designed and implemented idempotency in your solution you will want to test it thoroughly before making it live.  You can test your solution using a local dynamo DB or mocks to save on cloud costs; Powertools covers code testing scenarios completely in their <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/idempotency/#testing-your-code">online documentation</a>. </p>
<h2 id="conclusion">Conclusion</h2>
<p>My everyday work is building and training Serverless teams and making sure they build reliably, with resilience and taking note of the AWS Well-Architected Principles documented in the Serverless Lens.  I always recommend developing on AWS Lambda with the Python runtime since I can rely on teams meeting the Well-Architected principles through the AWS Lambda Powertools. For groups who insist on Typescript or .NET, I strongly urge them to use Python since it has Powertools, the swiss army knife that AWS Lambda developers need for every project!</p>
<p>If switching your development to Python for AWS Lambda Powertools is not for you, I recommend heading over to the AWS Powertools Roadmap and supporting the <a target="_blank" href="https://github.com/awslabs/aws-lambda-powertools-roadmap/issues/26">Typescript</a> and <a target="_blank" href="https://github.com/awslabs/aws-lambda-powertools-roadmap/issues/17">.NET</a> feature requests by adding a 👍.</p>
]]></content:encoded></item><item><title><![CDATA[Correlating Transactions with Zero Code]]></title><description><![CDATA[I take pride in writing detailed, technical articles filled with solid code examples and links to Github so you can review and learn from my serverless journey. Today, I think this will be my shortest ever article, and it lacks all these things since...]]></description><link>https://blog.walmsles.io/correlating-transactions-with-zero-code</link><guid isPermaLink="true">https://blog.walmsles.io/correlating-transactions-with-zero-code</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[serverless]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Tue, 12 Oct 2021 13:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1634349491409/QjwMU6_sS.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>I take pride in writing detailed, technical articles filled with solid code examples and links to Github so you can review and learn from my serverless journey. Today, I think this will be my shortest ever article, and it lacks all these things since starting on this Zero Code adventure.</p>
</blockquote>
<p>There are a lot of articles out there on correlating transactions across distributed systems and plenty of technical frameworks and solutions for providing transaction traceability. However, correlating transactions as the data passes through our services is essential to observe our system behaviour. So, as developers, we reach for what we know best our development tools, software frameworks and technical solutions. </p>
<p>Adding Correlation Ids, tracing codes and segment identifiers takes effort and time.</p>
<p>Usually, we place these identifiers in HTTP Headers and other unique Attribute locations for Cloud-Native services so we can hide these away and provide them as transparent travellers across our distributed systems. In an AWS Managed service sense, this means we need to know how to pack and unpack these identifiers at each step through our serverless systems - across SQS boundaries, through EventBridge events and via SNS topics. These cloud-native services each have a different mechanism to save, transport, and unpack these keys to observability.</p>
<p><strong>But what if there was a different way without all this complexity and code?</strong></p>
<p>I want to share with you my thinking on this and what I have arrived at to resolve this technical problem in a way that transcends the AWS service complexities I just mentioned. Nowadays, I believe in using the language of integration, and for tracing transactions through distributed services, I create an internal integration language encapsulating these observability identifiers.</p>
<p>Here is an example of a message structure from a system I am working on right now:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"correlation_id"</span>: <span class="hljs-string">"b58e7f98-2b58-11ec-8d3d-0242ac130003"</span>,
    <span class="hljs-attr">"message_id"</span>: <span class="hljs-string">"bde284dc-2b58-11ec-8d3d-0242ac130003"</span>,
    <span class="hljs-attr">"trace_id"</span>: <span class="hljs-string">"3965a72e-2b59-11ec-8d3d-0242ac130003"</span>,
    <span class="hljs-attr">"data"</span>:
    {
        <span class="hljs-attr">"id"</span>: <span class="hljs-string">"7e6c7638-218c-4947-8d84-a20a9cf68acd"</span>,
        <span class="hljs-attr">"firstname"</span>: <span class="hljs-string">"michael"</span>,
        <span class="hljs-attr">"lastname"</span>: <span class="hljs-string">"walmsley"</span>,
        <span class="hljs-attr">"email"</span>: <span class="hljs-string">"michael@myemail.com"</span>
    }
}
</code></pre>
<p>The <strong>data</strong> represents the original payload received from the initial caller of the service, usually via an API endpoint.   The API entry point is responsible for validating the data to ensure it is correct before transforming it into the internal message format containing the tracing identifiers (correlation_id, message_id, tracing_id). Using a standard messaging structure like this means we no longer worry about changing how we interact with each of the Managed Services we are using since our correlation data is now directly embedded within our message data. All of our messages now have correlation identifiers that pass through data services like SQS, Eventbridge, SNS, Kafka, Kinesis, etc., without the need for any custom code to embed meta-data or other custom markers for correlating our transactions. </p>
<p>This simple approach of defining an internal service messaging format allows your transaction data and correlation data to always be visible within every component of your distributed Serverless Architecture. Furthermore, we achieve Transaction observability within our Serverless data processing systems using nothing more than our already prepared Cloudwatch Logging utilities and Zero additional code!</p>
]]></content:encoded></item><item><title><![CDATA[Serverless Integration, Zero Code]]></title><description><![CDATA[The words of Farah Campbell and Ben Kehoe on "The Serverless Mindset" has inspired me to level up my thinking and approach to Serverless integration. 
So let me share my journey to zero code integration with you.

For my first foray into zero code in...]]></description><link>https://blog.walmsles.io/serverless-integration-zero-code</link><guid isPermaLink="true">https://blog.walmsles.io/serverless-integration-zero-code</guid><category><![CDATA[serverless]]></category><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[Cloud]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Fri, 08 Oct 2021 13:00:00 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>The words of Farah Campbell and Ben Kehoe on "The Serverless Mindset" has inspired me to level up my thinking and approach to Serverless integration. </p>
<p>So let me share my journey to zero code integration with you.</p>
</blockquote>
<p>For my first foray into zero code integration, I decided to keep things simple and play with the classic integration pattern of sending data via an API into an SQS queue for downstream processing.  Most serverless websites will have you build an architecture like this, an API gateway triggering a Lambda that pushes to the SQS queue for downstream processing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634348435952/dpPrTH0xm.jpeg" alt="Classic Lambda Architecture" /></p>
<p>This pattern is easy to build, deploy and get working with any of the main Serverless frameworks; my weapon of choice is the Serverless Framework from Serverless Inc.  The Serverless Framework focuses on deploying Lambda functions and makes this task quick and easy. For example, to build the Lambda processor pushing to SQS looks something like this.</p>
<pre><code class="lang-javascript">
<span class="hljs-attr">service</span>: api-lambda-sqs

<span class="hljs-attr">frameworkVersion</span>: <span class="hljs-string">'2'</span>

<span class="hljs-attr">provider</span>:
  name: aws
  <span class="hljs-attr">runtime</span>: python3<span class="hljs-number">.8</span>
  <span class="hljs-attr">lambdaHashingVersion</span>: <span class="hljs-number">20201221</span>

<span class="hljs-attr">functions</span>:
  apiPushSQS:
    handler: handler.api_push_sqs
    <span class="hljs-attr">events</span>:
      - http:
          method: POST
          <span class="hljs-attr">path</span>: /
    environment:
      THE_QUEUE: !Ref downstreamSQS

<span class="hljs-attr">resources</span>:
  Resources:

    downstreamSQS:
      Type: AWS::SQS::Queue
</code></pre>
<p>Building out the pattern is simple, and the framework focuses on Lambda functions and their events which makes building and deploying quick and easy.  I managed to do this in minutes and felt productive; the instant win had my endorphins flowing, and I was conquering it!  </p>
<p><strong>But is this the ideal serverless solution?</strong></p>
<p>I am sure Ben Kehoe would say, "If you came along to my talk today to hear about Lambda, you are in the wrong place", and these words are echoing in my head right now as I write this and think how I can do this without code. </p>
<p>We all know the AWS API Gateway supports direct, native integrations with many services, and I have designed many systems around these patterns. Still, I have never actually built one using the Serverless framework; until now.  </p>
<p>To achieve direct integrations from API gateway to other Services involves understanding how to configure the Integration request, and there are choices as to how we can go about this.  I started by configuring the API via the console and followed one of the few blog articles I could find on this topic to set it up, which you can look at <a target="_blank" href="https://medium.com/@pranaysankpal/aws-api-gateway-proxy-for-sqs-simple-queue-service-5b08fe18ce50">here</a>.  </p>
<p>This article allowed me to see the steps I needed to complete:</p>
<ol>
<li>Create the SQS Queue</li>
<li>Create the IAM Policy allowing API Gateway to use the sqs:SendMessage action on my SQS Queue.</li>
<li>Create the role to attach the policy to and enable API gateway to assume the role.</li>
<li>Create the API and define a POST method that we can use to map the incoming request through to the SQS queue.</li>
<li>Integrate the API resource with our SQS Queue and create an integration mapping template to transform the incoming payload for our forwarding request to the SQS service.</li>
<li>Deploy and Test the API</li>
</ol>
<p>Continue with me on my journey to complete these steps and end up with the following architecture, which uses zero-code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634348497869/tcx7IhsPN.jpeg" alt="Zero-Code Architecture" /></p>
<p>To achieve a zero-code solution, we need to learn about the native AWS language of Velocity Templates and understand how data flows through the API Gateway service.  As Ben and Farrah would say - writing code is easy, but being genuinely serverless takes work, and it's not always the work we want to do or is fun to do, but we need to challenge ourselves to leverage the cloud services and write less code!</p>
<h2 id="heading-how-the-api-gateway-data-flow-works">How the API Gateway Data flow works</h2>
<p>A quick high-level look at an API gateway request for a v1 REST API looks like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634348559941/H7Vrc82jj.png" alt="AWS API Gateway Data Flow" /></p>
<p>Each client request goes through the following stages for a simple un-authenticated request:</p>
<ol>
<li>Method Request </li>
<li>Integration Request - where transformations occur for the integration request to SQS</li>
<li>Backend Service (Simple Queue Service in this case)</li>
<li>Integration Response - where transformations occur for the integration response from SQS</li>
<li>Method Response - Mapping of integration Response to Method response for the client.</li>
</ol>
<p>Now that we know the steps for the API request through the gateway, we can start setting this up using the Serverless framework because that's my current go-to for deploying serverless solutions.</p>
<h2 id="heading-create-the-sqs-queue">Create the SQS Queue</h2>
<p>Creating the SQS queue is the easiest part of this solution. First, add the following to the resources section of the serverless YAML file.  I prefer not to provide the QueueName when setting up SQS; this allows the serverless framework to name the resources using the usual naming standard of serviceName-stage-resourceName-randomString.</p>
<pre><code class="lang-javascript">    sqsQueue:
      Type: AWS::SQS::Queue
</code></pre>
<h2 id="heading-create-the-iam-policy-and-role">Create the IAM Policy and Role</h2>
<p>To create the IAM Role and Policy, we cannot use the standard IAM methods for the Serverless Framework since these focus on setting up IAM Roles and Policies for functions in your solution.  This time there are no functions or Lambdas at all!
Add the role and policy details into the Serverless YAML file in the resources section.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634348600719/NpFKdwCOg.png" alt="theIAMRoleandPolicy.png" /></p>
<p><strong>Lines 23 - 34</strong> enable API gateway to assume the role </p>
<p><strong>Lines 35 - 44</strong> define the actions allowed by the policy</p>
<p><strong>Line 44</strong> uses Cloudformation intrinsic function !GetAtt to get the Arn of the SQS Queue we create in the stack.</p>
<h2 id="heading-create-the-rest-api">Create the Rest API</h2>
<p>To create the Rest API, we need to create an AWS::Apigateway::Rest resource.  Usually, the Serverless Framework would make this for you under the covers as it creates functions triggered by HTTP events.  In the configuration for the RestApi, I have used <code>${self:custom.resourcePrefix}</code> for the "Name", which is a variable I set up to use for the naming of Services throughout my Serverless YAML.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634348654138/94OTkEft1.png" alt="createTheAPI.png" /></p>
<h2 id="heading-create-the-api-resources-and-integrate-to-our-sqs">Create the API Resources and Integrate to our SQS</h2>
<p>We create the  API method using the "AWS::ApiGateway::Method" resource, which needs to look like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634348702308/8js8Gyucg.png" alt="createApiIntegration.png" /></p>
<p>Creating the API  method and integration is where all the work is in building out our zero-code solution and will break this down into the following steps for clarity:</p>
<ol>
<li>Create the API Method and Responses (lines 60 - 70)</li>
<li>Create the integration - the easy bits (lines 71 - 79)</li>
<li>Create the Integration Request (lines 80 - 84)</li>
<li>Create the Integration Response (lines 85 - 99)</li>
</ol>
<h3 id="heading-create-the-api-method">Create the API Method</h3>
<p>Creating the Method resource is the easy bit.  We set up the API Method on line 60 and Method responses on lines 64 - 69.  The HTTP status codes returned by the Integration response must be configured in the Method responses so that the API gateway will not throw an error and return an "HTTP 500 internal server error". </p>
<p>On <strong>line 61</strong>, we use "!GetAtt apiGw.RootResourceId" to obtain the actual Id for the RestApi.</p>
<p>On <strong>line 62</strong>, we use "!Ref apiGw" to retrieve the RestApiId for our REST API. </p>
<p>We have to use these Cloudformation intrinsic functions here since we want the values after AWS has created them during the stack deployment.</p>
<h3 id="heading-create-the-integration-the-easy-bits">Create the integration - the easy bits</h3>
<p>I will break this down by the lines to make it easier to follow and explain each step.</p>
<p><strong>Line 71</strong> defines the HTTP Method API gateway will use when calling the Integration endpoint; in this case, we want to use POST since we are sending data to SQS.</p>
<p><strong>Line 72</strong> defines the type of integration; in this case, we are doing a direct service integration, so will use "AWS".  Using AWS means using an internal integration that allows us to transform the payload rather than "AWS_PROXY", which will proxy the request directly to the integration service leaving the body untouched.  </p>
<p><strong>Line 73</strong> defines the IAM Role the API Gateway will assume while performing the integration.</p>
<p><strong>Line 74</strong> defines the URI of the action we want to invoke.  The Uri action we need to invoke will look like "arn:aws:API gateway:us-east-1:sqs:path/1234567890/the_queue_name", so we are using the Cloudformation intrinsic function "Join" to create this for us.</p>
<p>Once we have these attributes configured, we have defined the core details on the service we are integrating, the "How".</p>
<h3 id="heading-create-the-integration-request">Create The Integration Request</h3>
<p><strong>Line 80</strong> defines the PassthroughBehaviour - I like to use "NEVER" so that if we receive an unexpected request "Content-Type", then API gateway will respond with an <code>HTTP 415 error - Unsupported Media Type</code>, which I think is desirable.</p>
<p><strong>Line 81 - 82</strong> defines the request parameters we send to SQS; we will map our request into <code>x-www-form-urlencoded</code> values when sending our POST to SQS.</p>
<p><strong>Lines 83 - 84</strong> define how we will transform the incoming body and send it to SQS; this is a simple VTL template setting the <code>Action=SendMessage</code> and <code>MessageBody=$input.JSON($)</code>, which is the JSON body of the request to be sent to the SQS queue. </p>
<h3 id="heading-create-the-integration-response">Create the Integration Response</h3>
<p>So far, we have created the API, formed the integration request and sent it to SQS; now, we have to deal with the SQS service response and map a response back to our caller using the "IntegrationResponses" attribute.  The value expected here is a MAP of HTTP status codes and VTL template snippets to transform the HTTP response we get from SQS.  In this demo, I have mapped the following response codes - 200, 404 to responses that will look something like the following:</p>
<p><strong>HTTP 200 Response</strong></p>
<p>We use a VTL template to transform the SQS response into a payload that we want to return rather than the entire integration payload.</p>
<pre><code class="lang-javascript">{  
    <span class="hljs-string">"request_id"</span>: <span class="hljs-string">"ea87ccb1-92d3-51ff-b9f0-956ad19844d7"</span>,  
    <span class="hljs-string">"message_id"</span>: <span class="hljs-string">"173689a1-6b40-492e-9e6c-338c13b70f16"</span>, 
    <span class="hljs-string">"accepted"</span>: <span class="hljs-string">"ok"</span> 
}
</code></pre>
<p><strong>HTTP 404 Response</strong></p>
<pre><code class="lang-javascript">{
    <span class="hljs-string">"error_code"</span>: <span class="hljs-number">404</span>,
    <span class="hljs-string">"error_message"</span>: <span class="hljs-string">"Unable to complete Request"</span>
}
</code></pre>
<h2 id="heading-deploy-and-test-the-api">Deploy and Test the API</h2>
<p>After creating the SQS Queue and a new REST API, we need to deploy our API to a stage so our clients can call the API.
Lines 101 - 108 define the Stage Deployment of our API and will make the API available.
Important Note: This resource will only work for the first deployment; any change to your REST API will not deploy as part of a stack update.  I searched far and wide for a solution to this behaviour and landed on the need to run a post-stack update API deployment using the console or AWS CLI.</p>
<pre><code class="lang-javascript">$ aws apigateway create-deployment --region &lt;region&gt; \
    --rest-api-id &lt;api-id&gt; \
    --stage-name &lt;stage-name&gt;
</code></pre>
<h2 id="heading-the-wrap-up">The Wrap-Up</h2>
<p>Writing a Zero-Code Serverless integration has been an evolving journey, and I have shared the details of where I ended up in this article.  I have learned a lot about AWS along the way and encourage you to explore and try code-less integrations in your solutions as a way of levelling up your journey to serverless nirvana.  What I have shared today is not production-ready - I am still exploring these patterns to make them more resilient and reliable for live use.</p>
<p>Check out the real working Serverless Solution (No code included) on my GitHub repository <a target="_blank" href="https://github.com/walmsles/zero-code-api-sqs">here</a></p>
]]></content:encoded></item><item><title><![CDATA[Making All your APIs Idempotent]]></title><description><![CDATA[When you start working with Serverless on AWS, one of the first questions you should be asking yourself is — “is my system Idempotent”?

What is Idempotency?
Idempotency is a straightforward but misunderstood concept. The dictionary defines Idempoten...]]></description><link>https://blog.walmsles.io/making-all-your-apis-idempotent</link><guid isPermaLink="true">https://blog.walmsles.io/making-all-your-apis-idempotent</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[Python]]></category><category><![CDATA[serverless]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Sat, 21 Aug 2021 14:00:00 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>When you start working with Serverless on AWS, one of the first questions you should be asking yourself is — “is my system Idempotent”?</p>
</blockquote>
<h2 id="what-is-idempotency">What is Idempotency?</h2>
<p>Idempotency is a straightforward but misunderstood concept. The dictionary defines Idempotency as “<em>Unchanged when multiplied by itself</em>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634349284220/ykSqlMSnI.png" alt="idempotent-dictionary.png" /></p>
<p>In a micro-service context, this means any repeated action within the service will always have the same effect. The AWS Builders Library has a great article covering <a target="_blank" href="https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/">Idempotency</a> and how AWS implements Idempotent measures within their distributed systems. This concept makes writing API client error handling simpler, and I highly recommend reading the AWS article!</p>
<p>Implementing Idempotency will ensure your API will always respond to clients in the same way for the same input — this last part is essential — <strong><em>for the same input.\</em></strong> Idempotent solutions require your micro-service to store the state of each response to an API call so you can return the same payload for any repeated call. It is up to you to define what makes an API call original so that you can store the Idempotent response for replay later. Once you have this, you need to decide how long the initial response should persist for repeated calls. The length of time you cache API responses depends on your overall solution, so careful consideration is needed during service design to get this correct for your situation.</p>
<h2 id="why-do-i-need-this">Why do I need this?</h2>
<p>When designing a new cloud service, Idempotency should be the first tenet considered before building anything. Idempotency is needed because any external interaction over an API is likely to fail some of the time. The following diagrams show API scenarios and possible request-response scenarios for distributed API driven systems.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634349311947/nfJywcGyE.jpeg" alt="scenario1.jpg" /><strong>Scenario 1: All Okay</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634349325482/MDisNUJVJ.jpeg" alt="scenario2.jpg" /><strong>Scenario 2: Clear Error Response</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634349341041/3iJ-zX39C.jpeg" alt="scenario3.jpg" /><strong>Scenario 3: Client request Times out</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634349361210/rm7IrqljX.jpeg" alt="scenario4.jpg" /><strong>Scenario 4: Server Response never Received</strong></p>
<p>Scenarios 1 and 2 represent the usual test cases for your API. These are the most straightforward test cases. Scenarios 3 and 4 are the tricky cases and are the main reasons we must consider implementing Idempotency. Both of these scenarios for the API client are identical in behaviour; they both appear as API timeouts to the client. Client time outs or network issues where clients never receive a response feels like an unusual occurrence. It won’t usually be an issue during development or testing — only when your system is in use over a more extended period and at high volume will these scenarios start to emerge, often that is after you have gone live. Without Idempotency in place, any client timeout or non-response (which does happen!) means a client will retry the API action. In distributed systems, without Idempotency, this could result in duplicated data on the target platform, which is undesirable in all cases. Many implementations will take the simple approach of returning a duplicate record error and feel this is handling their Idempotency. In a way, it does, but now every client making the API call needs additional error processing to recognise the timeout case followed by a duplicate error means the original interaction was successful. Implementing Idempotency in all your APIs is critical for success in your distributed systems.</p>
<h2 id="implementing-idempotency">Implementing Idempotency</h2>
<p>Creating an idempotent API is easy if you have access to the right tools! I am currently implementing APIs using AWS lambda and python as my runtime, making the <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/idempotency/">AWS Lambda Powertools for Python</a> an effortless choice which implements a great utility that deals with Idempotency for you out of the box. In addition, the AWS Lambda Powertools for Python provides several useful tools and utilities to ensure you can easily follow the AWS Well-Architected Framework. Heitor Lessa from AWS contributed to the Serverless Lens version of the Well-Architected Framework and is the creator of the AWS Lambda Powertools for python, which provides simple tools and utilities to apply the tenets of the framework to all your Lambda projects.</p>
<p>Implementing Idempotency with the Lambda Powertools for Python is very straightforward; the community behind this open source project have thought about almost everything! The key to Idempotent APIs is defining what makes each API interaction with your service unique; it could be the entire body of your API or a specific data element provided by the caller. Once you have determined what makes each of your APIs Idempotent, it is time to introduce the Idempotent decorator from the Powertools library. The Idempotent utility in Powertools uses DynamoDB as the persistence layer. Therefore, if you do not have a DynamoDB table in your project already, you will need to add one to your Infrastructure as Code to make sure it exists.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634349380010/tzGI1dxg7.jpeg" alt="code.jpg" /><strong>example implementation of Idempotecy</strong></p>
<p>The above example implements Idempotency using the entire API body to provide uniqueness for my API. Let’s walk through the code in more detail:</p>
<p><strong>Line 4:</strong> Imports the required classes from Powertools to achieve Idempotency</p>
<ul>
<li><p><strong>DynamoDBPersistenceLayer</strong> — stores Idempotent data in our DynamoDB table.</p>
</li>
<li><p><strong>IdempotencyConfig</strong> — Provides configuration for the Idempotentcy decorator.</p>
</li>
<li><p><strong>Idempotent</strong> — this is the decorator providing the utility.</p>
</li>
</ul>
<p><strong>Lines 6–7:</strong> Creates the persistence layer for Idempotency; in this example, I am using a Lambda environment variable to store the name of the DynamoDB table to use.</p>
<p><strong>Line 9</strong>: This line defines the configuration of uniqueness for the API call. In this example, I use the <strong>body</strong> attribute of the Lambda API Gateway event to determine a unique transaction. Using the entire Lambda Event is impossible since AWS will always include unique request-ids and other AWS specific data. Using the <strong>body</strong> attribute means the whole payload for my API defines uniqueness.</p>
<p><strong>Line 11:</strong> Adds the <strong>idempotent</strong> decorator to your lambda handler passing in <strong>persistence_layer</strong> and <strong>config</strong>; this is where the Idempotency magic happens! If a duplicate API request is received and the response cache has not timed out, Powertools won’t call your Lambda handler. It will return the cached response from the first API call instead.</p>
<p>Like every utility in the AWS Lambda Powertools, implementation is quick, easy and painless. In my code example, I have achieved Idempotency for my serverless API with five lines of code. Every utility in the Powertools is equally effortless to implement, and I highly recommend adopting the AWS Lambda Powertools in every serverless project you create! I have shown an example today using python, but AWS Lambda Powertools also supports other runtimes — Java, C# (.NET) and Typescript. With these Open Source projects freely available and well supported in the community, there is no longer any excuse for not adopting AWS Well-Architected best practices for your Serverless projects!</p>
<p>I have only highlighted the Idempotency utility; there are so many more utilities available to implement. I recommend you review the complete documentation for <a target="_blank" href="https://awslabs.github.io/aws-lambda-powertools-python/latest/">AWS Lambda Powertools for Python</a> and see how easy it is to implement a Serverless project with best practices baked in!</p>
]]></content:encoded></item><item><title><![CDATA[Getting Started with Serverless]]></title><description><![CDATA[I started learning about serverless development and AWS lambda in 2016 while working with startup companies building web APIs and single-page apps. These companies were Digital Agencies building out products to disrupt their industries and since they...]]></description><link>https://blog.walmsles.io/getting-started-with-serverless</link><guid isPermaLink="true">https://blog.walmsles.io/getting-started-with-serverless</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[serverless]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Sun, 20 Jun 2021 04:13:03 GMT</pubDate><content:encoded><![CDATA[<p>I started learning about serverless development and AWS lambda in 2016 while working with startup companies building web APIs and single-page apps. These companies were Digital Agencies building out products to disrupt their industries and since they knew how to manage websites, server-based web hosting was the usual deployment environment. At this time I was heavily invested in docker containers and container orchestration and started introducing Docker workflows to these companies as a means of shrinking deployment footprints and standardising environment control from development to production.</p>
<p>It was this interest in perfecting and minimising deployment footprints and infrastructure management that made AWS lambda stand out for me as a technology choice for building out web APIs. Since learning about AWS lambda and its capabilities I have not returned to server or container approaches to building software, instead, I have been more focused on cloud-native services and AWS lambda. It has been over 4 years since I last deployed a server on AWS or any other provider and I feel cured of the need for servers forever.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634347709866/Usi5fTa43.jpeg" alt="Cloud Network" /></p>
<p>My journey to building full-time with cloud-native services is a winding road of failings and learnings as I started to understand how to control all the puzzle pieces that AWS provides and how to combine these into a cohesive system that was resilient, robust, and highly scalable. In the remainder of this article, I want to highlight what I feel are the main considerations when trying to build out serverless solutions on AWS, a snapshot of my experience to date, and the main considerations for getting started.</p>
<h2 id="heading-keeping-it-simple">Keeping it Simple</h2>
<p>One of the key evolutions of moving to Serverless is that of writing only the code that adds direct business value to your solution. Keeping your code small, concise and simple is important for several reasons:</p>
<p>A smaller code footprint means faster startup time when lambda is starting for the first time (minimising cold starts).</p>
<p>Less code means faster development iterations, so quicker delivery cycles</p>
<p>Less code means simpler code and also means testing is simpler or perhaps non-existent since there is less complexity in your code. Unit testing is definitely required for more complex or shared code to ensure correct operation but striving for 100% coverage is not as important.</p>
<blockquote>
<p>Keep It Simple Stupid (KISS)</p>
</blockquote>
<h2 id="heading-know-your-services">Know Your Services</h2>
<p>Developing solutions with AWS lambda you really have to understand how the managed services you are using will trigger lambda, how they need to be configured, how they could fail, and how you notify the service that your lambda has failed to process the event or has been successful. I feel this is the main change in thinking and attitude, serverless developers need to understand all the components they are using in-depth and to favour writing configuration over code. AWS provides managed services with a guaranteed SLA but sometimes these services just misbehave or behave in unusual ways so we need to understand how the services work and design with failures in mind.</p>
<blockquote>
<p>Know your Services, understand how they can fail</p>
</blockquote>
<h2 id="heading-understanding-your-service-limits">Understanding Your Service Limits</h2>
<p>This has to be one of my key learnings for designing and building cloud-native services. When building any solution we have to understand what the underlying "infrastructure" or managed service quotas and limits are. Without understanding the key limitations which AWS imposes to guarantee throughput and resilience of their managed services you really are starting to guess at how well your solution will scale and behave. Every AWS service has a defined set of limits and quotas which you need to understand so that your solution will work in every situation.</p>
<blockquote>
<p>Know all your limits to guarantee success</p>
</blockquote>
<h2 id="heading-know-your-volumes">Know Your Volumes</h2>
<p>In my experience not understanding your volumes is a recipe for disaster, this is not new for serverless development it applies to container or server-based systems as well. I would have to say the biggest mistake any developer can make is not understanding the transaction volumes that will be processed by your system. AWS Lambda will scale and this is fine but you have to be sure that any downstream integrations or services you make use of are also capable of scaling in exactly the same way. I would say this is the most important point for designing and building scalable distributed systems that are reliable and resilient.</p>
<blockquote>
<p>Know all your volumes, take the time to really know how your service will be used.</p>
</blockquote>
<h2 id="heading-understand-scalability-boundaries">Understand Scalability Boundaries</h2>
<p>Following on from "Know Your Volumes" is my next key learning, understand how different sections of your solution scale. Not all managed services scale at the same rate or sometimes we have systems we interact with that are not highly scalable like AWS lambda. In my experience to date, I would say this is the number one mistake made by developers who are starting out. They underestimate the scaling nature of lambda and when it starts to scale out of control every component in your AWS account is at risk, this also links directly back to knowing your volumes. Not considering how scalability boundaries clash is definitely the most common cause of failure when starting out with serverless development.</p>
<p>Watch for Scalability boundary clashes which will always be high scale pushing into lower scale or even connection-oriented services</p>
<h2 id="heading-visibility-of-your-services">Visibility of Your Services</h2>
<p>Operational logging is the most critical function in any distributed service. Without clean, concise logging you will not be able to understand what your system is doing. This is not unique to serverless development but in my experience when developers are put under pressure to deliver, logging is the last thing that is considered. Without clear and useful logging in place, it becomes more difficult to see what is happening within your services and almost impossible to know what your system is doing.</p>
<blockquote>
<p>Make sure you can see your transactions and how they flow through your system from end to end, don't underestimate good logging</p>
</blockquote>
<p>If you are just getting started with AWS lambda and cloud-native development take a moment to consider these themes and apply them as you start to learn and build on AWS with lambda and cloud-native services.</p>
]]></content:encoded></item><item><title><![CDATA[Using the AWS CLI Securely]]></title><description><![CDATA[Using the Amazon AWS CLI to interact with your cloud account is powerful and essential.  In the cloud we are encouraged to "codify all the things".  In other words we should write code to setup everything we do in the cloud, and there is good reason ...]]></description><link>https://blog.walmsles.io/using-the-aws-cli-securely</link><guid isPermaLink="true">https://blog.walmsles.io/using-the-aws-cli-securely</guid><category><![CDATA[AWS]]></category><category><![CDATA[cli]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Michael Walmsley]]></dc:creator><pubDate>Mon, 08 Mar 2021 13:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1634351462611/GIbXDkK4y.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Using the Amazon AWS CLI to interact with your cloud account is powerful and essential.  In the cloud we are encouraged to "codify all the things".  In other words we should write code to setup everything we do in the cloud, and there is good reason to do this and I strongly encourage you to look further into how you can do this.</p>
<p>Nearly every framework at it's core will make use of the aws cli in some fashion.  All the beginner documentation around using the AWS CLI or other serverless frameworks document setting up the core AWS Access Key or Secret when interacting with the cloud.  Whilst this is mostly fine and okay, I feel its not completely secure.  If your computer where you store your access key and secret is compromised in anyway then there is potential for your AWS credentials, the key to your cloud credit card to be compromised and this could be a serious problem.  Most of the time we all follow the general tutorials and logon using our root account, go to the IAM console and create an Access Key and Secret for our AWS account, for the root user with the MOST access to your cloud.  I actually feel really uncomfortable writing this because this setup for me is wrong, really wrong.  Don't take this statemnent out of context, 99.999999999% of the time the setup and tutorials are fine and mostly okay so far as security goes but it could be better.</p>
<p>As an enterprise cloud application architect I feel duty bound to look at the security of everything I do or allow to be done in relation to my cloud account.  Step number one for securing any account is to ensure that we are always using more than one mechansim to authenticate every logon, Multi-Factor Authentication (MFA) and this <strong>is possible</strong> to apply to the AWS cli and most frameworks that interact with AWS to deploy your applications.</p>
<h1 id="creating-a-user-for-secure-access">Creating a User for Secure Access</h1>
<p>It is possible to apply Multi-Factor authentication using the aws-cli through the use of the AWS Security Token Service (STS) and by setting up a new IAM User specifically for all your framework interactions via the cli.  I strongly recommend against using your root account logon for doing any aws-cli interactions with your AWS account other than for online console access.  So lets get started with setting up a new IAM user and enforce MFA for all operations.</p>
<h2 id="creating-a-new-iam-policy-forcemfa">Creating a new IAM Policy - ForceMFA</h2>
<p>The first thing I setup is a new IAM Policy that will allow access to everything but which also requires the existence of Multi-Factor Authentication.  The following steps assume you are logged on as an admin user to your AWS account.</p>
<ol>
<li>Navigate to the IAM console</li>
<li>Under Access Management select <strong>Policies</strong></li>
<li>Click <strong>Create Policy</strong> button</li>
<li>Click on the JSON TAB to get the JSON editor</li>
<li><p>Copy the following into the policy editor:</p>
<pre><code> {
     <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
     <span class="hljs-attr">"Statement"</span>: [
         {
             <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Deny"</span>,
             <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"*"</span>,
             <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"*"</span>,
             <span class="hljs-attr">"Condition"</span>: {
                 <span class="hljs-attr">"Bool"</span>: {
                     <span class="hljs-attr">"aws:MultiFactorAuthPresent"</span>: <span class="hljs-string">"false"</span>
                 }
             }
         }
     ]
 }
</code></pre></li>
<li><p>The key to this policy is the <strong>Condition</strong> which says if MFA is not present <strong>"aws:MultiFactorAuthPresent": "false"</strong> then the <strong>Effect</strong> of Deny should be applied to all resources and all actions.</p>
</li>
<li>Click the <strong>Next</strong> button</li>
<li>Click <strong>Next</strong> again to review the policy</li>
<li>Enter the name for your policy <strong>ForceMFA</strong></li>
<li>Scroll down and click <strong>Create Policy</strong></li>
</ol>
<p>This is the perfect policy to enforce use of MFA for the aws-cli user we will setup next.</p>
<h2 id="creating-a-new-iam-group">Creating a new IAM Group</h2>
<p>This part is not actually neccessary but I like to setup a group that has our new ForceMFA policy attached so that any future Users that I create I can simply assign to the same group.  </p>
<ol>
<li>Navigate to the IAM console</li>
<li>Under <strong>Access Management</strong> select <strong>Groups</strong></li>
<li>Click <strong>Create New Group</strong> to start the wizard</li>
<li>Type the group name and press <strong>Next</strong></li>
<li>Next attach the <strong>ForceMFA</strong> policy using the filter box to find it</li>
<li>Tick the checkbox next to <strong>ForceMFA</strong> policy </li>
<li>You will also need to provide more permissions so the user can create resources, to make life simple I use <strong>AdministratorAccess</strong> so I can deploy all services and since we have MFA in place there is further protection.</li>
<li>Press <strong>Next</strong> to review the Group details.</li>
<li>Click <strong>Create Group</strong> to complete group creation</li>
</ol>
<h2 id="creating-the-new-user-for-cli-access">Creating the new User for CLI access</h2>
<p>Now lets setup the new user using the new User Group we just created.</p>
<ol>
<li>Navigate to the IAM console</li>
<li>Under <strong>Access Management</strong> select <strong>Users</strong></li>
<li>Click the <strong>Add User</strong> button to start the wizard</li>
<li>Type the name of the user in the box at the top</li>
<li>At the bottom you get to choose one of two options for <strong>Access Type</strong></li>
<li>Lets tick the first one and force this to be <strong>Programmatic access</strong> only so we know there is no Console access granted.</li>
<li>Click next and we come to <strong>Add User to Group</strong></li>
<li>At this point we should see the Group you just created, if not hit the <strong>refresh</strong> button and it should appear.</li>
<li>Tick the box next to our new Group</li>
<li>Now press <strong>Next</strong> and we get to Tags, add any Tags if you want to the user.</li>
<li>Click <strong>Next</strong> to review your changes and press <strong>Create user</strong> to create the new user.</li>
<li>The next screen is important it shows the API Access Key and Secret for your new user.  Press the <strong>Download .csv</strong> button to download a csv file with the new credentials which we will need later.</li>
</ol>
<p><strong>Important Note:</strong> This is the only place where the actual <strong>Secret access key</strong> is displayed or available so make sure you complete the last step and download the .csv or take note of <strong>both</strong> the <strong>Access key ID</strong> and the <strong>Secret access key</strong>.</p>
<p>At this point in time we have a new user with programmatic access using an Access key ID and Secret Access Key.  At this point we can try to use the aws-cli using just these and you should not be able to access anything at all - AWS should <strong>Deny</strong> all access since we have not setup MFA yet.</p>
<h2 id="enabling-mfa-for-your-user">Enabling MFA for your User</h2>
<p>The following steps will walk you through setting up MFA on your new user.</p>
<ol>
<li>Navigate to the IAM console</li>
<li>Under <strong>Access Management</strong> select <strong>Users</strong></li>
<li>Locate the new user you have just created and click the name to go to the User details.</li>
<li>Click the <strong>Security credentials</strong> TAB</li>
<li>On the <strong>Assigned MFA Device</strong> line click the <strong>Manage</strong> link to setup MFA</li>
<li>The most common option to choose will be <strong>Virtual MFA Device</strong> which means something like <strong>Google Authenticator</strong>, this is what I am going to use today.</li>
<li>With <strong>Virtual MFA Device</strong> selected click <strong>Continue</strong></li>
<li>Follow the steps for installing a compatible application if you do not have one already</li>
<li>Click <strong>Show QR Code</strong> and scan the code using your authenticator application OR click <strong>Show secret key</strong> and enter this to setup the authenticator.</li>
<li>Now lets complete this setup by typing in 2 consecutive MFA codes as they change in the app and ensure we are correctly synced.</li>
<li>Click <strong>Assign MFA</strong> to complete the setup.</li>
</ol>
<p>At this point we now have a new programmatic access only user with MFA turned on and we can now prepare to use this from the command-line using the AWS Security Token Service (STS) to get a token for use by the cli.</p>
<h1 id="using-mfa-with-the-aws-cli">Using MFA with the aws-cli</h1>
<p>In order to set this up I have created a shell script to call the STS get-session-token and store the temporary credentials into the shell environment so that the aws-cli will work.  The <strong>awsenv</strong> script can be found <a target="_blank" href="https://gist.github.com/walmsles/e9f837bfbf567ffd29e99ae7f6612988">here</a>{:target="_blank"}.</p>
<p>The <strong>awsenv</strong> shell script assumes the following:</p>
<ul>
<li><strong>sed</strong> is available in your shell environment</li>
<li><strong>jq</strong> is available in your shell environment</li>
<li><strong>bash</strong> shell is available for executing the script</li>
<li>aws-cli is installed and available in your shell environment</li>
<li>assumes a bash-like shell environment</li>
<li>Profile folder exists in ~/.aws/profiles</li>
<li><p>A file exists for each user in the profile folder and contains the following details:</p>
<pre><code>  export AWS_ACCESS_KEY_ID=&lt;ACCESS_KEY_ID&gt;
  export AWS_SECRET_ACCESS_KEY=&lt;SECRET_KEY&gt;
  export AWS_DEFAULT_REGION=ap-southeast-<span class="hljs-number">2</span>
  export AWS_ARN_MFA=<span class="hljs-symbol">arn:</span><span class="hljs-symbol">aws:</span>iam::<span class="hljs-number">111111111111</span><span class="hljs-symbol">:mfa/user-device-name</span>
</code></pre></li>
</ul>
<p><strong>AWS_ACCESS_KEY_ID:</strong> Is the Access key ID from the IAM console for your cli user.<br /><strong>AWS_SECRET_ACCESS_KEY:</strong> Is the Access Secret Key downloaded in the csv file when we created the IAM user in the console.<br /><strong>AWS_DEFAULT_REGION:</strong> This is simply a conveninece to set the default region for aws-cli commands.<br /><strong>AWS_ARN_MFA:</strong> Is the ARN for the MFA device which is available on the <strong>Security</strong> tab of the IAM User.  The highlighted section of the security TAB shows the MFA ARN you need to have in the profile. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634349721775/IVinUY4JC.png" alt="User MFA ARN" /></p>
<p>The <strong>awsenv</strong> is a shell script so we are unable to EXPORT new environment variables by executing the shell-script.  Instead you source the script into your environment and pass in the command-line parameters as follows.</p>
<pre><code>    $ source awsenv <span class="hljs-keyword">user</span>-profile &lt;MFA number&gt;
</code></pre><p>Once you do this the <strong>awsenv</strong> script will source the <strong>user-profile</strong> file from <strong>$HOME/.aws/profiles</strong> folder setting up the core required details for calling <strong>aws sts get-session-token</strong>.</p>
<p>the get-session-token command will return temporary access token credentials in JSON similar to the following.</p>
<pre><code>    {
          <span class="hljs-attr">"Credentials"</span>: {
           <span class="hljs-attr">"AccessKeyId"</span>: <span class="hljs-string">"AKIAIOSFODNN7EXAMPLE"</span>,
           <span class="hljs-attr">"SecretAccessKey"</span>: <span class="hljs-string">"wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY"</span>,
           <span class="hljs-attr">"SessionToken"</span>: <span class="hljs-string">"AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4OlgkBN9bkUDNCJiBeb/AXlzBBko7b15fjrBs2+cTQtpZ3CYWFXG8C5zqx37wnOE49mRl/+OtkIKGO7fAE"</span>,
            <span class="hljs-attr">"Expiration"</span>: <span class="hljs-string">"2021-03-09T18:06:10+00:00"</span>
          }
    }
</code></pre><p>Which is where <strong>jq</strong> comes in to strip out the credentials ad store them in standard aws-cli environment variables.  Now that we have done this we can use the aws-cli or any othe framework command which interacts with the aws-cli to deploy cloud services.</p>
]]></content:encoded></item></channel></rss>