<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Blogs on Conijn.io</title>
    <link>https://conijn.io/categories/blog/</link>
    <description>Recent content in Blogs on Conijn.io</description>
    <generator>Hugo -- gohugo.io</generator>
    <language></language>
    <copyright>Joris Conijn</copyright>
    <lastBuildDate>Fri, 10 Apr 2026 08:00:00 +0200</lastBuildDate><atom:link href="https://conijn.io/categories/blog/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Govern and Discover AI Agents and Tools with Amazon Bedrock AgentCore Registry</title>
      <link>https://conijn.io/blog/2026/04/govern-and-discover-ai-agents-and-tools-with-amazon-bedrock-agentcore-registry/</link>
      <pubDate>Fri, 10 Apr 2026 08:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2026/04/govern-and-discover-ai-agents-and-tools-with-amazon-bedrock-agentcore-registry/</guid>
      <description>&lt;p&gt;Have you noticed it yet? Your organization is building AI agents across multiple teams. One team creates a customer support agent, another builds an internal knowledge assistant, and a third develops a set of MCP tools for code review. But none of these teams know what the others have built. Agents and tools are scattered across repositories, accounts, and business units with no central place to discover, govern, or manage them.&lt;/p&gt;
&lt;p&gt;This is the reality for many enterprises adopting agentic AI. The pace of agent development is accelerating, but the infrastructure to manage that growth is lagging behind. Without a centralized registry, you end up with duplicated efforts, unvetted external dependencies, and no consistent way to enforce security or quality standards.&lt;/p&gt;
&lt;h2 id=&#34;the-problem-with-siloed-agent-development&#34;&gt;The Problem with Siloed Agent Development&lt;/h2&gt;
&lt;p&gt;When agents and tools are built in isolation, several problems compound quickly. First, there is no discoverability. A developer in one team has no way to know that another team already built an agent that solves a similar problem. This leads to redundant work and inconsistent implementations across the organization.&lt;/p&gt;
&lt;p&gt;Second, governance becomes nearly impossible. When external MCP tools or third-party agents are pulled into projects without any vetting process, you introduce security risks. There is no audit trail, no approval workflow, and no way to enforce organizational policies before these resources reach production.&lt;/p&gt;
&lt;p&gt;Third, collaboration stalls. Without a shared catalog, teams cannot build on each other&amp;rsquo;s work. Every new project starts from scratch instead of composing existing, tested capabilities. The result is slower delivery and higher costs.&lt;/p&gt;
&lt;p&gt;In this blog post, we explore how &lt;a href=&#34;https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/registry.html&#34;&gt;Amazon Bedrock AgentCore Registry&lt;/a&gt; addresses these challenges by providing a centralized catalog and governance layer for AI agents, tools, skills, and custom resources across your organization.&lt;/p&gt;
&lt;h2 id=&#34;what-is-amazon-bedrock-agentcore-registry&#34;&gt;What Is Amazon Bedrock AgentCore Registry?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://aws.amazon.com/bedrock/&#34;&gt;Amazon Bedrock&lt;/a&gt; AgentCore Registry is a fully managed, serverless service that acts as a single source of truth for all your AI resources. It provides a centralized catalog where you can register, discover, and govern agents, MCP tools, skills, and custom resources.&lt;/p&gt;
&lt;p&gt;The service supports four resource types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A2A Agents&lt;/strong&gt; — aligned to the Agent Card JSON schema for agent-to-agent communication&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP Tools&lt;/strong&gt; — aligned to the &lt;a href=&#34;https://registry.modelcontextprotocol.io/docs&#34;&gt;MCP Registry&lt;/a&gt; open specification&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skills&lt;/strong&gt; — based on the skills.md YAML format&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Resources&lt;/strong&gt; — any valid JSON, giving you flexibility for resources that don&amp;rsquo;t fit the other categories&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can interact with the registry through the AWS Console, the AgentCore SDK, Boto3, RESTful APIs, or even as an MCP tool directly from your IDE.&lt;/p&gt;
&lt;h2 id=&#34;solution-overview&#34;&gt;Solution Overview&lt;/h2&gt;
&lt;p&gt;The architecture of Amazon Bedrock AgentCore Registry is built around three personas: Admin, Publisher, and Consumer. Each has a distinct role in the lifecycle of registry resources.&lt;/p&gt;
&lt;h3 id=&#34;the-admin-flow&#34;&gt;The Admin Flow&lt;/h3&gt;
&lt;p&gt;The admin is responsible for setting up and managing the registry infrastructure. This involves two primary tasks. First, the admin creates and configures the registry itself using CRUD operations. Second, the admin sets up authentication and authorization for developers, consumers, and approvers. The registry supports both &lt;a href=&#34;https://aws.amazon.com/iam/&#34;&gt;AWS Identity and Access Management (IAM)&lt;/a&gt; and OAuth-based authorization, giving you flexibility in how you control access.&lt;/p&gt;
&lt;p&gt;The admin also creates and manages approval workflows. When a publisher submits a new resource, the registry emits events through &lt;a href=&#34;https://aws.amazon.com/eventbridge/&#34;&gt;Amazon EventBridge&lt;/a&gt;. The admin can integrate these events with their own CI/CD pipelines to run security scans, deduplication checks, and validation before approving or rejecting a resource.&lt;/p&gt;
&lt;h3 id=&#34;the-publisher-flow&#34;&gt;The Publisher Flow&lt;/h3&gt;
&lt;p&gt;Publishers are agent developers, tool developers, or teams who build and maintain resources they want to share with others. The publishing workflow starts in the IDE, where a developer builds an agent or MCP tool. The developer can first search the registry to discover what already exists, avoiding duplicate work.&lt;/p&gt;
&lt;p&gt;Once the resource is ready, the developer integrates the registry into their CI/CD pipeline. For agents, the Agent Card schema is auto-synced to the registry. For MCP tools, the endpoint URL and schema are auto-synced as well. The publisher then creates a registry record and submits it for approval.&lt;/p&gt;
&lt;p&gt;After submission, Amazon EventBridge emits an event that triggers the admin-controlled CI pipeline. This pipeline can run security scan scoring, deduplication checks, and validation. Based on the results, the admin or an automated approver updates the registry record status to Approved, Rejected, or Deprecated.&lt;/p&gt;
&lt;h3 id=&#34;the-consumer-flow&#34;&gt;The Consumer Flow&lt;/h3&gt;
&lt;p&gt;Consumers are agent developers or autonomous agents that need to discover and integrate existing resources into their workflows. The consumer flow starts with a search. The registry supports both semantic and lexical search, so you can find resources by meaning or by keyword. Tag-based search enables fine-grained filtering using attribute-based access control (ABAC) policies.&lt;/p&gt;
&lt;p&gt;The registry also features metadata auto-sync, which polls live resources to keep the catalog current. When a consumer finds a resource they need, they request access through the resource onboarding API. The registry returns the appropriate credentials — OAuth client ID and secret, mTLS certificates, or IAM credentials — depending on the authorization model. The consumer can then call the agent or tool directly with proper authentication.&lt;/p&gt;
&lt;h3 id=&#34;the-approval-workflow&#34;&gt;The Approval Workflow&lt;/h3&gt;
&lt;p&gt;The approval workflow provides a structured governance process. Registry records transition through defined states: Draft, Pending Approval, Approved, Rejected, and Deprecated. A publisher creates a record in Draft state. When they submit it for approval, it moves to Pending Approval. If auto-approve is enabled on the registry, the record moves directly to Approved.&lt;/p&gt;
&lt;p&gt;Otherwise, the admin reviews the submission and updates the status. Approved resources can later be deprecated when they are no longer maintained. Rejected resources can be updated and resubmitted. This state machine gives you full control over what enters your organization&amp;rsquo;s catalog.&lt;/p&gt;
&lt;h2 id=&#34;multi-account-support&#34;&gt;Multi-Account Support&lt;/h2&gt;
&lt;p&gt;For organizations operating across multiple AWS accounts, the registry supports sharing through &lt;a href=&#34;https://aws.amazon.com/ram/&#34;&gt;AWS Resource Access Manager (RAM)&lt;/a&gt;. This means a central platform team can maintain the registry in one account and share it with development teams across the organization. This is particularly valuable for enterprises with strict account separation between development, staging, and production environments.&lt;/p&gt;
&lt;h2 id=&#34;security-and-governance&#34;&gt;Security and Governance&lt;/h2&gt;
&lt;p&gt;Security is built into every layer of the registry. Access control is handled through IAM-based policies or OAuth with custom JWT authorizers. The registry supports &lt;a href=&#34;https://aws.amazon.com/privatelink/&#34;&gt;AWS PrivateLink&lt;/a&gt; and VPC endpoints, so you can access resources hosted in customer VPCs without traversing the public internet.&lt;/p&gt;
&lt;p&gt;The approval workflow integrates with external security scanning tools. You can plug in open-source MCP scanners or third-party security solutions into your admin-controlled CI pipeline. Every state transition emits an EventBridge event, giving you a complete audit trail of who registered what, when it was approved, and by whom.&lt;/p&gt;
&lt;h2 id=&#34;registry-vs-gateway&#34;&gt;Registry vs. Gateway&lt;/h2&gt;
&lt;p&gt;A common question is how the registry relates to Amazon Bedrock AgentCore Gateway. The distinction is straightforward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AgentCore Gateway&lt;/strong&gt; is the data plane. It handles runtime execution, acting as a proxy when you call an agent or tool. Think of it as the traffic layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AgentCore Registry&lt;/strong&gt; is the control plane. It handles build-time concerns: cataloging, discovery, and governance of resources.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two services are complementary, not competing. You use the registry to discover and vet resources, and the gateway to execute them at runtime.&lt;/p&gt;
&lt;h2 id=&#34;efficiency-gains&#34;&gt;Efficiency Gains&lt;/h2&gt;
&lt;p&gt;The centralized nature of the registry introduces measurable efficiency improvements. Teams no longer spend time rebuilding capabilities that already exist elsewhere in the organization. The deduplication feature prevents redundant registrations, and semantic search makes it easy to find existing resources that match your needs.&lt;/p&gt;
&lt;p&gt;The approval workflow reduces the security review burden by automating checks through CI/CD integration. Instead of manual reviews for every new agent or tool, you define your policies once and let the pipeline enforce them consistently. This reduces the time from development to production while maintaining your security standards.&lt;/p&gt;
&lt;p&gt;For organizations with dozens or hundreds of agents and tools, the registry transforms what would be a chaotic landscape into a governed, searchable catalog. Platform teams gain visibility into what is being built and used across the organization, enabling better resource allocation and strategic planning.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;As agentic AI adoption grows within enterprises, the need for centralized discovery and governance becomes unavoidable. Amazon Bedrock AgentCore Registry provides the infrastructure to manage this complexity. It gives admins control over what enters the catalog, publishers a streamlined path to share their work, and consumers a fast way to find and integrate existing capabilities.&lt;/p&gt;
&lt;p&gt;If you are building agentic applications on AWS, consider how a registry fits into your platform strategy. You can learn more about Amazon Bedrock AgentCore Registry in the &lt;a href=&#34;https://docs.aws.amazon.com/bedrock/latest/userguide/&#34;&gt;AWS documentation&lt;/a&gt;. For a deeper look at the MCP Registry specification, visit the &lt;a href=&#34;https://registry.modelcontextprotocol.io/docs&#34;&gt;MCP Registry documentation&lt;/a&gt;. And if you are already using Amazon Bedrock for your agentic solutions, the registry integrates directly into your existing workflows.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/abandoned-book-in-close-up-shot-12983115/&#34;&gt;Strange Happenings&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why Cedar Policies Matter for Your Amazon Bedrock AgentCore Gateway</title>
      <link>https://conijn.io/blog/2026/03/cedar-policies-for-amazon-bedrock-agentcore-gateway/</link>
      <pubDate>Wed, 25 Mar 2026 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2026/03/cedar-policies-for-amazon-bedrock-agentcore-gateway/</guid>
      <description>&lt;p&gt;You are building an agentic solution. Your agent has access to MCP tools, and those tools perform real actions: approving expenses, issuing refunds, updating records. You write careful prompt instructions so the large language model picks the right tool for the right situation. Everything works on the happy path. But what happens when it doesn&amp;rsquo;t?&lt;/p&gt;
&lt;p&gt;Only thinking about the happy path gets results fast, but it leaves a lot of room for surprises. Prompt engineering can steer a model, but steering is not the same as enforcing. A well-crafted prompt injection could convince the model to approve a $2,000 expense through a tool that should only handle amounts up to $50. The model followed an instruction; it just wasn&amp;rsquo;t yours.&lt;/p&gt;
&lt;p&gt;In this post, we look at how Cedar policies in &lt;a href=&#34;https://aws.amazon.com/bedrock/agentcore/&#34;&gt;Amazon Bedrock AgentCore&lt;/a&gt; Gateway give you deterministic, auditable guardrails around your MCP tools, so you can stop relying on probability alone.&lt;/p&gt;
&lt;h2 id=&#34;why-prompt-engineering-is-not-enough&#34;&gt;Why Prompt Engineering Is Not Enough&lt;/h2&gt;
&lt;p&gt;When you describe your tools to the model, you include constraints in the tool descriptions. &amp;ldquo;This tool handles expenses up to $50.&amp;rdquo; The model reads that description and, most of the time, routes requests accordingly. But large language models are probabilistic. They do not enforce rules; they predict the most likely next token. That distinction matters when the stakes are real.&lt;/p&gt;
&lt;p&gt;There is no contract between the prompt and the model&amp;rsquo;s behavior. The model might follow your instructions 99% of the time, but that remaining 1% is where things go wrong. And in agentic applications, &amp;ldquo;going wrong&amp;rdquo; means a tool performs an action it should not have. Unlike a traditional API where input validation is deterministic, the decision to call a specific tool is made by the model based on pattern matching and token prediction.&lt;/p&gt;
&lt;p&gt;Consider a prompt injection attack. An attacker crafts input that overrides or confuses the model&amp;rsquo;s instructions. Suddenly the model calls the low-limit expense tool with a $2,000 amount. The tool executes, the money is approved, and your prompt-based &amp;ldquo;guardrail&amp;rdquo; did nothing to prevent it. The model did not malfunction. It simply followed the most convincing instruction it received, and that instruction came from the attacker, not from you.&lt;/p&gt;
&lt;p&gt;This is not a hypothetical risk. As agentic applications move into production and handle financial transactions, customer data, and operational workflows, the gap between &amp;ldquo;the model usually does the right thing&amp;rdquo; and &amp;ldquo;the model is guaranteed to do the right thing&amp;rdquo; becomes a real business risk. Relying on prompt engineering alone means accepting that gap as an inherent part of your system. You need a layer that evaluates requests deterministically, before the tool ever executes. That is where &lt;a href=&#34;https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/policy.html&#34;&gt;Amazon Bedrock AgentCore Policy&lt;/a&gt; comes in.&lt;/p&gt;
&lt;h2 id=&#34;how-cedar-policies-work-in-amazon-bedrock-agentcore-gateway&#34;&gt;How Cedar Policies Work in Amazon Bedrock AgentCore Gateway&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://aws.amazon.com/bedrock/agentcore/&#34;&gt;Amazon Bedrock AgentCore&lt;/a&gt; Gateway acts as the entry point for all tool calls your agent makes. When you attach a policy engine to the Gateway, every tool invocation is intercepted and evaluated before it reaches the tool itself. Amazon Bedrock AgentCore Policy uses &lt;a href=&#34;https://www.cedarpolicy.com/&#34;&gt;Cedar&lt;/a&gt;, an open-source policy language developed by AWS, to express those evaluation rules.&lt;/p&gt;
&lt;p&gt;A Cedar policy is a declarative statement that either permits or forbids access to a specific tool under specific conditions. Each policy defines a scope (who is making the request, what action they want to perform, and which Gateway resource it targets) and optional conditions that inspect the request&amp;rsquo;s context, such as input parameters or user attributes from an OAuth token.&lt;/p&gt;
&lt;p&gt;Cedar follows a default-deny model. If no policy explicitly permits a request, it is denied. And if any &lt;code&gt;forbid&lt;/code&gt; policy matches, the request is denied regardless of any matching &lt;code&gt;permit&lt;/code&gt; policies. This forbid-overrides-permit approach means your safety constraints always win. You do not need to anticipate every possible attack vector; you only need to define what is allowed, and everything else is blocked by default.&lt;/p&gt;
&lt;h3 id=&#34;an-expense-approval-example&#34;&gt;An Expense Approval Example&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s make this concrete. Imagine you have an expense approval agent with three tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;approve_small&lt;/code&gt; handles expenses up to $50&lt;/li&gt;
&lt;li&gt;&lt;code&gt;approve_medium&lt;/code&gt; handles expenses from $50 to $200&lt;/li&gt;
&lt;li&gt;&lt;code&gt;approve_large&lt;/code&gt; handles everything above $200&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your tool descriptions tell the model about these ranges, and most of the time the model respects them. But to enforce them deterministically, you write Cedar policies:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-cedar&#34; data-lang=&#34;cedar&#34;&gt;// Only allow the small expense tool for amounts under 50
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::&amp;#34;ExpenseTools__approve_small&amp;#34;,
  resource == AgentCore::Gateway::&amp;#34;arn:aws:bedrock-agentcore:eu-west-1:123456789012:gateway/expense-gateway&amp;#34;
)
when {
  context.input.amount &amp;lt; 50
};

// Block the small expense tool for amounts of 50 or more
forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::&amp;#34;ExpenseTools__approve_small&amp;#34;,
  resource == AgentCore::Gateway::&amp;#34;arn:aws:bedrock-agentcore:eu-west-1:123456789012:gateway/expense-gateway&amp;#34;
)
when {
  context.input.amount &amp;gt;= 50
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first policy permits the &lt;code&gt;approve_small&lt;/code&gt; tool only when the amount is below 50. The second policy explicitly forbids it when the amount is 50 or more. Because Cedar uses forbid-overrides-permit semantics, even if another policy were to accidentally permit the request, the &lt;code&gt;forbid&lt;/code&gt; policy would still block it.&lt;/p&gt;
&lt;p&gt;Now, even if a prompt injection tricks the model into calling &lt;code&gt;approve_small&lt;/code&gt; with an amount of $2,000, the Gateway evaluates the Cedar policy, sees that the amount exceeds the threshold, and denies the request. The tool never executes. No compute is consumed, no side effects occur, and the agent receives a denial it can handle gracefully.&lt;/p&gt;
&lt;p&gt;You would write similar policies for &lt;code&gt;approve_medium&lt;/code&gt; and &lt;code&gt;approve_large&lt;/code&gt;, each scoped to their respective amount ranges. The result is a complete set of boundaries that mirror your business rules but are enforced independently of the model&amp;rsquo;s behavior.&lt;/p&gt;
&lt;h2 id=&#34;why-this-matters-beyond-guard-clauses&#34;&gt;Why This Matters Beyond Guard Clauses&lt;/h2&gt;
&lt;p&gt;You might be thinking: &amp;ldquo;I can just add a guard clause in my tool code that raises an exception when the amount is out of range.&amp;rdquo; And you absolutely should. Defense in depth is always a good idea. But relying solely on guard clauses has drawbacks that Cedar policies address.&lt;/p&gt;
&lt;p&gt;First, the tool still gets invoked. That invocation consumes compute resources and incurs cost, even if the tool ultimately rejects the request. When your agent processes thousands of requests per day, those wasted invocations add up. Cedar policies stop the request at the Gateway, before any tool code runs. No Lambda execution, no container spin-up, no database connection opened for nothing.&lt;/p&gt;
&lt;p&gt;Second, guard clauses are scattered across your codebase. They live in different functions, written in different programming languages, maintained by different teams. If someone accidentally removes or weakens a guard clause during a refactor, you are exposed, and you might not notice until something goes wrong in production. Cedar policies are centralized and declarative. They live in one place, are easy to audit, and are independent of your tool implementations. A change to your tool code cannot weaken your Cedar policies.&lt;/p&gt;
&lt;p&gt;Third, from a governance perspective, it is far easier to review and validate a set of Cedar policies than to inspect guard clauses across dozens of functions and repositories. Auditors can read Cedar policies directly; they don&amp;rsquo;t need to understand Python, TypeScript, or whatever language your tools are written in. This separation of concerns means your security team can own the policies while your development team owns the tool implementations, each working independently without stepping on each other&amp;rsquo;s toes.&lt;/p&gt;
&lt;p&gt;Finally, Cedar policies give you a consistent enforcement mechanism regardless of how your tools are built. Whether a tool is an AWS Lambda function, a container behind an API, or a third-party service, the same Cedar policy applies at the Gateway layer. You define your rules once, and they protect every tool uniformly. That consistency is hard to achieve with guard clauses alone, especially as your agent&amp;rsquo;s toolset grows and evolves over time.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Prompt engineering is a powerful tool for guiding model behavior, but it is not a security boundary. When your agent&amp;rsquo;s tools perform real-world actions, you need deterministic enforcement that no prompt injection can bypass. Cedar policies in Amazon Bedrock AgentCore Gateway provide exactly that: a centralized, auditable, default-deny policy layer that evaluates every tool call before it executes.&lt;/p&gt;
&lt;p&gt;Combine Cedar policies with guard clauses in your tool code, and you get defense in depth that is both cost-effective and easy to govern. If you are building agentic applications on AWS, start by defining the boundaries your tools should never cross, then express them as Cedar policies.&lt;/p&gt;
&lt;p&gt;To get started, explore the &lt;a href=&#34;https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/policy.html&#34;&gt;Amazon Bedrock AgentCore Policy documentation&lt;/a&gt;, review the &lt;a href=&#34;https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/policy-common-patterns.html&#34;&gt;common Cedar policy patterns&lt;/a&gt;, and read the AWS blog post on &lt;a href=&#34;https://aws.amazon.com/blogs/machine-learning/secure-ai-agents-with-policy-in-amazon-bedrock-agentcore/&#34;&gt;securing AI agents with Policy in Amazon Bedrock AgentCore&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/photo-of-wooden-planks-2347329/&#34;&gt;Engin Akyurt&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why Prompt Management Matters for Agentic Applications</title>
      <link>https://conijn.io/blog/2026/02/prompt-management-for-agentic-applications/</link>
      <pubDate>Sat, 14 Feb 2026 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2026/02/prompt-management-for-agentic-applications/</guid>
      <description>&lt;p&gt;Have you experienced it already? You are working on an agentic solution and writing prompts for large language models. Where do you put your prompts? Do you write your prompt in your Python file itself? Or are you creating a separate file containing the prompt, then loading it into a variable to pass it to the model when invoking it?&lt;/p&gt;
&lt;p&gt;Both approaches work; they have the advantage of being in your codebase. And therefore they can be versioned using git, for example. But this also requires you to redeploy your code whenever you need to tweak your prompt, temperature, top-k, or top-p. And what if you want to do some a/b testing? Are you now duplicating the variable? Are you creating multiple files?&lt;/p&gt;
&lt;h2 id=&#34;why-prompt-management-matters&#34;&gt;Why Prompt Management Matters?&lt;/h2&gt;
&lt;p&gt;When you work with agentic applications, the prompt is the beating heart. The combination of the prompt and the model determines the success of your agentic application. Having the ability to easily tweak and test different versions of your prompt lets you steer the success of your agentic solution. Having a proper setup to do prompt management helps with this. It will also give you consistency, and because you can experiment with your prompt versions, you have more control over the quality of your agentic solution&amp;rsquo;s output.&lt;/p&gt;
&lt;p&gt;It also allows you to make your prompts more efficient, reducing token consumption and lowering costs. You also have a better grip on which guardrails are in place, thereby improving the overall security posture of your agentic solution.&lt;/p&gt;
&lt;p&gt;Having all your prompts in a central location helps with governance and stimulates re-use. This becomes especially critical when working in team environments where multiple developers are building different features of the same agentic system. Without centralized prompt management, you risk inconsistencies, duplicated efforts, and difficulty tracking which prompt versions are deployed in production versus development environments.&lt;/p&gt;
&lt;h2 id=&#34;the-challenges-of-traditional-prompt-management&#34;&gt;The Challenges of Traditional Prompt Management&lt;/h2&gt;
&lt;p&gt;Before diving into solutions, let&amp;rsquo;s examine the pain points developers face with traditional approaches. When prompts live directly in code, several problems emerge. First, there&amp;rsquo;s the deployment bottleneck. Every minor adjustment to wording, every temperature tweak, every experiment requires a full deployment cycle. This slows down iteration and makes rapid experimentation nearly impossible.&lt;/p&gt;
&lt;p&gt;Second, there&amp;rsquo;s the versioning challenge. While git can track changes, it doesn&amp;rsquo;t provide an intuitive way to compare prompt performance across versions. You can&amp;rsquo;t easily see which version of a prompt performed better in production without building custom tracking infrastructure.&lt;/p&gt;
&lt;p&gt;Third, collaboration becomes difficult. When prompts are scattered across multiple files and repositories, team members can&amp;rsquo;t easily discover existing prompts that might solve similar problems. This leads to reinventing the wheel and inconsistent approaches across the organization.&lt;/p&gt;
&lt;p&gt;Finally, there&amp;rsquo;s the security and compliance aspect. When prompts contain sensitive instructions or business logic, having them embedded in code makes it harder to implement proper access controls and audit trails.&lt;/p&gt;
&lt;h2 id=&#34;prompt-management-in-aws&#34;&gt;Prompt Management in AWS&lt;/h2&gt;
&lt;p&gt;When building agentic applications on AWS, you can use &lt;a href=&#34;https://aws.amazon.com/bedrock/prompt-management/&#34;&gt;Amazon Bedrock Prompt Management&lt;/a&gt;. Prompt management in &lt;a href=&#34;&#34;&gt;Amazon Bedrock&lt;/a&gt; allows you to create and iterate on your prompts faster. You can easily select a different model and test your prompt directly from the AWS Console. And it offers options to tweak the temperature, top-p, and top-k. You can create versions of your prompt and variants and test them side by side. Make adjustments and validate them.&lt;/p&gt;
&lt;p&gt;You can use predefined prompt variables and embed them in your prompts. These variables act as placeholders that get populated at runtime with actual values from your application. This makes prompts more reusable and maintainable, as you can use the same prompt template across different contexts by simply changing the variable values.&lt;/p&gt;
&lt;p&gt;The versioning system in Amazon Bedrock Prompt Management is particularly powerful. Each time you make changes to a prompt, you can create a new version. This creates an immutable snapshot of that prompt configuration, including the model selection, inference parameters, and the prompt text itself. You can then reference specific versions in your code, ensuring that your production application uses a tested, stable prompt while you continue experimenting with new versions in development.&lt;/p&gt;
&lt;p&gt;Variants take this a step further by allowing you to create multiple variations of the same prompt for A/B testing purposes. You might have one variant that uses a more formal tone and another that&amp;rsquo;s more conversational. By testing these variants side by side with the same inputs, you can determine which approach yields better results for your specific use case.&lt;/p&gt;
&lt;h2 id=&#34;benefits-beyond-basic-functionality&#34;&gt;Benefits Beyond Basic Functionality&lt;/h2&gt;
&lt;p&gt;The centralized nature of Amazon Bedrock Prompt Management brings additional benefits that might not be immediately obvious. For instance, it enables better cost optimization. By having all your prompts in one place, you can analyze which prompts are consuming the most tokens and focus your optimization efforts accordingly. You might discover that a slight rewording can reduce token usage by 20% without impacting output quality.&lt;/p&gt;
&lt;p&gt;It also improves your security posture. Instead of having sensitive prompt instructions scattered across your codebase, where they might be accidentally logged or exposed, they&amp;rsquo;re stored securely in AWS. You can leverage AWS IAM to control who can view, edit, or deploy prompts, creating a proper separation of concerns between prompt engineers and application developers.&lt;/p&gt;
&lt;p&gt;The governance aspect becomes crucial as your agentic applications scale. With centralized prompt management, you can implement approval workflows to ensure prompts go through proper review before being used in production. You can also maintain an audit trail of all changes, which is essential for compliance in regulated industries.&lt;/p&gt;
&lt;h2 id=&#34;how-to-use-prompt-management&#34;&gt;How to Use Prompt Management?&lt;/h2&gt;
&lt;p&gt;You can use the prompt directly in the converse method when you use the Boto3 library. You can also fetch the prompt details and feed all the settings to any other library. This flexibility means you&amp;rsquo;re not locked into a specific SDK or framework. Whether you&amp;rsquo;re using &lt;a href=&#34;https://www.langchain.com/&#34;&gt;LangChain&lt;/a&gt;, &lt;a href=&#34;https://strandsagents.com/latest/documentation/docs/&#34;&gt;Strands SDK&lt;/a&gt;, or building a custom solution, you can integrate Amazon Bedrock Prompt Management into your workflow.&lt;/p&gt;
&lt;p&gt;When using Boto3, you can retrieve a prompt by its identifier and version. The API returns not just the prompt text, but all associated configuration, including the model ARN, inference parameters, and variable definitions. This means your application code can remain clean and focused on business logic, while all the LLM-specific configuration lives in the prompt management system.&lt;/p&gt;
&lt;p&gt;For teams using the Strands SDK for agentic solutions, integrating with Amazon Bedrock Prompt Management provides a powerful combination. You get the agentic capabilities of Strands while maintaining the flexibility and governance of centralized prompt management.&lt;/p&gt;
&lt;h2 id=&#34;best-practices-for-implementation&#34;&gt;Best Practices for Implementation&lt;/h2&gt;
&lt;p&gt;When implementing prompt management in your organization, start by identifying your most critical prompts. These are the ones that directly impact user experience or business outcomes. Migrate these first to establish patterns and workflows that you can then apply to other prompts.&lt;/p&gt;
&lt;p&gt;Establish naming conventions early. Use descriptive names that indicate the prompt&amp;rsquo;s purpose, the model it&amp;rsquo;s designed for, and perhaps the team that owns it. This makes discovery easier as your prompt library grows.&lt;/p&gt;
&lt;p&gt;Create a testing strategy that includes both automated and manual evaluation. Automated tests can verify basic functionality and regression, while manual review ensures the output meets your quality standards.&lt;/p&gt;
&lt;p&gt;Document your prompts thoroughly. Include information about what the prompt is designed to do, what variables it expects, and any known limitations or edge cases. This documentation should live alongside the prompt in your management system.&lt;/p&gt;
&lt;p&gt;Finally, implement a promotion process. Prompts should move through environments (development, staging, production) just as code does, with appropriate testing at each stage.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Prompt management is no longer optional for serious agentic applications. As these systems become more complex and business-critical, having a robust approach to managing, versioning, and testing prompts becomes essential. Amazon Bedrock Prompt Management provides the infrastructure to build this capability, enabling teams to iterate faster, collaborate more effectively, and maintain higher quality standards. By treating prompts as first-class artifacts with proper lifecycle management, you set your agentic applications up for long-term success.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/photo-of-person-manipulating-keyboards-3355369/&#34;&gt;Clam Lo&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Organizing Git Access per Customer with 1Password SSH Agent</title>
      <link>https://conijn.io/blog/2025/10/organizing-git-access-per-customer-with-1password-ssh-agent/</link>
      <pubDate>Fri, 17 Oct 2025 14:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2025/10/organizing-git-access-per-customer-with-1password-ssh-agent/</guid>
      <description>&lt;p&gt;As consultants, we must juggle multiple credentials, including those from Xebia. But we are also onboarded at the customers where we are hired for our assignments. I already &lt;a href=&#34;https://conijn.io/blog/2022/09/how-to-work-with-different-browser-profiles/&#34;&gt;blogged&lt;/a&gt; on how I separate my browser sessions for different clients. This gives me the ability to have relevant bookmarks for my assignment. This blog will continue to explain how I organize my git access and separate them per customer.&lt;/p&gt;
&lt;h2 id=&#34;password-management&#34;&gt;Password management&lt;/h2&gt;
&lt;p&gt;Since I work with various customers, I store all my credentials in a password manager. I am using 1Password, and 1Password has an awesome feature called &lt;a href=&#34;https://developer.1password.com/docs/ssh/agent/&#34;&gt;SSH Agent&lt;/a&gt;. With this functionality, you can store your private SSH keys in 1Password and use them when you pull or push code to a remote repository over SSH.&lt;/p&gt;
&lt;p&gt;I create an SSH key per customer in my 1Password vault. Once created, I configure the public key in the customer&amp;rsquo;s repository system. (example: GitHub/GitLab/BitBucket) I set both an authentication and a signing key, so I upload the same key twice.&lt;/p&gt;
&lt;p&gt;I also create a file &lt;code&gt;~/.ssh/my-customer.pub&lt;/code&gt; that contains the public key; this file instructs SSH on which identity to use. Since we also uploaded a signing key, I will add the public key to the ~/.git/allowed_signers file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;joris.conijn@my-customer.com ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The only downside here is that the key must be stored in your main vault. I always create customer-specific vaults, and the 1Password agent can only use the main vault.&lt;/p&gt;
&lt;p&gt;More information on &lt;a href=&#34;https://developer.1password.com/docs/ssh/get-started/&#34;&gt;1Password Agents&lt;/a&gt; and how to configure them with GitHub.&lt;/p&gt;
&lt;h2 id=&#34;git-configuration&#34;&gt;Git Configuration&lt;/h2&gt;
&lt;p&gt;Next is the git configuration. This section has three parts. The first two parts are something that you need to set up per customer. The other part is something you need to set up just once.&lt;/p&gt;
&lt;p&gt;First, we will create the git configuration that will be used for this customer. I create a file per customer in the following location: &lt;code&gt;~/.git/gitconfig-my-customer&lt;/code&gt;. This file has the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[user]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;Joris Conijn
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;	email = joris.conijn@my-customer.com
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;	signingkey = ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[gpg]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;format&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;ssh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[gpg &amp;#34;ssh&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;program&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/Applications/1Password.app/Contents/MacOS/op-ssh-sign&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[commit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;gpgsign&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The user definition in this file will be used to commit the code. In this example, the user is Joris Conijn, and the email address is &lt;code&gt;joris.conijn@my-company.com&lt;/code&gt;. This ensures that you commit your code with the entity known in the customer&amp;rsquo;s environment.&lt;/p&gt;
&lt;p&gt;Use your own public key as the signing key and make sure that the 1Password path also matches your system. Next, for each customer, you will need to set the following in the &lt;code&gt;~/.gitconfig&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[includeIf &amp;#34;gitdir:~/workspace/my-customer/&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;~/.git/gitconfig-my-customer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[url &amp;#34;git@my-customer:my-org/“]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;insteadOf&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;git@github.com:my-org/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I need to explain a few things here. The first part ensures that any Git repository in the &lt;code&gt;~/workspace/my-customer/&lt;/code&gt; path uses the configuration defined in &lt;code&gt;~/.git/gitconfig-my-customer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second part will transform the &lt;code&gt;git@github.com:my-org/my-repo.git&lt;/code&gt; to &lt;code&gt;git@my-customer:my-org/my-repo.git&lt;/code&gt;. Now this is actually the secret sauce; the domain used is not a valid domain. It is just the name of the customer, and it will not resolve to the github.com servers just yet.&lt;/p&gt;
&lt;p&gt;The one-time setup required is to configure the &lt;code&gt;~/.git/allowed_signers&lt;/code&gt; file for use. You can do this by adding the following snippet to the &lt;code&gt;~/.gitconfig&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[gpg &amp;#34;ssh&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;allowedSignersFile&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;~/.git/allowed_signers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will configure the list of allowed_signers in git so that the git cli tooling shows valid signatures.&lt;/p&gt;
&lt;h2 id=&#34;ssh-configuration&#34;&gt;SSH Configuration&lt;/h2&gt;
&lt;p&gt;We are almost done; next up is the SSH configuration itself. We now need to update the &lt;code&gt;~/.ssh/config file&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Host my-customer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	HostName github.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	User git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	IdentityFile ~/.ssh/my-customer.pub
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	IdentitiesOnly yes
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What we are doing here is the following. We define a host called my-customer and overwrite the hostname with github.com. This ensures that GitHub servers are used again, and we point to our customer-specific configuration. The primary reason for this virtual hostname is that you can only define SSH configurations on hostnames, not URI paths.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Managing multiple customer environments as a consultant can easily become messy if credentials and Git configurations overlap. By leveraging 1Password’s SSH Agent and a structured Git and SSH setup, you can keep your projects securely isolated while maintaining a smooth developer experience. This approach ensures professional separation, clear commit histories, and effortless switching between customer contexts.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/shot-of-computer-screen-with-multicoloured-code-4816921/&#34;&gt;Myburgh Roux&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Fixing oversized artifacts AWS CDK Pipelines</title>
      <link>https://conijn.io/blog/2025/05/fixing-oversized-artifacts-in-aws-cdk-pipelines/</link>
      <pubDate>Thu, 29 May 2025 17:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2025/05/fixing-oversized-artifacts-in-aws-cdk-pipelines/</guid>
      <description>&lt;p&gt;I built a workload using &lt;a href=&#34;https://github.com/aws/aws-cdk&#34;&gt;AWS Cloud Development Kit (AWS CDK)&lt;/a&gt;, and the &lt;a href=&#34;https://aws.amazon.com/codepipeline/&#34;&gt;AWS CodePipeline&lt;/a&gt; stopped working at the worst moment. It was right at the end of the sprint; we had done multiple deployments before. But at this moment, the moment you might recognize. When this last PR is released, we will have met our deadline! Kaboom, the pipeline stops working, and the reason for the failure is not related to the changes you made.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/codepipeline.png&#34; alt=&#34;CodePipeline Failure Max Artifact Size&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;some-background&#34;&gt;Some background&lt;/h2&gt;
&lt;p&gt;When you use AWS CDK, you can write infrastructure as code using your favorite programming language. AWS CDK comes with certain constructs that make your life easier. For example, you can easily create a CodePipeline that will fetch the code from your repository. It will then synthesize the CDK code into &lt;a href=&#34;https://aws.amazon.com/cloudformation/&#34;&gt;AWS CloudFormation&lt;/a&gt;. The CodePipeline will then upload all assets to an &lt;a href=&#34;https://aws.amazon.com/s3/&#34;&gt;Amazon S3&lt;/a&gt; bucket, and the CloudFormation templates will be deployed to the target accounts. This CloudFormation template will contain references to the assets on S3. When the template is deployed, the assets are fetched from the S3 Bucket.&lt;/p&gt;
&lt;h2 id=&#34;what-has-happened&#34;&gt;What has happened?&lt;/h2&gt;
&lt;p&gt;I ran into the problem that CodePipeline will pass an artifact from step to step. This artifact is a single zipped file, which makes it easy to pass it along the pipeline.&lt;/p&gt;
&lt;p&gt;When CodePipeline wants to deploy the CloudFormation templates. It uploads the zipped file, and it knows where in the zipped file the template is located. However, all other assets are also in the zipfile; this could be the code that you want to run in &lt;a href=&#34;https://aws.amazon.com/lambda/&#34;&gt;AWS Lambda&lt;/a&gt; functions or even Lambda Layers. You can imagine that when your workload starts to become more mature, there will be more assets in this zipfile.&lt;/p&gt;
&lt;h2 id=&#34;why-is-that-a-problem&#34;&gt;Why is that a problem?&lt;/h2&gt;
&lt;p&gt;Well, the artifact has exceeded the maximum size! With CDK projects, this is something that you will run into sooner or later. Usually, you only add stuff to your projects and rarely remove things. And, as you already learned, this always happens at a moment that you don’t expect at all. And in my case, at the worst possible time, right before my deadline.&lt;/p&gt;
&lt;h2 id=&#34;how-can-we-fix-this&#34;&gt;How can we fix this?&lt;/h2&gt;
&lt;p&gt;The answer is quite simple. As I already explained, the problem is with the size of the artifact being passed to the CloudFormation deploy action. We just have to reduce the size after all the assets in the artifact are uploaded to S3.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stripAssets&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;pipeline&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;pipelines.CodePipeline&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;policies&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;iam&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PolicyStatement&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#a6e22e&#34;&gt;effect&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;iam.Effect.ALLOW&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#a6e22e&#34;&gt;resources&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;`arn:aws:s3:::&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;artifactBucket&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;bucketName&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/*`&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#a6e22e&#34;&gt;actions&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s3:PutObject&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;artifactBucket&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;encryptionKey&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;policies&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;iam&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PolicyStatement&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;effect&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;iam.Effect.ALLOW&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;resources&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;artifactBucket&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;encryptionKey&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;keyArn&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;actions&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;kms:GenerateDataKey&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pipeline&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addWave&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;BeforeStageDeploy&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;pre&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pipelines&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CodeBuildStep&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;StripAssetsFromAssembly&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;pipeline.cloudAssemblyFileSet&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;commands&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;S3_PATH=${CODEBUILD_SOURCE_VERSION#&amp;#34;arn:aws:s3:::&amp;#34;}&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ZIP_ARCHIVE=$(basename $S3_PATH)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;echo $S3_PATH&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;echo $ZIP_ARCHIVE&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ls&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rm -rfv asset.*&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zip -r -q -A $ZIP_ARCHIVE *&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ls&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;aws s3 cp $ZIP_ARCHIVE s3://$S3_PATH&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;rolePolicyStatements&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;policies&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What happens in the code sample?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We make sure that we have the &lt;code&gt;s3:PutObject&lt;/code&gt; rights on the artifact bucket. (We need to overwrite the existing artifact.)&lt;/li&gt;
&lt;li&gt;We need the &lt;code&gt;kms:GenerateDataKey&lt;/code&gt; rights. (Optional, I am using &lt;a href=&#34;https://aws.amazon.com/kms/&#34;&gt;AWS Key Management Service&lt;/a&gt; for encryption on the bucket.)&lt;/li&gt;
&lt;li&gt;We add a &lt;code&gt;CodeBuildStep&lt;/code&gt; that will consume the artifact.
&lt;ul&gt;
&lt;li&gt;The artifact is downloaded and unzipped by CodeBuild.&lt;/li&gt;
&lt;li&gt;We figure out the origin of the artifact.&lt;/li&gt;
&lt;li&gt;We remove all assets.&lt;/li&gt;
&lt;li&gt;We will zip the artifact.&lt;/li&gt;
&lt;li&gt;We will upload the artifact over the original.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because we placed the &lt;code&gt;CodeBuildStep&lt;/code&gt; in the pre-section it will be placed right before the deployment of the CloudFormation deploy action.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Hitting CodePipeline’s artifact limit can stall your sprint at the worst moment. By inserting a pre-deploy CodeBuildStep that strips the bulky assets after they’re safely uploaded to S3, you shrink the artifact without changing your workflow. This quick fix keeps your CDK pipeline flowing, your CloudFormation stacks deploying, and your release deadlines intact.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/grayscale-photo-of-road-closed-3907990/&#34;&gt;Ellie Burgin&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>AWS CDK and the Hidden Risks to Least Privilege</title>
      <link>https://conijn.io/blog/2025/04/aws-cdk-and-the-hidden-risks-to-least-privilege/</link>
      <pubDate>Mon, 28 Apr 2025 10:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2025/04/aws-cdk-and-the-hidden-risks-to-least-privilege/</guid>
      <description>&lt;p&gt;Have we given up on the least privileged principle? Personally, I am a big fan of it. But let’s be honest, it can also be tough to follow the principle strictly. With the rise of &lt;a href=&#34;https://github.com/aws/aws-cdk&#34;&gt;AWS Cloud Development Kit (AWS CDK)&lt;/a&gt;, it became even harder.&lt;/p&gt;
&lt;h2 id=&#34;why-does-aws-cdk-make-it-harder&#34;&gt;Why does AWS CDK make it harder?&lt;/h2&gt;
&lt;p&gt;AWS CDK is a great tool to use when you are developing your infrastructure. You can easily build resources using the level 1 and level 2 constructs. The problem lies within the level 2 constructs. They are somewhat opinionated about how you should use them. For example, when you want to store secrets in the Cloud, you create a secret in Secret Manager. This secret needs to be encrypted, so we will use &lt;a href=&#34;https://aws.amazon.com/kms/&#34;&gt;AWS Key Management Service&lt;/a&gt;. This can easily be achieved through AWS CDK:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;secret&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;secretsmanager&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Secret&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Secret&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;secretName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`MySecret`&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;description&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;My super Secret&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encryptionKey&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;kmsKey&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generateSecretString&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;secretStringTemplate&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;JSON.stringify&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;myUser&amp;#39;&lt;/span&gt; }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;generateStringKey&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see in the code example. We are using a customer-managed KMS key. We do this because we want to control who can use the key for decryption. The fact that you pass the key into the Secret construct means that AWS CDK will help you and adds a policy to your KMS key. The policy that gets added is the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AWS&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:iam::000000000000:root&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:CreateGrant&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:Decrypt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:DescribeKey&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:Encrypt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:GenerateDataKey*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:ReEncrypt*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;StringEquals&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;kms:ViaService&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;secretsmanager.eu-west-1.amazonaws.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those familiar with KMS policies will notice that any principal can now use the key if it is used through the secrets manager service. At first, you might think it is not that bad or convenient. But the problem with this is that any principal with an allow statement on the &lt;code&gt;secretsmanager:GetSecretValue&lt;/code&gt; action will be able to read your secret.&lt;/p&gt;
&lt;h2 id=&#34;it-gets-worse&#34;&gt;It gets worse&lt;/h2&gt;
&lt;p&gt;You are storing the secret for a reason. You probably need to read it somewhere in your workload. To do that, you need to create a role and grant the role the rights to read the secret. This is easily done with CDK.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;secret&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;grantRead&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;role&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this simple statement again changes the KMS policy. It will add the following policy.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AWS&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:iam::000000000000:role/MyRole&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:Decrypt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:Encrypt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:GenerateDataKey*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:ReEncrypt*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;StringEquals&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;kms:ViaService&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;secretsmanager.eu-west-1.amazonaws.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At least it’s specific to the role, and this is what you want. But remember the previous policy? It already allows all principals to use this key.&lt;/p&gt;
&lt;h2 id=&#34;how-to-work-around-this&#34;&gt;How to work around this?&lt;/h2&gt;
&lt;p&gt;Sadly, the only way around this is to use the level 1 constructs. These constructs are not opinionated and are a one-to-one mapping to the &lt;a href=&#34;https://aws.amazon.com/cloudformation/&#34;&gt;AWS CloudFormation&lt;/a&gt; resources. With those level 1 constructs, you can specify the KMS key without changing the key policy for you. The flip side is that you do need to allow all principals who need to read the secret in your KMS key policy.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In summary, while AWS CDK greatly simplifies infrastructure development, it can unintentionally weaken strict least privilege practices, especially around KMS key policies. You may need to step down to level 1 constructs to maintain tighter control, trading some convenience for the precision and security your workloads deserve.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/black-handled-key-on-key-hole-101808/&#34;&gt;AS Photography&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Optimizing OpenSearch Ingestion: Ensuring Reliability, Efficiency, and Cost Savings</title>
      <link>https://conijn.io/blog/2025/03/optimizing-opensearch-ingestion-ensuring-reliability-efficiency-and-cost-savings/</link>
      <pubDate>Tue, 11 Mar 2025 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2025/03/optimizing-opensearch-ingestion-ensuring-reliability-efficiency-and-cost-savings/</guid>
      <description>&lt;p&gt;Ingesting data into an OpenSearch cluster looks easy if you read the documentation. The truth is it is easy, but it all depends on how much you care about the data you are ingesting. Let me go one step back. Why do we even use OpenSearch? With the rise of AI, you also need a knowledge base. These knowledge bases can be hosted in OpenSearch. However, to use the OpenSearch database, you must also fill it out with data.&lt;/p&gt;
&lt;p&gt;I recently was challenged to create a database for GenAI purposes. We had a dataset that needed to be loaded into OpenSearch. &lt;a href=&#34;https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ingestion.html&#34;&gt;Amazon OpenSearch Ingestion&lt;/a&gt; sounds like a service that can help you with that.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/Ingestion.png&#34; alt=&#34;Ingestion Example from AWS&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;preparing-the-data&#34;&gt;Preparing the data&lt;/h2&gt;
&lt;p&gt;First of all, the data is never in the format that you want it to be. First, we will need to transform it into something that we can ingest. For this use-case, I used StepFunctions (but &lt;a href=&#34;https://conijn.io/blog/2024/10/avoid-costly-loops-in-aws-step-functions/&#34;&gt;avoid costly loops&lt;/a&gt;) each document coming in will trigger an execution. In my case, I wanted to be able to search the document with semantic meaning. In order to do this efficiently, we will need to chunk the documents into smaller bits. This way, we can generate embeddings for each chunk, which can be used for semantic search. You can create a single file with all chunks, and each chunk will have the same metadata set; the only difference is the chunk text and chunk identification. Once we have this file, we will place it on S3, S3 will send an S3 Event Trigger to an SQS Queue, and the OpenSearch Ingestion pipeline will ingest it into the OpenSearch database.&lt;/p&gt;
&lt;h2 id=&#34;so-far-so-good&#34;&gt;So far so good&lt;/h2&gt;
&lt;p&gt;There is nothing wrong with the approach I just described. This is a textbook example coming from the AWS documentation pages. But an ingestion pipeline will cost you money even when you don’t ingest anything. For this reason, I developed a Lambda function that would stop the pipeline when no messages are in the queue. It will also start the pipeline when there are messages in the queue. This was a nice optimization from a cost perspective.&lt;/p&gt;
&lt;p&gt;After some testing, we expected 10k documents in the system, but we only had around 5k. Where did the other documents go? The dead-letter queues were empty, and we did not have any log traces of any failures whatsoever… It turned out that when you stop the ingestion pipeline, all messages in the buffer are lost.&lt;/p&gt;
&lt;h2 id=&#34;acknowledgments-to-the-rescue&#34;&gt;Acknowledgments to the rescue&lt;/h2&gt;
&lt;p&gt;You can argue a lot about the fact that when you stop a pipeline, the documents in the buffer are lost. But that is the reality that I needed to deal with. You can enable persistent buffering, which would at least help with the data loss part. But you still need to start the pipeline to continue the job. I looked more in the direction of SQS. The whole purpose of SQS is to queue messages and ensure they are processed. After some investigation, I noticed two options I wanted to share.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;s3_pipeline&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;s3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;workers&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;notification_type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sqs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;notification_source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;s3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;codec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;json&lt;/span&gt;: {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;compression&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;acknowledgments&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;records_to_accumulate&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;on_error&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;retain_messages&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;sqs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;queue_url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;https://sqs.eu-west-1.amazonaws.com/000000000000/MyQueue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;maximum_messages&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;visibility_timeout&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;poll_delay&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;visibility_duplication_protection&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;aws&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;sts_role_arn&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::000000000000:role/MyRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;sink&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;opensearch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;serverless&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#ae81ff&#34;&gt;https://vpcxxxxxx.eu-west-1.es.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;index&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;documents-${/index}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;bulk_size&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;max_retries&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;dlq&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;s3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;bucket&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MyBucketName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;key_path_prefix&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;dlq/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;sts_role_arn&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::000000000000:role/MyRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;aws&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;sts_role_arn&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::000000000000:role/MyRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;document_id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${/chunk_id}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;actions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;when&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/operation == &amp;#34;delete&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;update&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;when&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/operation == &amp;#34;update&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;index&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;when&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/operation == &amp;#34;index&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The configuration options are &lt;code&gt;acknowledgments&lt;/code&gt; and &lt;code&gt;visibility_duplication_protection&lt;/code&gt;. The former will keep the message in the queue until it has been ingested into the sink, and the latter will ensure that the message stays in flight as long as it is in the buffer.
These options will ensure that the SQS queue is used as intended. When a failure occurs, the message will reoccur in the queue and be retried. After x number of attempts, it will go to the dead-letter queue. These settings ensured that all 10k documents in the batch were ingested as expected.&lt;/p&gt;
&lt;p&gt;The pipeline would still be stopped once the SQS Queue is empty. To prevent this from happening I also included the &lt;code&gt;ApproximateNumberOfMessagesNotVisible&lt;/code&gt; next to the &lt;code&gt;ApproximateNumberOfMessages&lt;/code&gt; metric.&lt;/p&gt;
&lt;h2 id=&#34;dealing-with-updates-and-deletes&#34;&gt;Dealing with updates and deletes&lt;/h2&gt;
&lt;p&gt;I also wanted to share how we deal with updates and deletions. Ingestion of a new document is easy, but updates and deletions are different. When you have ingested a document with 15 chunks, and the updated version only has 12, you need to delete 3 and potentially update some chunks. For example, this might occur when you delete a paragraph in the middle of a text.&lt;/p&gt;
&lt;p&gt;First, you need to know if the document has already been ingested. A search action on the OpenSearch database can do this. This search action returns all the chunks already present in the database. You can use logic to determine what chunk needs to be updated and which needs to be deleted. We add three additional metadata fields in each chunk: &lt;code&gt;index&lt;/code&gt;, &lt;code&gt;chunk_id&lt;/code&gt;, and &lt;code&gt;operation&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;index&lt;/code&gt; field determines the index to which the document is being ingested. The &lt;code&gt;chunck_id&lt;/code&gt; will become the internal document id, and the &lt;code&gt;operation&lt;/code&gt; field will determine if the chunk needs to be indexed, updated, or deleted.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Ingesting data into OpenSearch sounds straightforward, but the real challenge lies in ensuring reliability, efficiency, and cost-effectiveness. While the textbook approach works, real-world scenarios often demand optimizations—like dynamically stopping the ingestion pipeline to save costs or leveraging SQS acknowledgments to guarantee message processing.&lt;/p&gt;
&lt;p&gt;By implementing these adjustments, we achieved a more resilient ingestion process that ensures all documents make it into OpenSearch as expected. The combination of Step Functions for document transformation, SQS for reliable queuing, and metadata tagging for updates and deletions provided a scalable and maintainable solution.
At the end of the day, ingestion isn’t just about getting data into the system—it’s about making sure the right data gets there, stays there, and can be efficiently queried when needed.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/man-in-brown-leather-jacket-using-binoculars-3811807/&#34;&gt;Andrea Piacquadio&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>RDS User Provisioning and Schema Migrations with AWS Lambda</title>
      <link>https://conijn.io/blog/2025/03/cross-stack-rds-user-provisioning-and-schema-migrations-with-aws-lambda/</link>
      <pubDate>Tue, 04 Mar 2025 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2025/03/cross-stack-rds-user-provisioning-and-schema-migrations-with-aws-lambda/</guid>
      <description>&lt;p&gt;Have you ever been in a situation where you want to provision or configure things cross-stack? Splitting these into logical stacks is always good when dealing with more complex environments. I already shared this in one of my &lt;a href=&#34;https://conijn.io/blog/2025/01/streamlining-workflows-with-feature-branches-and-logical-stacks/&#34;&gt;previous blogs&lt;/a&gt;. But this also introduces a different problem!&lt;/p&gt;
&lt;h2 id=&#34;granting-access-to-your-database&#34;&gt;Granting access to your database&lt;/h2&gt;
&lt;p&gt;When you use &lt;a href=&#34;https://aws.amazon.com/dynamodb/&#34;&gt;Amazon DynamoDB&lt;/a&gt;, you have to use &lt;a href=&#34;https://aws.amazon.com/iam/&#34;&gt;AWS Identity and Access Management (IAM)&lt;/a&gt; to grant access. However, other databases like MySQL also have an internal authentication method. You have a few options when you create the database in one stack and a different stack wants to access the database.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the credentials that you created at deployment time.&lt;/li&gt;
&lt;li&gt;Use identity and access management (AWS IAM).&lt;/li&gt;
&lt;li&gt;Create a local user per use-case.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, all options will work in the end, but they do have their pros and cons. I am a big fan of the least privileged principle. So, for me, using the credentials you created at deployment time is too privileged. With these credentials, you can create databases and tables. This also means that you can drop tables and delete databases. You can compare these credentials with the root credentials of a Linux system or the root account for your AWS account. The root account is there for a reason. That reason is not for day-to-day work. You should use those credentials for administrative tasks, not operational tasks.&lt;/p&gt;
&lt;p&gt;You could use AWS IAM, and this will give us the ability to be more least privileged. This is because you need to create a user in the database. That user will receive grants on what it can do on the database and tables. You can create a read-only role if you only need to read from the database. If you also need to write, you can simply add that permission. Regardless of whether you go for option 2 or 3, you still need to provision the user and the permissions in the database.&lt;/p&gt;
&lt;h2 id=&#34;provisioning-rds-users&#34;&gt;Provisioning RDS users&lt;/h2&gt;
&lt;p&gt;After you created an &lt;a href=&#34;https://aws.amazon.com/rds/&#34;&gt;Amazon Relational Database Service - managed SQL database&lt;/a&gt; instance, you configured the database&amp;rsquo;s “root” credentials. The &lt;a href=&#34;https://aws.amazon.com/secrets-manager/&#34;&gt;AWS Secrets Manager&lt;/a&gt; should generate these credentials. Users should never have these credentials, but if they do, they are there just in case. Since we don’t want to use the root credentials, we need a user to access the database through our application. For this, we can use a provisioner lambda function. This lambda function creates the local users in the database. You can simply invoke the &lt;a href=&#34;https://aws.amazon.com/lambda/&#34;&gt;AWS Lambda&lt;/a&gt; function as a custom resource using the same template as the RDS instance.&lt;/p&gt;
&lt;p&gt;The Lambda function can retrieve the “root” credentials from Secrets Manager. It can use those credentials to connect to the database, create it, and load the initial schema. Next, it will create the user and the grants that the user needs. You don’t need to assign a password if you plan to use IAM. If you don’t want to use IAM, you should create the credentials in Secrets Manager and pass them dynamically into the custom resource. Afterward, your user is ready to use your application.&lt;/p&gt;
&lt;h2 id=&#34;provisioning-rds-users-cross-stack&#34;&gt;Provisioning RDS users cross-stack&lt;/h2&gt;
&lt;p&gt;But what if you have multiple stacks and want to use the RDS instance created in a different stack? The same principle applies. You still need to create the provisioner with the RDS instance. But instead of creating the custom resource in the template of the RDS instance, you create it in the other template. For this, you need the ARN of the Lambda function. You can use a naming schema or store the ARN in an SSM Parameter.&lt;/p&gt;
&lt;p&gt;The advantage of this is that you can invoke the creation of database users in the template where they are used. Plus, if you make the provisioner smart enough, you can pass on the grants the user will need. This way, you encapsulate the user, the rights, and the application into the same template. This will reduce the maintenance load on your application and its infrastructure.&lt;/p&gt;
&lt;h2 id=&#34;schema-migrations&#34;&gt;Schema migrations&lt;/h2&gt;
&lt;p&gt;One of the other advantages of having a provisioner is that you can also manage schema migrations. Mutating the schema can be invoked with the same ease as the user creation. This gives you the ability to perform these migrations during the &lt;a href=&#34;https://aws.amazon.com/cloudformation/&#34;&gt;AWS CloudFormation&lt;/a&gt; deployments. You do need to make sure that the application is backward compatible.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Splitting infrastructure into multiple stacks keeps things organized, but it also introduces challenges like managing database access and schema changes. Using a Lambda function as a provisioner, you can automate user creation and schema migrations as part of your CloudFormation deployments. This approach keeps everything tightly scoped, ensures the least privilege principle, and reduces operational overhead. Whether you use IAM authentication or local users, making the provisioner smart enough to handle both ensures flexibility. With schema migrations baked into your deployments, you get a reliable and repeatable way to evolve your database without manual intervention.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/text-on-computer-monitor-10725897/&#34;&gt;Muhammed Ensar&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Securing Amazon S3 Downloads with ALB and Cognito Authentication</title>
      <link>https://conijn.io/blog/2025/02/securing-s3-downloads-with-alb-and-cognito-authentication/</link>
      <pubDate>Tue, 25 Feb 2025 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2025/02/securing-s3-downloads-with-alb-and-cognito-authentication/</guid>
      <description>&lt;p&gt;Securing an endpoint used to be hard. Nowadays, with the cloud, it’s quite easy. You only need to know how! Assume you have files on &lt;a href=&#34;https://aws.amazon.com/s3/&#34;&gt;Amazon S3&lt;/a&gt; that you like to share. You could make the object publicly available. This would allow your users to download the file using their browsers simply. If you need to scale it, you can add &lt;a href=&#34;https://aws.amazon.com/cloudfront/&#34;&gt;Amazon CloudFront&lt;/a&gt;. This would cache the content closer to your users, making sure that your users have the best performance. But what if you want to control who can download the file? For this, you will need authentication and authorization.&lt;/p&gt;
&lt;h2 id=&#34;authentication-vs-authorization&#34;&gt;Authentication vs Authorization&lt;/h2&gt;
&lt;p&gt;Authentication is all about identifying who you are. First, we need to make sure that we know who the user is. Once we establish who the user is, we can see if the user can access the content. The latter is authorization. AWS has a service called &lt;a href=&#34;https://aws.amazon.com/cognito/&#34;&gt;Amazon Cognito&lt;/a&gt; that allows you to manage a pool of users. These users can originate from other sources like Google, Facebook, and your own identity provider. Or, if you don’t want to use identity providers, you can also create users directly in the user pool.&lt;/p&gt;
&lt;p&gt;You can also create groups, and based on these groups, you can manage the authorization. For example, you could make a group called developers. All users within this group should be allowed to fetch the build report hosted on S3.&lt;/p&gt;
&lt;h2 id=&#34;building-the-endpoint&#34;&gt;Building the endpoint&lt;/h2&gt;
&lt;p&gt;With the Cognito User Pool in place, we need a way to validate the user during the request. I am using an Application Load Balancer to invoke a &lt;a href=&#34;https://aws.amazon.com/lambda/&#34;&gt;AWS Lambda&lt;/a&gt; function. This function can then check if the user can access the report. The logic is quite simple: if the user is part of the developer&amp;rsquo;s group, the user can read the report.&lt;/p&gt;
&lt;p&gt;In this case, we can use the native Cognito integration of &lt;a href=&#34;https://aws.amazon.com/elasticloadbalancing/&#34;&gt;Elastic Load Balancing&lt;/a&gt; with the &lt;a href=&#34;https://aws.amazon.com/elasticloadbalancing/application-load-balancer/&#34;&gt;Application Load Balancer&lt;/a&gt;. What this will do is the following:&lt;/p&gt;
&lt;p&gt;If the user is unauthenticated, navigate to the Cognito-hosted UI.
If you use your identity provider, you will be redirected to your company&amp;rsquo;s login page. If you host the users from the user pool, a login form is shown to the user.
After the user has logged a redirect, the user is now authenticated.
The load balancer will now invoke the target group with the request.&lt;/p&gt;
&lt;p&gt;We also want to check if the user is in the developer group. We will use a Lambda function to check this. The following code would do the trick:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; base64
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; List
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(data: str) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; dict:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(base64&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;b64decode(data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;])&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;decode(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resolve_groups&lt;/span&gt;(groups: str) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; List[str]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; group: group&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip(), groups[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;(event, context) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; dict:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    code &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;403&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;403 Access Denied&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Access Denied&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; decode(event[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;headers&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x-amzn-oidc-data&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    groups &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resolve_groups(user&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;custom:groups&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[]&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;developers&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; groups:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        code &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        description &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;200 OK&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        body &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hi &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;user&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, you should be able to download the report&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;statusCode&amp;#34;&lt;/span&gt;: code,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;statusDescription&amp;#34;&lt;/span&gt;: description,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;isBase64Encoded&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;headers&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;json; charset=utf-8&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;: body,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The listener on the application load balancer and the user pool client can be configured as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Listener&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::ElasticLoadBalancingV2::Listener&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;LoadBalancerArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref LoadBalancer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Port&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Protocol&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;HTTPS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Certificates&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;CertificateArn&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:acm:eu-west-1:111122223333:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;DefaultActions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;AuthenticateCognitoConfig&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;OnUnauthenticatedRequest&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;authenticate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Scope&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;openid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;UserPoolArn&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:cognito-idp:eu-west-1:111122223333:userpool/eu-west-1_xXXXxxxx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;UserPoolClientId&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref UserPoolClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Order&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;authenticate-cognito&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;Order&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;TargetGroupArn&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Ref&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;LambdaTarget&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;forward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;UserPoolClient&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Cognito::UserPoolClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;AllowedOAuthFlows&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;AllowedOAuthFlowsUserPoolClient&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;AllowedOAuthScopes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;phone&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;email&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;openid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;aws.cognito.signin.user.admin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;CallbackURLs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;https://&amp;lt;MyDomainName&amp;gt;/oauth2/idpresponse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ClientName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MyClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;GenerateSecret&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;LogoutURLs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;https://&amp;lt;MyDomainName&amp;gt;/logout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;SupportedIdentityProviders&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;COGNITO&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;UserPoolId&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1_xXXXxxxx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You also need to setup the lambda function as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;LambdaSecurityGroup&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::EC2::SecurityGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;SecurityGroupEgress&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;CidrIp&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0.0.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow all outbound traffic by default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;IpProtocol&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;SecurityGroupIngress&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;CidrIp&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0.0.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow HTTPS access&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;FromPort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;IpProtocol&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;tcp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;ToPort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;VpcId&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref VPCAsParameter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;LambdaRole&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;AssumeRolePolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2012-10-17&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sts:AssumeRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Service&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;lambda.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ManagedPolicyArns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;LambdaFunction&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Lambda::Function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Code&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;S3Bucket&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;my-bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;S3Key&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;path/to/code.zip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Handler&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;index.handler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Role&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt LambdaRole.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Runtime&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;python3.12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;VpcConfig&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;SecurityGroupIds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt LambdaSecurityGroup.GroupId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;SubnetIds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref Subnet1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref Subnet2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref Subnet3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;LambdaPermissions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Lambda::Permission&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;lambda:InvokeFunction&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;FunctionName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt LambdaFunction.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;elasticloadbalancing.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;LambdaTarget&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::ElasticLoadBalancingV2::TargetGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;DependsOn&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;LambdaPermissions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;TargetType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;lambda&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Targets&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;Id&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt LambdaFunction.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will be prompted to log in when you navigate to the load balancer. Afterward, you can see that the lambda function was invoked. This is a very simple example, but the idea is that you can extend the lambda function with the logic you need. For example, you could create a pre-signed URL for the report on S3 and redirect the user directly to that URL. This will then download the report automatically.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Securing access to your Amazon S3 files doesn’t have to be complicated. By leveraging AWS Cognito, an Application Load Balancer, and a simple Lambda function, you can control exactly who gets access to your files—without exposing them to the public. With this setup, authentication is handled seamlessly, and authorization is as simple as checking group memberships. From here, you can expand the functionality further, such as generating pre-signed URLs for downloads or adding more granular permissions. The cloud makes it easy—you just need to know how!&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/security-logo-60504/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Become a documentation ninja</title>
      <link>https://conijn.io/blog/2025/02/become-a-documentation-ninja/</link>
      <pubDate>Tue, 18 Feb 2025 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2025/02/become-a-documentation-ninja/</guid>
      <description>&lt;p&gt;Writing documentation sucks! But there are ways to make it easier and maybe even fun! In one of my &lt;a href=&#34;https://conijn.io/blog/2024/09/make-writing-documentation-part-of-your-pull-request/&#34;&gt;previous blogs&lt;/a&gt;, I explained how you can embed your documentation in your pull requests and why you should consider doing it, too. In this blog, I want to go a bit deeper into the syntax you can use to improve your documentation pages on Confluence.&lt;/p&gt;
&lt;h2 id=&#34;syntax-you-mean-markdown-right&#34;&gt;Syntax? You mean Markdown, right?&lt;/h2&gt;
&lt;p&gt;Yes, Markdown is the documentation syntax that Mark uses. However, it has some extensions that are still valid in Markdown format and allow you to use the macros from Confluence.&lt;/p&gt;
&lt;h2 id=&#34;linking-to-other-pages&#34;&gt;Linking to other pages&lt;/h2&gt;
&lt;p&gt;When you write documentation, you probably have different pages explaining different things. You can logically split areas, making it easier to navigate and read. But sometimes, you want to link from one page to another. The problem is that you don’t know the URL yet since that is a Confluence page. You could publish the page and look up the link, which kills the motivation to write documentation. Assume we have a page like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# My Confluence Page
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;My description of the page
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we want to link to this page. You can simply do this with the following syntax:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Read this on [My Confluence Page](ac:) or [read](ac:My Confluence Page) the page.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, you can link to the page title itself. Or, you can use a custom word, like “read” and link to the page. When mark uploads the page to Confluence the links will be resolved automatically.&lt;/p&gt;
&lt;h2 id=&#34;linking-to-jira-issues&#34;&gt;Linking to Jira issues&lt;/h2&gt;
&lt;p&gt;Next to linking to pages, you will probably also refer to Jira issues. However, you could link to Jira issues using native markdown. When you use the Jira reference macro, the type, title, and status of the issue are shown. This gives immediate feedback to the reader on what the status is. What you need to do is to define a macro in the top of your markdown file and mention the issue as plain text. Here is an example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Macro: MYJIRA-&lt;/span&gt;\d&lt;span style=&#34;color:#75715e&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     Template: ac:jira:ticket
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     Ticket: ${0} →
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;# My page that contains a Jira ticket reference
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;See task MYJIRA-123.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example, &lt;code&gt;MYJIRA&lt;/code&gt; is the Jira project code. The macro definition matches everything that starts with &lt;code&gt;MYJIRA-&lt;/code&gt; and is followed by a set of digits. You can then simply mention the issues in your markdown file, and they will be converted into Jira macros automatically.&lt;/p&gt;
&lt;h2 id=&#34;hide-code-snippets-in-collapsable-macros&#34;&gt;Hide code snippets in collapsable macros&lt;/h2&gt;
&lt;p&gt;Especially when you write documentation, you may need to hide parts of the code snippets. One example that I use is for OS-dependent instructions. Assume you want to provide instructions on using your product on the command line. In that case, you could give instructions for Windows, Linux, and MacOS. To use this, you first need to define the macro. Then, use the macro to build the collapsable snippet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/expand.png&#34; alt=&#34;Example of an collapsable snippet&#34;&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Macro: :expand-start:(?s)(.*?):&lt;/span&gt;\n&lt;span style=&#34;color:#75715e&#34;&gt;(.*?)&lt;/span&gt;\n&lt;span style=&#34;color:#75715e&#34;&gt;:expand-end:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     Template: ac:expand
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     Title: ${1}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     Body: ${2}  --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# My page with command line instructions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Before you start, you must ensure you set the environment variable:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:expand-start:Unix:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Unix instructions go here
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:expand-end:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:expand-start:Windows:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Windows instructions go here
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:expand-end:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;page-properties-and-status-badges&#34;&gt;Page properties and status badges&lt;/h2&gt;
&lt;p&gt;This is my favorite! This is such a powerful feature of Confluence. For those of you who are not familiar with this functionality, you define a table, which contains a set of keys and values. For example, you can specify an owner and a date in this table. You also need to set a label on the page. This is required for the report page. The label is used to make a list of pages that have this label. But the cool thing is that you can also display the values of the page properties.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/projects.png&#34; alt=&#34;Example of page properties report&#34;&gt;&lt;/p&gt;
&lt;p&gt;As you can see in the screenshot above, we have three pages. The parent page holds the report, and the child pages are Project 1 and Project 2. However, it also shows the date, owner, and status badge. This is a great way to give a nice overview of projects.&lt;/p&gt;
&lt;p&gt;To create the page properties on the child page, you need the following syntax.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Parent: Projects --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Label: project --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Macro: :in-progress:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     Template: ac:status
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     Title: In Progress
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     Color: Blue --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Project 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;ac:structured-macro&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ac:name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;details&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ac:schema-version&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;ac:rich-text-body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;table&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tbody&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;strong&lt;/span&gt;&amp;gt;Status&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;strong&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;:in-progress:&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;strong&lt;/span&gt;&amp;gt;Owner&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;strong&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;Joris Conijn&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;strong&lt;/span&gt;&amp;gt;Date&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;strong&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;2025-02-15&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tbody&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;table&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;ac:rich-text-body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;ac:structured-macro&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Project 2 markdown file is similar to the one above. However, the parent page has a different definition.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Projects
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Include: ac:detailssummary
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     SortBy: Title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     CQL: &amp;#39;space = &amp;#34;Projects&amp;#34; and label = &amp;#34;project&amp;#34;&amp;#39; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example, my confluence space is called Projects. The Confluence Query Language (CQL) retrieves all pages in the Projects space that have the label project. A third requirement is that the page also needs to have the page properties defined. We did this on Projects 1 and 2, so the two projects are rendered in the table.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Writing documentation doesn’t have to be a chore. By leveraging Confluence macros within Markdown, you can enhance your documentation with dynamic links, Jira issue references, collapsible content, and structured page properties. These features make your documentation more interactive, easier to maintain, and more useful for your team.&lt;/p&gt;
&lt;p&gt;By embedding these enhancements directly in your markdown files, you streamline the process of writing and updating documentation, reducing friction and improving adoption. So, next time you document a feature or process, try incorporating these techniques. You might find documentation efficient and enjoyable!&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/a-man-practicing-japanese-martial-arts-7792259/&#34;&gt;cottonbro studio&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>ECS Fargate Persistent Storage: EFS Access Points vs. Lambda Workarounds</title>
      <link>https://conijn.io/blog/2025/02/ecs-fargate-persistent-storage-efs-access-points-vs-lambda-workarounds/</link>
      <pubDate>Tue, 04 Feb 2025 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2025/02/ecs-fargate-persistent-storage-efs-access-points-vs-lambda-workarounds/</guid>
      <description>&lt;p&gt;When running a Docker container on &lt;a href=&#34;https://aws.amazon.com/ecs/&#34;&gt;Amazon Elastic Container Service&lt;/a&gt; and &lt;a href=&#34;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html&#34;&gt;AWS Fargate&lt;/a&gt;, persistent storage is often a necessity. I initially attempted to solve this by manually creating the required directory on &lt;a href=&#34;https://aws.amazon.com/efs/&#34;&gt;Amazon Elastic File System&lt;/a&gt; using a &lt;a href=&#34;https://aws.amazon.com/lambda/&#34;&gt;AWS Lambda&lt;/a&gt; backed custom resource. While this worked, it introduced unnecessary complexity. Through experimentation, I discovered a more elegant solution—using EFS access points. In this post, I&amp;rsquo;ll walk through my journey, the challenges I faced, and how I ultimately simplified the setup with fewer resources and less maintenance.&lt;/p&gt;
&lt;h2 id=&#34;setting-up-efs&#34;&gt;Setting up EFS&lt;/h2&gt;
&lt;p&gt;When you start a container on AWS Fargate, you must define a TaskDefinition. This definition is used to start the container. Within this definition, you can define Volumes. These volumes can then be mounted in the container by specifying a mount point per container.&lt;/p&gt;
&lt;p&gt;So far, so good! But I quickly ran into an issue. The task could not be started because the path on EFS did not yet exist. I tried to mount &lt;code&gt;/my-task/data&lt;/code&gt; on &lt;code&gt;/data&lt;/code&gt; in the container. However, the &lt;code&gt;/my-task/data&lt;/code&gt; path does not exist on the EFS drive. The easy solution would be to hook it up to an &lt;a href=&#34;https://aws.amazon.com/ec2/&#34;&gt;Amazon EC2&lt;/a&gt; instance and create the folder from there. You might think that the problem is solved, but this is not the solution to the underlying problem. As you could have read in my &lt;a href=&#34;https://conijn.io/blog/2025/01/streamlining-workflows-with-feature-branches-and-logical-stacks/&#34;&gt;previous blog&lt;/a&gt;, I like to be able to spin up multiple versions of a stack. With this manual step in between, you can’t do that.&lt;/p&gt;
&lt;h2 id=&#34;how-about-a-custom-resource&#34;&gt;How about a custom resource?&lt;/h2&gt;
&lt;p&gt;We know we need to create a folder on the EFS drive. A Lambda function could do this, so I started implementing a custom resource. The idea is very simple: create a Lambda function that mounts the root of the EFS, and then the Lambda code would simply make the folder on EFS. We mark this custom resource as a dependency on the AWS Fargate service to ensure that the folder exists before we start the container and we are done.&lt;/p&gt;
&lt;p&gt;I fully implemented this flow, and it worked perfectly. When you deploy your stack, the custom resource does its thing, and the container can be launched successfully.&lt;/p&gt;
&lt;h2 id=&#34;is-there-a-better-way&#34;&gt;Is there a better way?&lt;/h2&gt;
&lt;p&gt;When implementing the custom resource, I needed to create an access point. A Lambda function can only mount an EFS drive through an access point. This access point allows you to define a path on EFS and the user and group ID used to access this path. If you supply &lt;code&gt;/my-lambda/&lt;/code&gt; as the path, the function can only see the content of the &lt;code&gt;my-lambda&lt;/code&gt; folder. This is great for protecting other paths from being accessed. From the lambda functions perspective, you don’t see the &lt;code&gt;my-lambda&lt;/code&gt; folder you only see the content.&lt;/p&gt;
&lt;p&gt;Why is this so important, you might ask? Well, this path is created on the EFS drive for you. I looked at the ECS documentation, and you can also use an access point for container volumes. Using the access point to define the &lt;code&gt;/my-task/data&lt;/code&gt;, I set the uid and gid to the same ID the user executed in the container. We could run the container without the need for a custom resource, so I deleted the function from my template.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Experimentation often leads to better solutions. My initial approach—using a Lambda function to create the necessary EFS directory—worked but added unnecessary complexity. By leveraging EFS access points, I achieved the same result with a cleaner, more maintainable setup. This experience reinforced an important lesson: the simplest solution is often the best. When working with cloud infrastructure, it&amp;rsquo;s always worth exploring built-in features before resorting to custom implementations.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/a-close-up-shot-of-letter-dice-6120219/&#34;&gt;Nataliya Vaitkevich&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Streamlining Workflows with Feature Branches and Logical Stacks</title>
      <link>https://conijn.io/blog/2025/01/streamlining-workflows-with-feature-branches-and-logical-stacks/</link>
      <pubDate>Sun, 26 Jan 2025 15:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2025/01/streamlining-workflows-with-feature-branches-and-logical-stacks/</guid>
      <description>&lt;p&gt;Efficient collaboration and streamlined deployment processes are essential in modern development workflows, especially for teams working on complex projects. Feature branches and stack-based development approaches offer powerful ways to isolate changes, test effectively, and ensure seamless integration. However, proper strategies can make managing resources, dependencies, and environments challenging. This blog explores how to optimize feature branch workflows, maintain encapsulated logical stacks, and apply best practices like resource naming to improve clarity, scalability, and cost-effectiveness.&lt;/p&gt;
&lt;h2 id=&#34;working-with-feature-branches&#34;&gt;Working with feature branches&lt;/h2&gt;
&lt;p&gt;I have a strong preference for encapsulating things into logical stacks. These stacks should have a minimal number of dependencies. And you should be able to deploy multiple instances. Let me first explain why you want to deploy multiple instances of the same stack. The answer is quite simple! Feature branches! If you work in a team with multiple people, you might work on different stories. Therefore, you don’t want to update the same environment with your changes. My change might break your change and vice versa. Detecting why something failed becomes more challenging in this case. By deploying your own environment, your changes will only impact your environment. When you are done, you can thoroughly test your changes before merging them into the main branch.&lt;/p&gt;
&lt;h2 id=&#34;working-with-multiple-stacks&#34;&gt;Working with multiple stacks&lt;/h2&gt;
&lt;p&gt;You can use multiple stacks, each with its area of responsibility, to keep things small and simple. However, this also introduces the danger of breaking encapsulation and introducing dependencies, such as an &lt;a href=&#34;https://aws.amazon.com/rds/&#34;&gt;Amazon Relational Database Service - managed SQL database&lt;/a&gt;. You need to create it in a base stack and pass it to other stacks. This is needed because the components in the other stacks will need the endpoint to connect to it, creating a dependency.&lt;/p&gt;
&lt;p&gt;This example applies to the more traditional lift and shift approaches. However, we always advise modernizing to reap all the benefits of the cloud. Why? Simple: In the example, we needed an RDS instance. If you create an RDS instance per stack, you will have multiple instances and must pay per RDS instance, driving up the cost. By switching to serverless, you pay for the usage. This will allow you to move the database into your stacks, removing the dependency. I explicitly say option because, in relational databases, it’s not always possible to split the database into two because of the nature of relational databases.&lt;/p&gt;
&lt;h2 id=&#34;use-proper-naming-for-your-resources&#34;&gt;Use proper naming for your resources.&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://aws.amazon.com/cloudformation/&#34;&gt;Amazon CloudFormation&lt;/a&gt; can handle naming resources for you. A naming schema follows this pattern: StackName + LogicalResourceId + RandomizedString. This ensures that you don’t end up with duplicate resource names. Some resources allow you to have duplicates, while others do not. So, having a guaranteed unique name is perfect from the perspective of having feature branches. But from a management perspective, this is bad. Most names are trimmed after several characters, and the generated long names are not that descriptive.&lt;/p&gt;
&lt;p&gt;By introducing a prefix, you can have a clear name and still deploy your stacks multiple times. You simply pass the prefix as a parameter and prefix each resource name. So, for an &lt;a href=&#34;https://aws.amazon.com/iam/&#34;&gt;AWS Identity and Access Management&lt;/a&gt; role, that could be &lt;code&gt;Joris-CheckoutProcess&lt;/code&gt;. The CheckoutProcess name describes what it is, a role used by, for example, a &lt;a href=&#34;https://aws.amazon.com/lambda/&#34;&gt;AWS Lambda&lt;/a&gt; function that processes the checkout. The prefix &lt;code&gt;Joris&lt;/code&gt; makes it unique and provides information about who owns and created the resource. Using this pattern you avoid creating a &lt;a href=&#34;https://conijn.io/blog/2025/01/stop-organizing-scavenger-hunts-in-your-cloud-infrastructure/&#34;&gt;scavenger hunt&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Adopting feature branches and logical stacks is an excellent way to foster efficient, error-free collaboration in team environments. You can enhance your development workflow by deploying isolated environments for individual changes, embracing serverless solutions to reduce dependencies, and implementing descriptive naming conventions. These practices streamline debugging and testing and ensure resource management remains clear and cost-efficient. Whether you’re modernizing applications or maintaining legacy systems, these strategies will help you maximize benefits of cloud-based development.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/green-leafed-tree-low-angle-photography-1313807/&#34;&gt;Min An&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Stop organizing scavenger hunts in your cloud infrastructure</title>
      <link>https://conijn.io/blog/2025/01/stop-organizing-scavenger-hunts-in-your-cloud-infrastructure/</link>
      <pubDate>Sat, 18 Jan 2025 16:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2025/01/stop-organizing-scavenger-hunts-in-your-cloud-infrastructure/</guid>
      <description>&lt;p&gt;A CloudWatch alarm is triggered. Now what? I am not the first person to tell you that observability is essential to your cloud infrastructure. You are not done when you have set up CloudWatch alarms!&lt;/p&gt;
&lt;h2 id=&#34;who-will-act-on-those-alarms&#34;&gt;Who will act on those alarms?&lt;/h2&gt;
&lt;p&gt;Observing some metrics and raising an alarm if a certain threshold is breached is just the start! You must also consider who will act if this alarm is breached. In some cases, you might be able to automate your actions. For example, you could think of a high CPU load on your application servers. The CloudWatch alarm is then triggered, and you can scale up your cluster.&lt;/p&gt;
&lt;p&gt;These auto-remediation actions work very well for all known issues that can go wrong. You can detect them and build remediation actions that you trigger based on these CloudWatch alarms.&lt;/p&gt;
&lt;p&gt;But in some cases, you just need a human to jump online and figure out what happened. When this happens, you don’t want to start a scavenger hunt in your AWS account to find the problem. You want a clear starting point and, from there, guidance in the right direction.&lt;/p&gt;
&lt;h2 id=&#34;cloudwatch-dashboards&#34;&gt;CloudWatch Dashboards&lt;/h2&gt;
&lt;p&gt;CloudWatch dashboards can guide you in the right direction during production issues. For example, assume we have a SQS Queue that contains messages. A lambda function is processing these messages. If the process fails, it will retry, and after three attempts, it will deliver the message to the dead-letter queue.&lt;/p&gt;
&lt;p&gt;When one or more messages are in the dead-letter queue, a CloudWatch alarm is triggered, and we know something went wrong in our application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/example.png&#34; alt=&#34;Example infrastructure of a SQS Queue with a dead-letter queue&#34;&gt;&lt;/p&gt;
&lt;p&gt;This alarm can trigger an SNS topic, and from there, you can reach an engineer who can investigate this issue. A dashboard that shows these components makes it easy to navigate to the issue.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/dashboard-alarm.png&#34; alt=&#34;CloudWatch Dashboard example where the alarm is triggered&#34;&gt;&lt;/p&gt;
&lt;p&gt;Here, you can see the dead-letter queues from our application. MyLambda is in an alarm state, and on the right, you see a link that brings you directly to that function&amp;rsquo;s LogGroup.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/loggroup.png&#34; alt=&#34;Screenshot of the log group of the lambda function&#34;&gt;&lt;/p&gt;
&lt;p&gt;It turns out that someone pushed code with an exception. We fixed the code and started re-driving the messages in the dead-letter queue back to the original queue. The lambda function is now processing the messages as it should, and the alarm will go back to an OK state.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/dashboard-ok.png&#34; alt=&#34;CloudWatch Dashboard example where the alarm is in a OK state&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you build cloud infrastructure, you also need to think about what can go wrong. Design for failure, build auto-remediation for all known failures and loop in a person when unknown issues appear. But be kind for your fellow engineer and don’t sent him on a scavenger hunt. Guide him to problem and give context about the failures in a CloudWatch dashboard.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/falcon-bird-flying-on-sky-6585227/&#34;&gt;Gill Heward&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Boost Your Productivity with awscurl: Simplifying IAM-Secured API Testing in AWS</title>
      <link>https://conijn.io/blog/2024/12/boost-your-productivity-with-awscurl/</link>
      <pubDate>Sat, 21 Dec 2024 14:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2024/12/boost-your-productivity-with-awscurl/</guid>
      <description>&lt;p&gt;With modern clouds, you can build awesome things. You can bring your ideas to life within the hour, enabling experimentation and driving innovation. One of the significant advantages of the cloud is that you get a lot of security controls out of the box. But these security controls can also block you from being productive!&lt;/p&gt;
&lt;h2 id=&#34;how-are-these-security-controls-blocking-me&#34;&gt;How are these security controls blocking me?&lt;/h2&gt;
&lt;p&gt;When you use AWS, you can interact with it through the console, sdk, or cli. But these all have one thing in common. They all use the same set of APIs to perform the actions requested by the user. These APIs are protected, and how authentication and authorization are done through the service &lt;a href=&#34;https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html&#34;&gt;IAM&lt;/a&gt;. When you develop a workload or work on a &lt;a href=&#34;https://en.wikipedia.org/wiki/Proof_of_concept&#34;&gt;PoC&lt;/a&gt;, you will also use the IAM service.&lt;/p&gt;
&lt;p&gt;Assume you want to build an internal API and use the &lt;a href=&#34;https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html&#34;&gt;API Gateway&lt;/a&gt; service. Enabling IAM authentication on the methods you define is easy. You can then give the appropriate roles access to your API. The consumer of this API only needs to add the &lt;a href=&#34;https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html&#34;&gt;AWSSigv4&lt;/a&gt; header, and as long as the role policy allows the invocation of the API, it will work.&lt;/p&gt;
&lt;p&gt;Various programming languages can be used to add the signature. But what if you want to test the API from your local machine or the cloud shell from the console? In those cases, you have a shell, and generating the signature becomes much harder. This will impact your productivity.&lt;/p&gt;
&lt;h2 id=&#34;how-can-we-unblock-ourselves&#34;&gt;How can we unblock ourselves?&lt;/h2&gt;
&lt;p&gt;As explained in the previous section, the challenge is to generate the signature based on the current payload. In the past, I used a simple Python script to perform these API calls, but that always took some time and energy to build. I also felt demotivated when I encountered such situations.&lt;/p&gt;
&lt;p&gt;The problem is not the script that you need to write. The problem is that you need to do it repeatedly because each situation is different. This goes against one of my principles, which is to automate everything! There should be a solution to this problem, and the good news is there is one!&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/okigan&#34;&gt;Igor Okulist&lt;/a&gt; created a small tool called &lt;a href=&#34;https://github.com/okigan/awscurl&#34;&gt;awscurl&lt;/a&gt;. This tool allows you to perform a curl command that automatically signs your API call. For example, when we use the scenario described in the previous section, you can simply do the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export AWS_PROFILE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;my-profile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;awscurl --region eu-west-1 --service execute-api -X GET https://my-url-to-my-api
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The example assumes that you have a valid profile configured called my-profile. Then, you do the same command as the original curl command. But you only add a region and a service option to the command. For the comparison, the normal curl command would look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -X GET https://my-url-to-my-api
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this command will result in the following error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Missing Authentication Token&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The awscurl command will inject the Authorization header that looks something like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Authorization: AWS4-HMAC-SHA256 Credential=XXX, SignedHeaders=XXX, Signature=XXX
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you try this out yourself, you can add the &lt;code&gt;-v&lt;/code&gt; option, which will print out the headers sent and received. You will see that the API now accepts the signature and responds with the actual results.&lt;/p&gt;
&lt;p&gt;You can use it to perform any API call that supports sigv4, but for the majority of services, the AWS cli tool is the best tool for the job. But for your own APIs or calls to your &lt;a href=&#34;https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html&#34;&gt;OpenSearch&lt;/a&gt; cluster, this tool is definitely a timesaver.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you develop an internal API or have an OpenSearch cluster that uses IAM for authentication and authorization, testing calls towards these endpoints can be challenging as you will need to create a signature. The awscurl cli tool helps you create this signature, allowing you to focus on your business value instead of building signatures for tests.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/launching-of-white-space-shuttle-34521/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Relative Python imports in a Dockerized lambda function</title>
      <link>https://conijn.io/blog/2024/12/relative-python-imports/</link>
      <pubDate>Sat, 14 Dec 2024 00:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2024/12/relative-python-imports/</guid>
      <description>&lt;p&gt;Relative Python imports can be tricky for lambda functions. I wrote &lt;a href=&#34;https://conijn.io/blog/2021/10/python-and-relative-imports-in-aws-lambda-functions/&#34;&gt;a blog&lt;/a&gt; on this 3 years ago. But recently, I ran into the same issue with Dockerized lambda functions. So, I figured it was time for a new blog!&lt;/p&gt;
&lt;p&gt;You can follow along with the steps or look at the result directly &lt;a href=&#34;https://github.com/conijnio/aws-cdk-docker-function&#34;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;project-setup&#34;&gt;Project setup&lt;/h2&gt;
&lt;p&gt;Make sure you installed the AWS CDK cli.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;brew install aws-cdk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Initialize the project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk init app --language&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;typescript
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;lambda-setup&#34;&gt;Lambda setup&lt;/h2&gt;
&lt;p&gt;First we will need to create the file and folder structure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p lib/functions/hello-world/hello_world
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch lib/functions/hello-world/hello_world/__init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch lib/functions/hello-world/hello_world/business_logic.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch lib/functions/hello-world/requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch lib/functions/hello-world/Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you will need to fill the Dockerfile, like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Dockerfile&#34; data-lang=&#34;Dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; public.ecr.aws/lambda/python:3.12&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;COPY&lt;/span&gt; requirements.txt .&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;COPY&lt;/span&gt; hello_world &lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;LAMBDA_TASK_ROOT&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;/hello_world&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; pip install --no-cache-dir -r requirements.txt&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CMD&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello_world.handler&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are using a Python base image that is based on Python 3.12. Next, we will copy in the &lt;code&gt;requirements.txt&lt;/code&gt; file and the source code. We will install all dependencies listed in the &lt;code&gt;requirements.txt&lt;/code&gt; file and make sure that the &lt;code&gt;handler&lt;/code&gt; method is set as the &lt;code&gt;CMD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we will need to fill our Python files with some code. In the &lt;code&gt;__init__.py&lt;/code&gt; file, you can place the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Dict, Any
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Example of the relative import&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; .business_logic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; business_logic
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;(event: Dict[str, Any], context: Any) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; Dict[str, str]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# User the method that is imported based on a relative path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; business_logic(event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;: name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;!&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__all__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;handler&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Afterward, we will need to fill the &lt;code&gt;business_logic.py&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Dict, Any
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;business_logic&lt;/span&gt;(event: Dict[str, Any]) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;NOTE: The code used here could use multiple relative imports. This is possible because it is in a separate package. This example only shows one example in the &lt;code&gt;__init__.py&lt;/code&gt; file. However, you can use multiple files here to improve the maintainability of your project.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;For this example, I don&amp;rsquo;t need any dependencies, so we can keep the &lt;code&gt;requirements.txt&lt;/code&gt; file empty. I included it in this example to illustrate how you can include dependencies as well.&lt;/p&gt;
&lt;h2 id=&#34;create-the-lambda-function-using-iac&#34;&gt;Create the Lambda function using IaC&lt;/h2&gt;
&lt;p&gt;Our folders and files are in place, so it is time to add the Lambda function to the CDK construct. You can simply add it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt;.Function(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Function&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;functionName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello-world&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;lambda.Code.fromAssetImage&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lib/functions/hello-world&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;ecr_assets.Platform.LINUX_ARM64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;runtime&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;lambda.Runtime.FROM_IMAGE&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;lambda.Handler.FROM_IMAGE&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;architecture&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;lambda.Architecture.ARM_64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;timeout&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;cdk.Duration.seconds&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;memorySize&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;128&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For this to work, you also need the following imports:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;aws-cdk-lib/aws-lambda&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ecr_assets&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;aws-cdk-lib/aws-ecr-assets&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that we make sure that the code directory points to the directory that contains the &lt;code&gt;Dockerfile&lt;/code&gt; and that we select the ARM platform for both the code and the function itself.&lt;/p&gt;
&lt;h2 id=&#34;testing-the-lambda-function-locally&#34;&gt;Testing the lambda function locally&lt;/h2&gt;
&lt;p&gt;Fast feedback is important, so there might be cases where you need to run the container locally. For this, you first need to build the container:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker build --platform linux/arm64 &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -t hello-world:latest &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -f ./lib/functions/hello-world/Dockerfile &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  ./lib/functions/hello-world
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that this command can be executed from the project&amp;rsquo;s root. Next, we need to make sure it&amp;rsquo;s running before we can invoke it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run --platform linux/arm64 -p 9000:8080 hello-world:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Afterwards, you can invoke the function as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl http://localhost:9000/2015-03-31/functions/function/invocations -d &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{&amp;#34;name&amp;#34;: &amp;#34;Joris&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Relative imports can be tricky! You need to place your code in a package. This allows you to do relative imports within your own package. This will enable cleaner code, as you can split responsibilities into multiple files, making it easier to manage and maintain.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/man-jumping-on-intermodal-container-379964/&#34;&gt;Kaique Rocha&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Serverless, it can help you brew beer</title>
      <link>https://conijn.io/blog/2024/10/serverless-it-can-help-you-brew-beer/</link>
      <pubDate>Fri, 25 Oct 2024 13:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2024/10/serverless-it-can-help-you-brew-beer/</guid>
      <description>&lt;p&gt;I like to combine technology with something more practical. This helps me understand the technology much better. Plus, when you have a practical example, it’s also easier to explain to my wife and friends. During the coronavirus pandemic, I started brewing beer. Brewing beer involves a lot of steps, and the brewer needs to take many steps. But some steps can be automated!&lt;/p&gt;
&lt;h2 id=&#34;brewing-101&#34;&gt;Brewing 101&lt;/h2&gt;
&lt;p&gt;Before we dive into the automation part, let’s do a quick brewing introduction. To brew a beer, you need the following ingredients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Malted grains&lt;/li&gt;
&lt;li&gt;Hops&lt;/li&gt;
&lt;li&gt;Water&lt;/li&gt;
&lt;li&gt;Yeast&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The grains are crushed just enough to be broken inside. The other layer needs to be somewhat whole. We can then start the mashing process in water around 60 degrees. This will extract the sugars from the grain. When this process is done, you must separate the water from the grain bed. Afterward, you can sparge some additional water through the grain bed to grab the last bits of sugar from the grain.&lt;/p&gt;
&lt;p&gt;The product we now have is called wort. Now it’s time to boil the wort. Depending on the recipe, you need to add hops and other ingredients. The general rule for hops is that when you add hops in the beginning, it makes the beer more bitter. Adding more towards the end adds more aroma. The type of hops that you add also plays a role here.&lt;/p&gt;
&lt;p&gt;After you boil the wort, it’s time to cool it down. You need to bring the wort back to around 20 degrees. Again, this highly depends on the yeast you are using. When the wort is cool enough, you want to add as much oxygen as possible to it and the yeast.&lt;/p&gt;
&lt;p&gt;Close the kettle holding the beer and make sure it has a water lock. The yeast will start eating the sugars in the wort and producing CO2 and alcohol. The CO2 will escape through the water lock, and the alcohol will stay in the wort.&lt;/p&gt;
&lt;h2 id=&#34;how-does-serverless-help&#34;&gt;How does Serverless help?&lt;/h2&gt;
&lt;p&gt;During the fermentation process, the density of the wort changes. It’s slowly changing from wort to beer. The sugars will decrease, and the alcohol will increase over time. But when is this process done?&lt;/p&gt;
&lt;p&gt;Density can be measured, so by putting a device in the wort, you can send updates about the density. When you combine this with some cloud expertise, you can build some cool things! Since this device would only send some reading with a value, I don’t need to wait for a response. The API needs to be as fast as possible! When the device waits for confirmation, it consumes more power. Since this device is floating in a fermenting chamber, you can’t hook it up to the power grid or change batteries.&lt;/p&gt;
&lt;p&gt;Due to this requirement, I used the API Gateway service from AWS. It allows you to place the incoming payload directly into an SQS Queue. This creates a really fast API for the device floating in my fermenting chamber. Using a queue completely decouples it. A secondary process can just read the messages from the queue and process them accordingly.&lt;/p&gt;
&lt;h2 id=&#34;how-about-security&#34;&gt;How about security&lt;/h2&gt;
&lt;p&gt;When the device sends its data, it also contains the identifier of the device itself. I used this device ID to link it to a registered device. If the device is unknown, I would just stop processing the message. This would allow you to tie a device to a particular tenant in your solution. However, when you know the endpoint, you can still inject messages. To prevent this from happening, you could use mTLS. The downside in my case is that that has an impact on the battery life. Another other option would be a custom authorizer. This allows you to use a Lambda function to use business logic to decide whether the call can be performed.&lt;/p&gt;
&lt;p&gt;In my case, I used the usage plans functionality. The documentation clearly states that you should not use the usage plans for authentication. And I am not! I only use it to limit the pressure on my backend, just in case someone figures out my API endpoint and tries to DDOS me. When the payload is malformed or the device ID is unknown, I will directly drop the messages anyway. So, the impact is limited unless they know the identifiers of my devices.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Real-world examples help illustrate our options for serverless technology. They help you consider other dimensions, like do I have enough battery life? These pros and cons need to be considered during your design. Based on those questions, you might pivot your solution&amp;rsquo;s architecture.&lt;/p&gt;
&lt;p&gt;If you want to know more about what I have automated, make sure you follow me on my social media.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Avoid Costly Loops in AWS Step Functions</title>
      <link>https://conijn.io/blog/2024/10/avoid-costly-loops-in-aws-step-functions/</link>
      <pubDate>Sat, 12 Oct 2024 13:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2024/10/avoid-costly-loops-in-aws-step-functions/</guid>
      <description>&lt;p&gt;We all played around with AWS StepFunctions in our careers. It is a fantastic orchestration tool! But there are some scenarios where your cloud bill can explode in your face! In this blog, I will walk you through how you can end up in this situation and, maybe more importantly, how to avoid it!&lt;/p&gt;
&lt;h2 id=&#34;looping&#34;&gt;Looping&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;./images/stepfunction_loop.png&#34; alt=&#34;StepFunction loop example&#34;&gt;&lt;/p&gt;
&lt;p&gt;When you look at this pattern, you might think, okay, we wait for 5 seconds and use lambda to query something. If the response is true, we are done; if not, we will wait again and check the status once more. The problem with this pattern is that the response might never be true, causing an endless loop, which will generate cost.&lt;/p&gt;
&lt;h2 id=&#34;there-are-no-endless-loops-in-stepfunctions&#34;&gt;There are no endless loops in StepFunctions&lt;/h2&gt;
&lt;p&gt;If you find yourself in such an “endless” loop, AWS will stop executing once you have exceeded 25,000 events. Even if you implement it, it will eventually be stopped by AWS. But each loop has 3 transitions, and you must pay for these transactions. They are pretty cheap, but the problem lies within this loop, especially if this pattern is part of a map distribution that allows you to do 10,000 parallel executions.&lt;/p&gt;
&lt;p&gt;Assume your StepFunction is invoked based on an S3 event trigger. We upload 100 files in our S3 bucket. This will trigger 100 executions resulting in 3,000,000 state transitions each 5 seconds until they exceed the 25,000 events.&lt;/p&gt;
&lt;p&gt;You can already see that this will ramp up your AWS bill.&lt;/p&gt;
&lt;h2 id=&#34;the-callback-pattern&#34;&gt;The callback pattern&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;./images/stepfunctions_callback.png&#34; alt=&#34;StepFunction Callback pattern&#34;&gt;&lt;/p&gt;
&lt;p&gt;You can solve this by implementing a callback pattern. It’s quite simple: You will place a message in an SQS Queue. You can then use any consumer to do the task at hand. When it’s done, the consumer notifies the step function execution and passes the result back.&lt;/p&gt;
&lt;p&gt;This is a known pattern, but we design our systems for failure! The cool thing about a SQS Queue is its retry mechanism.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/architecture.png&#34; alt=&#34;Callback pattern with a dead letter queue&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now, we can design it as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The StepFunction will place the task in the SQS Queue.&lt;/li&gt;
&lt;li&gt;The Processor will receive a batch of messages&lt;/li&gt;
&lt;li&gt;The function will process each message, and for each, it will:
&lt;ol&gt;
&lt;li&gt;Send a heartbeat to the StepFunction execution. This prevents the StepFunction from timing out.&lt;/li&gt;
&lt;li&gt;If the message is invalid, we can fail the StepFunction execution directly.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Depending on the outcome, the function will:
&lt;ol&gt;
&lt;li&gt;When a failure occurs, the message will be kept in the queue.
&lt;ul&gt;
&lt;li&gt;This will trigger the retry mechanism based on the visibility settings on the queue.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When the message is processed successfully, we will:
&lt;ul&gt;
&lt;li&gt;Send a success to the StepFunction execution.&lt;/li&gt;
&lt;li&gt;Remove the message from the queue.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;When the message has reached the maximum number of retries, it will be delivered to the dead letter queue.&lt;/li&gt;
&lt;li&gt;The Dead Letter Queue Processor will receive the message.&lt;/li&gt;
&lt;li&gt;It will fail the StepFunction execution with some additional context.&lt;/li&gt;
&lt;li&gt;Remove the message from the dead letter queue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Why is this a good design? It accounts for failure! We left the actual processing job out of scope here. However, the lambda processing can fail for many reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It can reach AWS Service limits.&lt;/li&gt;
&lt;li&gt;It might depend on another external system that is unavailable.&lt;/li&gt;
&lt;li&gt;You&amp;rsquo;re scaling so fast that the serverless components can’t handle the load yet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You have a better chance of succeeding with the SQS Queue and the retry mechanism. If the retries are exhausted, the dead letter queue will mark the failure in the step function itself. This will keep all the execution information in a single place for you to analyze.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Avoid creating loops in your StepFunctions. Use the callback pattern instead! Design your flows to be robust and expect failures to happen!&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/intricate-abstract-3d-render-sculpture-28844046/&#34;&gt;Steve Johnson&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Make writing documentation part of your pull request</title>
      <link>https://conijn.io/blog/2024/09/make-writing-documentation-part-of-your-pull-request/</link>
      <pubDate>Wed, 25 Sep 2024 20:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2024/09/make-writing-documentation-part-of-your-pull-request/</guid>
      <description>&lt;p&gt;We are all guilty of this one! We changed something, and we forgot to update the documentation. Even if you have the best intentions to do your best, it&amp;rsquo;s way too easy not to do it.&lt;/p&gt;
&lt;h2 id=&#34;why-is-it-so-easy-to-skip&#34;&gt;Why is it so easy to skip?&lt;/h2&gt;
&lt;p&gt;Assume you changed something, created a pull request, and followed all the defined processes before you can merge the change. When is the best time to update the documentation in this process? If you update the documentation while the pull request is open, the documentation is ahead. It refers to a state that might not be true until you merge your pull request. What if you decide not to merge? Will you remember to revert the documentation?&lt;/p&gt;
&lt;p&gt;The other way around is also far from ideal. Before you are ready to merge the pull request, it might take some iterations. Your peers give feedback and comments. So, by the time you have your pull request ready to be merged, you are already mentally done with it. So you need discipline to do the extra step and write the documentation.&lt;/p&gt;
&lt;p&gt;Of course, you can make this part of a definition of done and create all sorts of processes around it. But these highly rely on the professionalism of the team itself.&lt;/p&gt;
&lt;h2 id=&#34;make-it-part-of-your-pull-request&#34;&gt;Make it part of your pull request.&lt;/h2&gt;
&lt;p&gt;I began my career as a software engineer, where I learned concepts like encapsulation. The idea is simple: You place all the things that belong to each other as close as possible.&lt;/p&gt;
&lt;p&gt;Let’s apply this concept to the documentation of our solution. We can place the documentation in our git repository and change the logic and behavior in a pull request. The developer can also include any documentation changes in this same pull request. For the reviewer, it becomes easier. They see the code changes and the documentation changes, and they can see the correlation between them. When a developer forgets to update the documentation, peers can ask for the documentation changes as well. So, it&amp;rsquo;s a win-win situation already!&lt;/p&gt;
&lt;h2 id=&#34;but-my-audience-does-not-use-git&#34;&gt;But my audience does not use git!&lt;/h2&gt;
&lt;p&gt;Most companies that I worked for use Confluence for their documentation. However, this does not have to stop you from using git! You can sync your Markdown-based documentation to Confluence using &lt;a href=&#34;https://github.com/kovetskiy/mark&#34;&gt;mark&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;In this example, I use the GitHub actions to perform the sync action when we push to our main branch:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy the documentation to confluence&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;push&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;branches&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Documentation&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Checkout repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;fetch-depth&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Setup Golang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/setup-go@v5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;go-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;^1.21.13&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;cache-dependency-path&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;**/go.sum&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;go install github.com/kovetskiy/mark@latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Sync Documentation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;MARK_SPACE&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MySpace&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;MARK_BASE_URL&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;https://xebia.atlassian.net/wiki&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;MARK_USERNAME&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.CONFLUENCE_USERNAME }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;MARK_PASSWORD&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.CONFLUENCE_PASSWORD }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          MESSAGE=$(git log -1 --pretty=format:&amp;#34;%s (by %an in %h)&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          UPDATED_DOC_FILES=$(git diff-tree --no-commit-id --name-only -r HEAD --root docs | grep &amp;#39;\.md$&amp;#39; || true)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          for FILE in ${UPDATED_DOC_FILES}; do
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            if [[ -e &amp;#34;${FILE}&amp;#34; ]]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              echo &amp;#34;&amp;gt; Sync ${FILE}&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              mark --version-message=&amp;#34;${MESSAGE}&amp;#34; --drop-h1 --title-from-h1 -f ${FILE} || exit 1; echo;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              echo &amp;#34;&amp;gt; Skipping ${FILE} as it was deleted in the last commit&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let’s break it down into smaller steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check out the code with a depth of 2. This enables us to detect the changes made in the last commit.&lt;/li&gt;
&lt;li&gt;Ensure we have a Golang environment, and we will cache the dependencies to speed up the build process.&lt;/li&gt;
&lt;li&gt;Installing mark into the environment.&lt;/li&gt;
&lt;li&gt;Grab the last commit message.&lt;/li&gt;
&lt;li&gt;Grab Markdown files from the last commit that have been updated.&lt;/li&gt;
&lt;li&gt;Sync each file that has been changed, use the commit message in the confluence version management.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;./images/confluence.png&#34; alt=&#34;Example of the history of confluence&#34;&gt;&lt;/p&gt;
&lt;p&gt;Pretty cool, right? Now, when the pull request is merged, the changes made, including the documentation, are applied when they are merged! Unfortunately, this tool does not support deleting pages.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By combining the documentation with your code, you will encapsulate them. This will make it easier to keep them up to date and thus improve the overall quality.&lt;/p&gt;
&lt;p&gt;Want to &lt;a href=&#34;https://conijn.io/blog/2025/02/become-a-documentation-ninja/&#34;&gt;become a documentation ninja&lt;/a&gt;, read my follow-up blog?&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/spiral-notebooks-272980/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Abuse CDK functionality in CloudFormation</title>
      <link>https://conijn.io/blog/2024/09/abuse-cdk-functionality-in-cloudformation/</link>
      <pubDate>Wed, 18 Sep 2024 19:42:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2024/09/abuse-cdk-functionality-in-cloudformation/</guid>
      <description>&lt;p&gt;Each “Infrastructure as Code” framework has pros and cons. My go-to framework is CloudFormation, but I like CDK&amp;rsquo;s tree view, so I started thinking about borrowing this.&lt;/p&gt;
&lt;h2 id=&#34;what-is-the-tree-view&#34;&gt;What is the tree view?&lt;/h2&gt;
&lt;p&gt;AWS &lt;a href=&#34;https://aws.amazon.com/about-aws/whats-new/2022/09/aws-cloud-development-kit-cdk-announces-cdk-construct-tree-view-cloudformation-console/&#34;&gt;announced this functionality&lt;/a&gt; on September the 14th, 2022.&lt;/p&gt;
&lt;p&gt;Customers using CDK want a simple way to map the resources synthesized in a CloudFormation template back to the source CDK Construct.&lt;/p&gt;
&lt;p&gt;Normally, the Resources tab looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/resources-cloudformation.png&#34; alt=&#34;Example of a normal CloudFormation template&#34;&gt;&lt;/p&gt;
&lt;p&gt;But when you deploy a CDK stack, you get a new option, the Tree view! When you navigate to the stack in the console, you will see the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/resources-cdk.png&#34; alt=&#34;Example of the new option showing in the console&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;how-is-cdk-using-it&#34;&gt;How is CDK using it?&lt;/h2&gt;
&lt;p&gt;When you write your constructs, each resource will be nested in the construct. You can use a construct inside of a construct. This will result in a nested tree structure like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/cdk-stack-example.png&#34; alt=&#34;Example of a CDK Stack&#34;&gt;&lt;/p&gt;
&lt;p&gt;As you might have noticed, there is some unneeded nesting here. At the root level, we have only one entry, the environment name from the pipeline. Then, we have the name of the stack that we use. Going back to the intention of this feature, it makes sense, but we are looking at a stack deployment in CloudFormation. So yes, this will be in the development account we already knew because we logged into it. Next, the stack name, we needed to click the stack name to get to this view. From that point of view, it has no value in showing it here.&lt;/p&gt;
&lt;p&gt;The third level becomes more interesting. Here are the constructs that we use inside the stack itself. On this level, CDK does a pretty good job of organizing the resources into logical nested groups. A bucket policy applies to a bucket, so it’s nested under the bucket itself, for example.&lt;/p&gt;
&lt;h2 id=&#34;how-can-we-abuse-it&#34;&gt;How can we abuse it?&lt;/h2&gt;
&lt;p&gt;When you look at the resource definitions in the synthesized version, you can see how it is done. The bucket will contain some additional metadata like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;ApplicationBucketPolicy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::BucketPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;aws:cdk:path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Development/AppStack/StaticWebsiteConstruct/Bucket/BucketPolicy/Resource&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can play around with this and organize the resources any way you want. Here is an example that I did to experiment with:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/custom-cdk-paths.png&#34; alt=&#34;Example on how to organize your CloudFormation resources using this functionality&#34;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, this is organized more logically. We have the following major components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Going into the Authentication, you can see that I have some triggers. Diving deeper, you can see what kind of triggers. From there, you will have the resources needed for the operation, like permissions, log groups, and the function itself.&lt;/p&gt;
&lt;h2 id=&#34;known-requirements&#34;&gt;Known requirements&lt;/h2&gt;
&lt;p&gt;While playing around with the organization of this tree view, I discovered the following requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The value in the &lt;code&gt;aws:cdk:path&lt;/code&gt; must be unique across the processed template!&lt;/li&gt;
&lt;li&gt;When you use the &lt;a href=&#34;https://aws.amazon.com/serverless/sam/&#34;&gt;AWS Serverless Application Model&lt;/a&gt;, you need to ensure that the resource definition does not result in multiple resources. (This will lead to multiple values being the same, which will break the rendering of the tree view.)&lt;/li&gt;
&lt;li&gt;When you omit the &lt;code&gt;/Resource&lt;/code&gt; at the end, the LogicalId in the template is used. This could lead to uglier names in the tree view.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you like organizing your CloudFormation templates, check out the tree view. It might have been built for CDK, but you can use it directly in your CloudFormation templates too.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/selective-focus-photography-of-color-pencil-lot-2170/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Share your configuration across AWS accounts</title>
      <link>https://conijn.io/blog/2024/08/share-your-configuration-across-aws-accounts/</link>
      <pubDate>Sat, 31 Aug 2024 10:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2024/08/share-your-configuration-across-aws-accounts/</guid>
      <description>&lt;p&gt;It’s a best practice to split your environments into separate AWS accounts. It does not cost you anything extra and you can guarantee that your development environment is completely isolated from the higher environments. With this setup, it’s also common to have a build or tooling account to be responsible for the deployment of your application.&lt;/p&gt;
&lt;p&gt;But it does bring some other challenges, for example: When you have a web application it usually has a frontend and an API that it uses. If you use separate pipelines to deploy these components you need a way to share the endpoint. You could use a predictable schema to determine the endpoint programmatically when you build the web application. This works well for a URL, but this does not work for a Cognito Client ID.&lt;/p&gt;
&lt;h2 id=&#34;use-ssm-parameters&#34;&gt;Use SSM Parameters&lt;/h2&gt;
&lt;p&gt;SSM Parameters are a key/value store, they are perfect for configuration values. When you deploy your API, Cognito user pool and client. You can store the value in an SSM parameter like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;UserPoolClientIdParameter&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::SSM::Parameter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/&amp;lt;Environment&amp;gt;/cognito/user-pool-client-id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Tier&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Advanced&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Value&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref AppClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, you can share this parameter with the build/tooling account:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;ShareUserPoolClientIdParameter&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::RAM::ResourceShare&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;AllowExternalPrincipals&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;UserPool Client ID - &amp;lt;Environment&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;PermissionArns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     - &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:ram::aws:permission/AWSRAMDefaultPermissionSSMParameterReadOnly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Principals&lt;/span&gt;: 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#75715e&#34;&gt;# This is the account id of your build/tooling account&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     - &lt;span style=&#34;color:#ae81ff&#34;&gt;111122223333&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;ResourceArns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${UserPoolClientIdParameter}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;resolve-the-values-in-codebuild&#34;&gt;Resolve the values in CodeBuild&lt;/h2&gt;
&lt;p&gt;Now that we have shared the configuration with the build/tooling account. We need to resolve the value. First, make sure that the principle is allowed to resolve the parameter:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ssm:GetParameter&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ssm:GetParameters&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:ssm:eu-west-1:444455556666:parameter/&amp;lt;Environment&amp;gt;/cognito/user-pool-client-id&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we need to add the parameter to the &lt;a href=&#34;https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec-ref-example&#34;&gt;buildspec&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;parameter-store&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;USER_POOL_CLIENT_ID&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:ssm:eu-west-1:444455556666:parameter/&amp;lt;Environment&amp;gt;/cognito/user-pool-client-id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;phases&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ae81ff&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now in theory this should work… However, this parameter will not resolve. I assume that CodeBuild can only resolve parameters stored in the same account. But you can simply work around this issue by fetching the parameter yourself:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export USER_POOL_CLIENT_ID&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;aws ssm get-parameter --name arn:aws:ssm:eu-west-1:444455556666:parameter/&amp;lt;Environment&amp;gt;/cognito/user-pool-client-id --output text --query &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Parameter.Value&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I preferred the native solution but unfortunately, this is not working. Until it is this would be my recommended solution.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Following best practices is always a good idea. But they can introduce new challenges. In the example given, I have shown you how to use an SSM parameter to share configuration across accounts.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/man-passing-case-of-bottles-3079261/&#34;&gt;Mostafa Meraji&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to Speedup Your CDK Test Feedback</title>
      <link>https://conijn.io/blog/2024/05/how-to-speedup-your-cdk-test-feedback/</link>
      <pubDate>Wed, 08 May 2024 19:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2024/05/how-to-speedup-your-cdk-test-feedback/</guid>
      <description>&lt;p&gt;When you deploy lambda functions using CDK and a test-driven approach, you might have noticed that the test feedback takes longer each time you add a new function. That&amp;rsquo;s because each function will be bundled with its own set of dependencies. These dependencies need to be downloaded before CDK can bundle them into a single package.&lt;/p&gt;
&lt;h2 id=&#34;cdk-synth&#34;&gt;CDK Synth&lt;/h2&gt;
&lt;p&gt;Before you can deploy your infrastructure you need to synthesize your code. This will transform your code into a CloudFormation template plus some assets. When you have a lambda function the content of this function will be bundled into one of these assets.&lt;/p&gt;
&lt;p&gt;This all makes sense when you deploy your code. You need to ship the actual lambda code to AWS in the correct format with the correct dependencies. Otherwise, your solution will not work.&lt;/p&gt;
&lt;p&gt;But the synth step is also used when you test your infrastructure code. The process is like the deploy step. Instead of deploying your templates, you will inspect the outcome. For example, you are expecting a Lambda function. This function will use a certain runtime. And, for production environments, you want to confirm that the backup tags are applied.&lt;/p&gt;
&lt;h3 id=&#34;skip-the-bundling-when-you-run-the-tests&#34;&gt;Skip the bundling when you run the tests!&lt;/h3&gt;
&lt;p&gt;It might be an obvious answer. &lt;strong&gt;Skip the bundling when you run the tests!&lt;/strong&gt; Assume we use the following code to create a Lambda function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sampleFunction&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt;.Function(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SampleFunction&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;lambda.Code.fromAsset&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;./sample_function/&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;bundling&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;command&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bash&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pip install --target /asset-output -r ./requirements.txt &amp;amp;&amp;amp; cp -au . /asset-output&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;lambda.Runtime.PYTHON_3_12.bundlingImage&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;runtime&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;lambda.Runtime.PYTHON_3_12&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;index.handler&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;timeout&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;cdk.Duration.seconds&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;memorySize&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;1024&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can see that we are loading the code from a folder called &lt;code&gt;sample_function&lt;/code&gt;. Then we pass the options on how CDK needs to bundle the function:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, the dependencies will be downloaded and installed.&lt;/li&gt;
&lt;li&gt;Next, the content from the &lt;code&gt;sample_function&lt;/code&gt; is copied.&lt;/li&gt;
&lt;li&gt;The content of the &lt;code&gt;asset_output&lt;/code&gt; folder will be the bundled function.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, these steps will become part of the test. You can skip the bundling by defining the test as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cdk&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;aws-cdk-lib&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;Template&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;aws-cdk-lib/assertions&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;AppStack&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;./AppStack&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Sample unit test on how to skip bundling&amp;#39;&lt;/span&gt;, () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Setup the application
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cdk&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;aws:cdk:bundling-stacks&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Setup the CDK stack
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stack&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AppStack&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MyTestStack&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;backups&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// For production we enable backups
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Synth the code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Template&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromStack&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;stack&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Confirm that the Lambda function uses python 3.12 runtime
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;template&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;hasResourceProperties&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;AWS::Lambda::Function&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Runtime&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;python3.12&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Confirm that the DynamoDB table has the Backup tag
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;template&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;hasResourceProperties&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;AWS::DynamoDB::Table&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Tags&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Backup&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;YES&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A few things are happening in this example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the app object is being created, some specific context is supplied. This context will overwrite the &lt;code&gt;aws:cdk:bundling-stacks&lt;/code&gt; option. This means that the bundling options are overwritten with nothing.&lt;/li&gt;
&lt;li&gt;Then the stack object is created and a template is synthesized.&lt;/li&gt;
&lt;li&gt;We will confirm that we have a lambda function with the Python 3.12 runtime.&lt;/li&gt;
&lt;li&gt;We will also confirm that the DynamoDB table has the Backup tag with the value YES.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because we skipped the bundling the test will execute immediately. Giving you instant feedback on the code that you have been writing. You can now run the test after each change. This enables you to create a nice development cycle:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Adding a test implementation.&lt;/li&gt;
&lt;li&gt;Adding the business logic.&lt;/li&gt;
&lt;li&gt;Running the tests to confirm the implementation.&lt;/li&gt;
&lt;li&gt;Repeat&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;why-is-it-ok-to-skip-the-bundling&#34;&gt;Why is it ok to skip the bundling?&lt;/h2&gt;
&lt;p&gt;When you write tests you should confirm &lt;strong&gt;your&lt;/strong&gt; business logic. The bundling logic has already been tested by the team that is maintaining CDK. So you don’t need to test it and it’s safe to skip the bundling.&lt;/p&gt;
&lt;p&gt;You could argue that the options in the bundling need validation too. And yes, that is now skipped. Yet the bundling of these functions is usually always the same for each runtime. And when you deploy you will catch any bundling mistakes anyway. Next to that, you have tests for your lambda code as well. These tests are not executed against the bundled version of your code. These are executed against the source version of your code. So there is no need to bundle them from a testing perspective.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When it takes too long to run tests, you will run them less. This has a negative impact on your quality and efficiency. You can solve this by excluding heavy operations. Operations like downloading dependencies and bundling your lambda functions. This also helps with focussing on the tests that matter. The tests that actually confirm your business value.&lt;/p&gt;
&lt;p&gt;Follow me if you want to learn more about improving your efficiency and code quality.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/black-and-white-roller-coaster-106155/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use CloudWatch LogGroups for EC2 logging</title>
      <link>https://conijn.io/blog/2024/04/use-cloudwatch-loggroups-for-ec2-logging/</link>
      <pubDate>Wed, 10 Apr 2024 10:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2024/04/use-cloudwatch-loggroups-for-ec2-logging/</guid>
      <description>&lt;p&gt;You can protect yourself from losing logs on Amazon EC2 by using CloudWatch Logs. Configure the CloudWatch Agent to stream your logs to a LogGroup. This protects you from losing logs. For example, when the instance is replaced by autoscaling. You are also protected against tampering of the logs. An attacker who has gained access to your system can remove the logs. But the logs in the LogGroup will contain the original log lines.&lt;/p&gt;
&lt;h2 id=&#34;preparations&#34;&gt;Preparations&lt;/h2&gt;
&lt;p&gt;We will need the following resources:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;InstanceSecurityGroup&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::EC2::SecurityGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;GroupDescription&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Security group for the test instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;VpcId&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{resolve:ssm:/landingzone/vpc/vpc-id}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;SecurityGroupEgress&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#f92672&#34;&gt;Description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow outbound connectivity to port 443.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;IpProtocol&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;tcp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;FromPort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;ToPort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;CidrIp&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0.0.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Role&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;PermissionsBoundary&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${AWS::AccountId}:policy/landingzone-workload-permissions-boundary&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;AssumeRolePolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;2012-10-17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sts:AssumeRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;Service&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ec2.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;ManagedPolicyArns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::aws:policy/AmazonInspector2ManagedCispolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;InstanceProfile&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::InstanceProfile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Roles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;LogGroup&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Logs::LogGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;LogGroupName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${AWS::StackName}-Instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;RetentionInDays&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!int&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;365&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;InstanceSecurityGroup&lt;/strong&gt;, allows an outbound connection on 443. The CloudWatch Agent needs to stream the logs to the CloudWatch endpoint.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role&lt;/strong&gt;, holds the policies that allow the instance to send the logs to the LogGroup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;InstanceProfile&lt;/strong&gt;, the profile used to attach the role to the EC2 instances.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LogGroup&lt;/strong&gt;, the LogGroup used to store the logs in.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;setting-up-the-ec2-instance&#34;&gt;Setting up the EC2 Instance&lt;/h2&gt;
&lt;p&gt;In my &lt;a href=&#34;https://conijn.io/blog/2024/03/compliant-infrastructure-using-infrastructure-as-code/&#34;&gt;previous blog&lt;/a&gt; I wrote about how you can create an EC2 instance using Infrastructure as Code. We will continue on that example. Instead of a single instance we will create an autoscaling group.&lt;/p&gt;
&lt;p&gt;First we will need a LaunchTemplate. This LaunchTemplate will contain some metadata called &lt;code&gt;AWS::CloudFormation::Init&lt;/code&gt;. The metadata has so called configSets and blocks of config. Here is an example that we will use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;LaunchTemplate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::EC2::LaunchTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;AWS::CloudFormation::Init&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;configSets&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;default&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           - &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormationInit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           - &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudWatchLogs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;CloudFormationInit&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;files&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;/etc/cfn/cfn-hup.conf&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;group&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;mode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;000400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;content&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub |-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               [&lt;span style=&#34;color:#ae81ff&#34;&gt;main]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#ae81ff&#34;&gt;stack=${AWS::StackId}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#ae81ff&#34;&gt;region=${AWS::Region}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;/etc/cfn/hooks.d/cfn-auto-reloader.conf&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;group&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;mode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;000400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;content&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub |-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               [&lt;span style=&#34;color:#ae81ff&#34;&gt;cfn-auto-reloader-hook]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#ae81ff&#34;&gt;triggers=post.update&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#ae81ff&#34;&gt;path=Resources.LaunchTemplate.Metadata.AWS::CloudFormation::Init&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#ae81ff&#34;&gt;action=https://conijn.io/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --region ${AWS::Region} --resource LaunchTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#ae81ff&#34;&gt;runas=root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;sysvinit&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;cfn-hup&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;enabled&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;ensureRunning&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;files&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 - &lt;span style=&#34;color:#ae81ff&#34;&gt;/etc/cfn/cfn-hup.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 - &lt;span style=&#34;color:#ae81ff&#34;&gt;/etc/cfn/hooks.d/cfn-auto-reloader.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;CloudWatchLogs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;packages&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;yum&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;amazon-cloudwatch-agent&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;files&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.json&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;group&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;mode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;000400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;content&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub |-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;agent&amp;#34;: &lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;region&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${AWS::Region}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;logfile&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;debug&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;logs&amp;#34;: &lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;logs_collected&amp;#34;: &lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;files&amp;#34;: &lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;collect_list&amp;#34;: &lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;file_path&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/var/log/user-data.log&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_group_name&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${LogGroup}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_stream_name&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{instance_id}/user-data.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;file_path&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/var/log/cfn-hup.log&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_group_name&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${LogGroup}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_stream_name&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{instance_id}/cfn-hup.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;file_path&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_group_name&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${LogGroup}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_stream_name&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{instance_id}/amazon-cloudwatch-agent.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_stream_name&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;default_log_stream&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;metrics&amp;#34;: &lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;append_dimensions&amp;#34;: &lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AutoScalingGroupName&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${!aws:AutoScalingGroupName}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;metrics_collected&amp;#34;: &lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;disk&amp;#34;: &lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;measurement&amp;#34;: &lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;used_percent&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;metrics_collection_interval&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;resources&amp;#34;: &lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;sysvinit&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;amazon-cloudwatch-agent&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;enabled&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;ensureRunning&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;files&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 - &lt;span style=&#34;color:#ae81ff&#34;&gt;/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;LaunchTemplateData&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;BlockDeviceMappings&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         - &lt;span style=&#34;color:#f92672&#34;&gt;DeviceName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/dev/xvda&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Ebs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;DeleteOnTermination&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!bool&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;Encrypted&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!bool&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;KmsKeyId&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt EncryptionKey.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;VolumeSize&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!int&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;VolumeType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gp2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;DisableApiTermination&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!bool&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;IamInstanceProfile&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Arn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt InstanceProfile.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;ImageId&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;InstanceType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;t3.micro&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;SecurityGroupIds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref InstanceSecurityGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;MetadataOptions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;HttpTokens&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;required&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;InstanceMetadataTags&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;enabled&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;UserData&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Fn::Base64&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub |-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash -x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#ae81ff&#34;&gt;/opt/aws/bin/cfn-init --stack ${AWS::StackName} --region ${AWS::Region} --resource LaunchTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#ae81ff&#34;&gt;/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --region ${AWS::Region} --resource AutoScalingGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cloudformationinit&#34;&gt;CloudFormationInit&lt;/h2&gt;
&lt;p&gt;The CloudFormationInit holds a configuration. This configuration is used by the cfn-init agent. It comes pre-installed with the AmazonLinux2 and AmazonLinux2023 AMIs. Under the files section you can see that we are defining 2 files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/etc/cfn/cfn-hup.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/cfn/hooks.d/cfn-auto-reloader.conf&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The syntax is quite explanatory. You set an owner, group, mode and the content of the file. The content of the tells the EC2 instance where it can find the metadata. During the initial boot this is given in the user-data. But you might change the metadata overtime. This makes sure that the running instances checks if there is an update available.&lt;/p&gt;
&lt;p&gt;To recap this will make sure that the running EC2 instances stay in sync with the given metadata.&lt;/p&gt;
&lt;h2 id=&#34;cloudwatchlogs&#34;&gt;CloudWatchLogs&lt;/h2&gt;
&lt;p&gt;The Amazon Linux AMIs do not come with the CloudWatch Agent you need to install the agent first. You can do this by providing the &lt;code&gt;amazon-cloudwatch-agent&lt;/code&gt; package, under packages.&lt;/p&gt;
&lt;p&gt;Now it’s time to configure the agent to stream the log files to CloudWatch LogGroups. We have defined 3 files that will be watched by the agent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/log/user-data.log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log/cfn-hup.log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can extend this list to your own needs. In this example each EC2 instance will have 3 streams. 1 per file and the instance id is used as a prefix, example: &lt;code&gt;i-0000000000/user-data.log&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also do measurements on the instance itself and expose this as a CloudWatch Metric. In this example we are capturing a percentage of the used space of the root volume.&lt;/p&gt;
&lt;p&gt;We have now configured the agent. But we also need to make sure it’s running. When changes are made to the configuration file, we also need to restart it. We do this by pointing to the config file. Now cfn-init knows when it changes the configuration that it also needs to restart the agent.&lt;/p&gt;
&lt;h2 id=&#34;user-data&#34;&gt;User Data&lt;/h2&gt;
&lt;p&gt;The user data will make sure that the configuration is applied. Since we configure the cfn-init to check the configuration it will stay in sync from here on. The second thing it will do is it will send a signal to the AutoScalingGroup resource. If the cfn-init is executed successfully it will signal a success and if not it will signal a failure.&lt;/p&gt;
&lt;p&gt;This means that if the configuration fails the autoscaling group will receive a failure. As a result it will rollback the stack.&lt;/p&gt;
&lt;h2 id=&#34;autoscalinggroup&#34;&gt;AutoScalingGroup&lt;/h2&gt;
&lt;p&gt;The autoscaling group looks as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;AutoScalingGroup&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::AutoScaling::AutoScalingGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;CreationPolicy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ResourceSignal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Timeout&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;PT5M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;UpdatePolicy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;AutoScalingRollingUpdate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;MinInstancesInService&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;MaxBatchSize&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;PauseTime&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;PT5M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;WaitOnResourceSignals&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!bool&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;LaunchTemplate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;LaunchTemplateId&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt LaunchTemplate.LaunchTemplateId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt LaunchTemplate.LatestVersionNumber&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;MinSize&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!int&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;MaxSize&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!int&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;DesiredCapacity&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;!!int&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;VPCZoneIdentifier&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{resolve:ssm:/landingzone/vpc/private-subnet-1-id}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{resolve:ssm:/landingzone/vpc/private-subnet-2-id}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We will have 2 policies defined, one for creation and one for updates. If the signal configured in the user-data fails the resource will fail and trigger a rollback. If the instance is configured successfully it will continue. In the auto scaling group you can define the amount of instances required. In this example the maximum is 2 and the minimum 1. This makes sure that at least one EC2 instance is running. When a replacement is needed a new instance will be added first and if it is deployed successfully the old one is removed. This is why the max is set to 2 so that we can replace the old one gracefully.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Configuring a LogGroup to stream your log files is not hard. Using CloudFormation Init you can install and configure applications. In our example we configured the CloudWatch agent that will stream the logs.&lt;/p&gt;
&lt;p&gt;When you have to deal with autoscaling events and/or malicious attackers.  You can prevent the loss of logs by centralising your application log files.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/brown-logs-on-grass-field-358532/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Compliant infrastructure using infrastructure as code</title>
      <link>https://conijn.io/blog/2024/03/compliant-infrastructure-using-infrastructure-as-code/</link>
      <pubDate>Mon, 18 Mar 2024 10:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2024/03/compliant-infrastructure-using-infrastructure-as-code/</guid>
      <description>&lt;p&gt;When you are using compute you have a lot of options. One of these options is &lt;a href=&#34;https://aws.amazon.com/ec2/&#34;&gt;Amazon EC2&lt;/a&gt;. In a world where more and more workloads become serverless. You might still have this use-case that is better off on EC2. But, how do you combine EC2 with compliance and security? In this blog post we will explore how we can build a compliant and secure EC2 stack.&lt;/p&gt;
&lt;h2 id=&#34;compliance-in-aws&#34;&gt;Compliance in AWS&lt;/h2&gt;
&lt;p&gt;When we talk about compliance we are actually saying AWS Config. AWS Config is the service that enables you to confirm if the infrastructure is compliant. I wrote a blog in the past on how you can write &lt;a href=&#34;https://conijn.io/blog/2024/02/use-custom-rules-to-validate-your-compliance/&#34;&gt;your own rules&lt;/a&gt;. But out of the box AWS has the AWS Foundational Security Best Practices v1.0.0. This standard comes with a few config rules for EC2:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/config/latest/developerguide/ec2-instance-multiple-eni-check.html&#34;&gt;ec2-instance-multiple-eni-check&lt;/a&gt;, this control checks if Amazon EC2 instance uses multiple ENI/EFA. This control will pass if single network adapters is used.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/config/latest/developerguide/ec2-paravirtual-instance-check.html&#34;&gt;ec2-paravirtual-instance-check&lt;/a&gt;, this control checks if the EC2 virtualization type is paravirtual. The control fails for an EC2 instance if &lt;code&gt;virtualizationType&lt;/code&gt; is set to &lt;code&gt;paravirtual&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/config/latest/developerguide/ec2-instance-no-public-ip.html&#34;&gt;ec2-instance-no-public-ip&lt;/a&gt;, this control checks whether EC2 instances have a public IP address. The control fails if the publicIp field is present in the instance configuration. This control applies to IPv4 addresses only.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/config/latest/developerguide/ec2-imdsv2-check.html&#34;&gt;ec2-imdsv2-check&lt;/a&gt;,  this control checks if your EC2 instances have IMDSv2 configured. The control passes if HttpTokens is set to required for IMDSv2. The control fails if HttpTokens is set to optional.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/config/latest/developerguide/ec2-instance-managed-by-systems-manager.html&#34;&gt;ec2-instance-managed-by-ssm&lt;/a&gt;, this control checks if the EC2 instances are managed by SSM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first 3 are easy, with a proper VPC design and using sane defaults you will be compliant. But for the last 2 you need to perform some extra actions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/Config.png&#34; alt=&#34;Example of AWS Config rules&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;security-in-aws&#34;&gt;Security in AWS&lt;/h2&gt;
&lt;p&gt;Security is a broad topic and can include a lot of different things. In this blog I will only focus on the EC2 instance itself. This means that patch management and hardening is in scope. The service that AWS has for this is called AWS Inspector. This tool will scan the EC2 instance for known vulnerabilities. Inspector will report the findings through the AWS Console. It also integrates with Security Hub. Here you can manage all your security related findings from a single place.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/InspectorFindings.png&#34; alt=&#34;Example of AWS Inspector findings&#34;&gt;&lt;/p&gt;
&lt;p&gt;Inspector can also scan your instance against the CIS hardening baseline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/CISScan.png&#34; alt=&#34;Example of a CIS Scan&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;why-should-i-use-infrastructure-as-code&#34;&gt;Why should I use infrastructure as code?&lt;/h2&gt;
&lt;p&gt;When you use IaC (infrastructure as code), you create repeatable and predictable infrastructure. Now why is this important? Assume you create an EC2 instance, and you will use the console to launch it. Now AWS Config will start reporting that IMDSv2 is not enforced. And that the instance is not managed by SSM (AWS Systems Manager). This is because you forgot all those extra steps. Humans are not designed to think about all the options that we need to include. IaC is designed to do this for you, you define a template. You deploy the template and the end result is what is defined in the template.&lt;/p&gt;
&lt;p&gt;If you design your templates to be compliant and secure. The infrastructure that you deploy will be compliant and secure.&lt;/p&gt;
&lt;h2 id=&#34;lets-build-a-compliant-and-secure-template&#34;&gt;Let’s build a compliant and secure template!&lt;/h2&gt;
&lt;p&gt;Now my IaC technology of choice is AWS CloudFormation. So the examples that I will present are built in CloudFormation. But, this can also be achieved in any other IaC technology that you want to use.&lt;/p&gt;
&lt;h3 id=&#34;setting-up-an-instance-profile&#34;&gt;Setting up an instance profile&lt;/h3&gt;
&lt;p&gt;We want the EC2 instance to be managed by SSM. For this we need to create an IAM Role and an Instance Profile. We will attach 2 managed policies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AmazonSSMManagedInstanceCore&lt;/strong&gt;, enables the instance to be managed by SSM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AmazonInspector2ManagedCispolicy&lt;/strong&gt;, enables the ability to perform the CIS baseline scan.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Role&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;AssumeRolePolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;2012-10-17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sts:AssumeRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;Service&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ec2.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;ManagedPolicyArns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::aws:policy/AmazonInspector2ManagedCispolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;InstanceProfile&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::InstanceProfile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Roles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;setting-up-the-ec2-instance&#34;&gt;Setting up the EC2 Instance&lt;/h3&gt;
&lt;p&gt;When you launch an EC2 instance we will be needing a security group. We also want our EC2 instance to be managed by SSM. To SSM Agent comes pre-installed in Amazon Linux 2. Needs to be able to connect outbound via HTTPS, for this reason we add 0.0.0.0/0 on port 443 on the egress traffic.&lt;/p&gt;
&lt;p&gt;We also want to make IMDSv2 required. We can configure this in the launch template &lt;em&gt;MetadataOptions&lt;/em&gt;. To ensure that all security updates are installed during the first boot. We include the &lt;code&gt;yum update -y --security&lt;/code&gt; option in the user-data.&lt;/p&gt;
&lt;p&gt;You can now use the launch template to create an EC2 instance. Or, use an auto scaling group to launch one or more EC2 instances.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;InstanceSecurityGroup&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::EC2::SecurityGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;GroupDescription&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Security group for the test instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;VpcId&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{resolve:ssm:/landingzone/vpc/vpc-id}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;SecurityGroupEgress&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#f92672&#34;&gt;Description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow outbound connectivity to port 443.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;IpProtocol&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;tcp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;FromPort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;ToPort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;CidrIp&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0.0.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;LaunchTemplate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::EC2::LaunchTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;LaunchTemplateData&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;IamInstanceProfile&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Arn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt InstanceProfile.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;ImageId&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;InstanceType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;t3.micro&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;SecurityGroupIds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref InstanceSecurityGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;MetadataOptions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;HttpTokens&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;required&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;UserData&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Fn::Base64&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub |-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash -x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#ae81ff&#34;&gt;yum update -y --security&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Instance&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::EC2::Instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;LaunchTemplate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;LaunchTemplateId&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt LaunchTemplate.LaunchTemplateId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt LaunchTemplate.LatestVersionNumber&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;SubnetId&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{resolve:ssm:/landingzone/vpc/private-subnet-1-id}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;spinning-up-the-instance&#34;&gt;Spinning up the instance&lt;/h2&gt;
&lt;p&gt;If you combine the two snippets you can spin-up a compliant EC2 instance. This instance has all the latest security patches installed. But when you look into Inspector you will notice that there are still some findings reported.&lt;/p&gt;
&lt;p&gt;The findings that popped up during the time of writing were kernel related. So for me to fix this I need to update the kernel. When you lookup your EC2 instance in the console you will notice the &lt;strong&gt;Connect&lt;/strong&gt; button. This will create a shell session to your instance:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/Connect.png&#34; alt=&#34;Example of how to connect to a EC2 instance&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;updating-the-kernel&#34;&gt;Updating the kernel&lt;/h3&gt;
&lt;p&gt;First, let’s see what kernel we have running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;s​​h-4.2$ uname -r
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4.14.336-253.554.amzn2.x86_64
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sh-4.2$
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let’s see what kernels are available:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sh-4.2$ amazon-linux-extras | grep kernel
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;49&lt;/span&gt;  kernel-5.4               available    &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stable &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;55&lt;/span&gt;  kernel-5.10              available    &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stable &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;62&lt;/span&gt;  kernel-5.15              available    &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stable &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sh-4.2$
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We will install the latest version of the kernel 5.15 with the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sh-4.2$ sudo amazon-linux-extras install kernel-5.15
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because a kernel update requires a reboot we need to reboot the instance:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo reboot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All done!&lt;/p&gt;
&lt;h3 id=&#34;avoiding-the-kernel-update&#34;&gt;Avoiding the kernel update&lt;/h3&gt;
&lt;p&gt;The problem with the kernel update is the required reboot. You only have two options to remove this step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create your own up to date base AMI.&lt;/li&gt;
&lt;li&gt;Use the Amazon Linux 2023 AMI with a newer kernel version.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Maintaining a base AMI comes with a lot of extra overhead. So my recommendation is to always use the newer version that uses the newer kernel versions.&lt;/p&gt;
&lt;p&gt;But when you need to harden your images using the CIS benchmarks. You might need to start maintaining your own images anyway. There are &lt;a href=&#34;https://aws.amazon.com/marketplace/seller-profile?id=dfa1e6a8-0b7b-4d35-a59c-ce272caee4fc&#34;&gt;CIS Hardened images&lt;/a&gt; available through the marketplace for an extra charge.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;IaC (Infrastructure as Code) can help you spin up compliant and secure infrastructure. It removes the human aspect of forgetting to configure certain options. Using IaC you will have a predictable outcome every time you spin up the same template. This makes it reliable and is less time consuming. As a bonus you are not triggering any config rules to be marked as non compliant.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/man-in-grey-sweater-holding-yellow-sticky-note-879109/&#34;&gt;Hitesh Choudhary&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Tracking your security posture in AWS</title>
      <link>https://conijn.io/blog/2024/03/tracking-your-security-posture-in-aws/</link>
      <pubDate>Fri, 01 Mar 2024 09:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2024/03/tracking-your-security-posture-in-aws/</guid>
      <description>&lt;p&gt;How do you track your security posture in AWS? You have services like Security Hub, but it will only show you the scores of a limited set of standards. This blog post will continue on the previous 2 blog post that I wrote:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://conijn.io/blog/2024/02/use-custom-rules-to-validate-your-compliance/&#34;&gt;Use custom rules to validate your compliance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://conijn.io/blog/2024/02/deploy-config-rules-across-your-organization/&#34;&gt;Deploy config rules across your organization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this blog we will look at how you can track your security posture in your organization.&lt;/p&gt;
&lt;h2 id=&#34;aws-security-hub&#34;&gt;AWS Security Hub&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://aws.amazon.com/security-hub/&#34;&gt;AWS Security Hub&lt;/a&gt; is the service for your cloud security posture management. And this works for smaller organizations and when you don’t have your own config rules. Security Hub will only show you the compliance scores of standards that AWS provides. Plus, it will not give you historical insight of the scores. This does not need to be a problem. Having historical insight can help a lot when you interact with the workload teams.&lt;/p&gt;
&lt;p&gt;For example, a workload team could say: “Yesterday the score was fine, it&amp;rsquo;s only lower because of &lt;code&gt;&amp;lt;insert reason here&amp;gt;&lt;/code&gt;”. Having the historical insight will actually tell you if this is true or not.&lt;/p&gt;
&lt;h2 id=&#34;how-about-conformance-packs&#34;&gt;How about conformance packs?&lt;/h2&gt;
&lt;p&gt;In my &lt;a href=&#34;https://conijn.io/blog/2024/02/deploy-config-rules-across-your-organization/&#34;&gt;previous blog&lt;/a&gt; I also wrote that conformance packs give you this insight. Yes this is true but there is a subtle difference between the scores in AWS Config and AWS Security Hub. In config the state is black and white, a resource is either compliant or non compliant. The calculated score is the percentage of compliant resources.&lt;/p&gt;
&lt;p&gt;In AWS Security Hub when 1 resource is non compliant in the control, the control is non compliant. When 9 controls are compliant and you have one that is not. The score would be 90%, regardless of the amount of resources.&lt;/p&gt;
&lt;p&gt;Security hub also allows you the ability to suppress the non compliant findings. This is useful where you have a resource that has a good reason to not adhere to a control. You can suppress the finding on the resource. These suppressed findings are excluded when the compliance score is being defined.&lt;/p&gt;
&lt;h2 id=&#34;how-do-you-calculate-the-score-then&#34;&gt;How do you calculate the score then?&lt;/h2&gt;
&lt;p&gt;If you have set up a &lt;a href=&#34;https://conijn.io/blog/2023/12/setting-up-my-own-landing-zone-on-aws/&#34;&gt;landingzone&lt;/a&gt; you have an audit account. This account collects all config rule results. But also all security hub findings into a single account.&lt;/p&gt;
&lt;p&gt;By querying the security hub for findings we can calculate the compliance score. You can create handy dashboards. These dashboards can display all scores of a workload across environments.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/dashboard_example.png&#34; alt=&#34;Screenshot of the CloudWatch Dashboard of your security posture&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This requires an AWS Account per environment and you need a naming schema. For example: &lt;code&gt;xebia-my-workload-development&lt;/code&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;When you expose these dashboards to your teams. You will increase the visibility into the compliance scores. Visualized in a single place and easy to access. Giving the product owners the insights they need to plan the backlog. This will increase the adoption of built-in security responsibilities.&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;For the full implementation details I refer to the &lt;a href=&#34;https://github.com/conijnio/aws-security-posture&#34;&gt;github repository&lt;/a&gt;. But in essence we will perform the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the filter to retrieve all findings, we are fetching 100 findings per invocation.&lt;/li&gt;
&lt;li&gt;When there is a NextToken we need to collect the rest of the findings.&lt;/li&gt;
&lt;li&gt;When we have 100 files, containing each 100 findings. We will add them into a single file containing 10K findings. We are repeating this until we have all the findings.&lt;/li&gt;
&lt;li&gt;Split the findings per AWS Account ID.&lt;/li&gt;
&lt;li&gt;In parallel, calculate the score based on the findings.&lt;/li&gt;
&lt;li&gt;Publish the results to CloudWatch metrics. The metric will have the workload name and environment as dimensions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are 3 supported flavors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The security standards from AWS Security Hub.&lt;/li&gt;
&lt;li&gt;Conformance packs deployed in the organization.&lt;/li&gt;
&lt;li&gt;List of custom AWS Config rules.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the conformance packs there is one more step. We need to get the number of rules available within the conformance pack. The reason for this is that controls that are compliant are not visible in Security Hub. This would lead to an incorrect calculation of the security score.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you need one of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Historical insight in yous security posture&lt;/li&gt;
&lt;li&gt;Compliance scores of your conformance pack.&lt;/li&gt;
&lt;li&gt;Dashboards with the scores  of all environments of a workload&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You need to do some manual labor as it is not supported by AWS. I hope I peaked your interest to check out the &lt;a href=&#34;https://github.com/conijnio/aws-security-posture&#34;&gt;aws-security-posture&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/person-holding-black-smartphone-3912952/&#34;&gt;ThisIsEngineering&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Deploy AWS Config rules across your organization</title>
      <link>https://conijn.io/blog/2024/02/deploy-config-rules-across-your-organization/</link>
      <pubDate>Thu, 15 Feb 2024 11:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2024/02/deploy-config-rules-across-your-organization/</guid>
      <description>&lt;p&gt;In my previous &lt;a href=&#34;https://conijn.io/blog/2024/02/use-custom-rules-to-validate-your-compliance/&#34;&gt;blog&lt;/a&gt; I showed you how you can write your own config rules. But it will only bring you value if you deploy the rule in your AWS Accounts. In this blog we will dive into the distribution of these config rules.&lt;/p&gt;
&lt;h2 id=&#34;what-are-my-options&#34;&gt;What are my options?&lt;/h2&gt;
&lt;p&gt;There are many ways you can deploy these rules in your member accounts. In this blog I will only focus on the 2 real options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy the rules using CloudFormation StackSets in each account.&lt;/li&gt;
&lt;li&gt;Use conformance packs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;using-cloudformation-stacksets&#34;&gt;Using CloudFormation StackSets&lt;/h2&gt;
&lt;p&gt;When you look at what CloudFormation StackSets does, you will see the reason why this would be a good fit:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/stack_set_conceptual_sv.png&#34; alt=&#34;Example how CloudFormation StackSets work&#34;&gt;&lt;/p&gt;
&lt;p&gt;You need to have a delegated administrator. This admin has the ability to deploy the template in other accounts. You define all the account ids and regions you want to deploy the config rules in.&lt;/p&gt;
&lt;p&gt;If you combine this with a &lt;a href=&#34;https://conijn.io/blog/2023/12/setting-up-my-own-landing-zone-on-aws/&#34;&gt;landingzone&lt;/a&gt; you most likely already have this in place.&lt;/p&gt;
&lt;h2 id=&#34;using-conformance-packs&#34;&gt;Using Conformance Packs&lt;/h2&gt;
&lt;p&gt;A conformance pack is nothing more than a bunch of config rules grouped together. But having this bundle gives you some more benefits. For example, you can see the compliance score of your bundle in your account. You can compare it to the compliance scores that you can see in Security Hub. Assume you have 10 config rules. Now one has resources that are non-compliant you will have a compliance score of 90%.&lt;/p&gt;
&lt;p&gt;But this alone would not address the need to distribute the rules across all your accounts. But the conformance packs come in 2 flavors, regular and organizational conformance packs. Regular rules deploy the conformance pack in a single account. The organizational conformance pack will deploy the rule in all accounts.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note, You can deploy the regular config rules using an organization config rule. This would not give you the grouping of all your rules. For this reason I tend to use conformance packs.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;show-me-an-example&#34;&gt;Show me an example&lt;/h2&gt;
&lt;p&gt;It’s always better to use an example, this snippet will only work when you follow the &lt;a href=&#34;https://docs.aws.amazon.com/config/latest/developerguide/cpack-prerequisites.html&#34;&gt;prerequisites&lt;/a&gt;. When you deploy this template in the account that is setup as the delegated administrator. All member accounts will have the config rules available.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;ConformancePackBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;DeletionPolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Retain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;UpdateReplacePolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Retain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;BucketName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub awsconfigconforms-${AWS::AccountId}-${AWS::Region}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionConfiguration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         - &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionByDefault&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;SSEAlgorithm&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AES256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;PublicAccessBlockConfiguration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;BlockPublicAcls&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;BlockPublicPolicy&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;IgnorePublicAcls&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;RestrictPublicBuckets&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;ConformancePackBucketPolicy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::BucketPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Bucket&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref ConformancePackBucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;2012-10-17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         - &lt;span style=&#34;color:#f92672&#34;&gt;Sid&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWSConfigConformsCheckAcls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:GetBucketAcl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt ConformancePackBucket.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;AWS&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/config-conforms.amazonaws.com/AWSServiceRoleForConfigConforms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Condition&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;StringEquals&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;aws:PrincipalOrgID&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${aws:PrincipalOrgID}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;Bool&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;aws:SecureTransport&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         - &lt;span style=&#34;color:#f92672&#34;&gt;Sid&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWSConfigConformsReadWriteBucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:PutObject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:GetObject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ConformancePackBucket.Arn}/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;AWS&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/config-conforms.amazonaws.com/AWSServiceRoleForConfigConforms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Condition&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;StringEquals&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;aws:PrincipalOrgID&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${aws:PrincipalOrgID}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;Bool&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;aws:SecureTransport&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;OrganizationConformancePack&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Config::OrganizationConformancePack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;OrganizationConformancePackName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;lz-framework&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;DeliveryS3Bucket&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref ConformancePackBucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;ExcludedAccounts&lt;/span&gt;: [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;000000000000&amp;#34;&lt;/span&gt; ] &lt;span style=&#34;color:#75715e&#34;&gt;# AccountID to exclude&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;TemplateBody&lt;/span&gt;: |-&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;       Resources:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;         S3AccessLogging:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           Type: AWS::Config::ConfigRule
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           Properties:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;             ConfigRuleName: lz-my-custom-rule
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;             Description: My custom rule
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;             EvaluationModes:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               - Mode: DETECTIVE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;             Scope:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               ComplianceResourceTypes:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                 - AWS::S3::Bucket
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;             Source:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               Owner: CUSTOM_POLICY
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               SourceDetails:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                 - EventSource: aws.config
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                   MessageType: ConfigurationItemChangeNotification
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                 - EventSource: aws.config
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                   MessageType: OversizedConfigurationItemChangeNotification
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               CustomPolicyDetails:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                 EnableDebugLogDelivery: &amp;#39;true&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                 PolicyRuntime: guard-2.x.x
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                 PolicyText: |-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                   # The rule definition goes here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The template only includes 3 main resources, but 2 actual logical components. A bucket with a policy and the conformance pack definition.  Let&amp;rsquo;s go over all the components in the example.&lt;/p&gt;
&lt;h2 id=&#34;s3-bucket-and-the-bucket-policy&#34;&gt;S3 Bucket and the bucket policy&lt;/h2&gt;
&lt;p&gt;This bucket needs to start with &lt;code&gt;awsconfigconforms&lt;/code&gt;. The &lt;code&gt;config-conforms.amazonaws.com&lt;/code&gt; service will use this bucket to upload the template. The template comes from the &lt;code&gt;OrganizationConformancePack&lt;/code&gt; resource. The policy allows the service to put and get objects as long as it is within the organization. This allows the service to deploy the template in all member accounts.&lt;/p&gt;
&lt;h2 id=&#34;organization-conformance-pack&#34;&gt;Organization Conformance Pack&lt;/h2&gt;
&lt;p&gt;The organization conformance pack definition looks big but it’s quite simple. If you have a closer look you will notice it has only 4 properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OrganizationConformancePackName&lt;/li&gt;
&lt;li&gt;DeliveryS3Bucket&lt;/li&gt;
&lt;li&gt;ExcludedAccounts&lt;/li&gt;
&lt;li&gt;TemplateBody&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now the first 3 options are pretty straight forward. The template itself is a bit more complicated. In my example I used an inline template, I did this for the sake of this blog. But you can also reference an existing object on S3. This way you can use linting tools like &lt;a href=&#34;https://github.com/aws-cloudformation/cfn-lint&#34;&gt;cfn-lint&lt;/a&gt; on your conformance pack. This will reduce errors during deployment as you can catch them before you commit and push your code.&lt;/p&gt;
&lt;p&gt;If you need help with setting up the conformance pack template, have a look at &lt;a href=&#34;https://docs.aws.amazon.com/config/latest/developerguide/conformancepack-sample-templates.html&#34;&gt;these samples&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you combine custom rules in a conformance pack. It will allow you to simplify the distribution of these rules. You don’t need to maintain any pipelines or stack sets. And you outsource the distribution of your rules to AWS.&lt;/p&gt;
&lt;p&gt;This removes all the maintenance on the infrastructure to do this distribution yourself. And you don’t need to think about scenarios like, adding/removing accounts.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/person-using-forklift-1267338/&#34;&gt;ELEVATE&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use custom rules to validate your compliance</title>
      <link>https://conijn.io/blog/2024/02/use-custom-rules-to-validate-your-compliance/</link>
      <pubDate>Sat, 03 Feb 2024 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2024/02/use-custom-rules-to-validate-your-compliance/</guid>
      <description>&lt;p&gt;AWS has a lot of controls built in, but what if you need more? &lt;a href=&#34;https://aws.amazon.com/config/&#34;&gt;AWS Config&lt;/a&gt; allows you to create your own rules. These rules can then inspect your resources and determine if they are compliant. This is useful when you want to enforce certain configuration settings. Giving you an overview of how compliant your workloads are.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s use a specific example, but you can apply this concept to other scenarios.&lt;/p&gt;
&lt;h2 id=&#34;check-s3-access-logs-configuration&#34;&gt;Check S3 access logs configuration&lt;/h2&gt;
&lt;p&gt;When an engineer creates an Amazon S3 bucket, they need to enable access logging on the bucket. AWS has a built-in config rule for this called &lt;code&gt;s3-bucket-logging-enabled&lt;/code&gt;. When you enable the &lt;strong&gt;AWS Foundational Security Best Practices v1.0.0&lt;/strong&gt; standard in Security Hub it becomes available.&lt;/p&gt;
&lt;p&gt;When we create a new account, part of the bootstrapping is the deployment of a bucket. The engineers who use the account only have read rights on this bucket. When an engineer creates a bucket, we expect him to use this bucket for the access logs.&lt;/p&gt;
&lt;h2 id=&#34;creating-rules-lambda-vs-cloudformation-guard&#34;&gt;Creating rules: Lambda vs CloudFormation Guard&lt;/h2&gt;
&lt;p&gt;You can build a custom config rules in 2 ways, using &lt;a href=&#34;https://aws.amazon.com/lambda/&#34;&gt;AWS Lambda&lt;/a&gt; and &lt;a href=&#34;https://docs.aws.amazon.com/cfn-guard/latest/ug/what-is-guard.html&#34;&gt;CloudFormation Guard&lt;/a&gt;. Lambda gives you a lot of flexibility, but it also brings complexity of maintaining. CloudFormation Guard is a bit more lightweight in that regard. Yes, you still need to maintain the logic to determine when your resource is compliant or not. But you need to do this in both cases, thus my go to preference is CloudFormation Guard.&lt;/p&gt;
&lt;h2 id=&#34;config-rule&#34;&gt;Config Rule&lt;/h2&gt;
&lt;p&gt;When you start to define your custom config rule you need to think about the scenarios that happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the resource is not an S3 bucket, we should skip the rule.&lt;/li&gt;
&lt;li&gt;When the resource is the actual logging bucket, we should skip the rule.&lt;/li&gt;
&lt;li&gt;When the resource does not have logging configured, we should fail the rule.&lt;/li&gt;
&lt;li&gt;When the resource has logging configured on a different bucket then the one we want. We should fail the rule.&lt;/li&gt;
&lt;li&gt;When the resource has logging configured with the expected bucket, we should pass the rule.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With AWS Config you can define &lt;strong&gt;InputParameters&lt;/strong&gt;. We will use this to pass in the name of the bucket that will store the access logs. We named the parameter &lt;code&gt;loggingBucket&lt;/code&gt;. You can reference that value of this parameter using: &lt;code&gt;CONFIG_RULE_PARAMETERS.loggingBucket&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;deployment&#34;&gt;Deployment&lt;/h2&gt;
&lt;p&gt;I use CloudFormation for the deployment of the rule here is a snippet of the template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;S3AccessLogging&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Config::ConfigRule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ConfigRuleName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;lz-s3-access-logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Validate that access logging has been enabled and that the correct logging bucket is used.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;EvaluationModes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;Mode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;DETECTIVE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;InputParameters&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Fn::Sub&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{&amp;#34;loggingBucket&amp;#34;: &amp;#34;s3-access-logs-${AWS::AccountId}-${AWS::Region}&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Scope&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ComplianceResourceTypes&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::S3::Bucket&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Source&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CUSTOM_POLICY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;SourceDetails&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;EventSource&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;aws.config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;MessageType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ConfigurationItemChangeNotification&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;EventSource&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;aws.config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;MessageType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;OversizedConfigurationItemChangeNotification&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;CustomPolicyDetails&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;EnableDebugLogDelivery&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;true&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;PolicyRuntime&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;guard-2.x.x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;PolicyText&lt;/span&gt;: |-&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            rule s3_logging_configuration when resourceType == &amp;#34;AWS::S3::Bucket&amp;#34; resourceName != CONFIG_RULE_PARAMETERS.loggingBucket {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                supplementaryConfiguration.BucketLoggingConfiguration exists
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                &amp;lt;&amp;lt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                Violation: S3 Bucket needs to have access logging configured
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                Fix: Configure the destinationBucketName on your S3 bucket
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                &amp;gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            rule s3_logging_correct_bucket when s3_logging_configuration {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                supplementaryConfiguration.BucketLoggingConfiguration {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    destinationBucketName == CONFIG_RULE_PARAMETERS.loggingBucket
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    &amp;lt;&amp;lt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    Violation: S3 Bucket needs to have access logging configured
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    Fix: Configure the destinationBucketName on your S3 bucket
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    &amp;gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s walk over this policy and explain what is happening. The first rule only applies when the resource is a S3 bucket and the name is not equal to the name of the logging bucket. The name of the &lt;code&gt;loggingBucket&lt;/code&gt; is configurable via the InputParameters.
Next, we will check if the bucket has used the logging bucket for the access logs configured. When the logging configuration exists the &lt;code&gt;destinationBucketName&lt;/code&gt; needs to match the given name.&lt;/p&gt;
&lt;p&gt;You can test the scenarios I mentioned earlier using unit tests. Read: &lt;a href=&#34;https://conijn.io/blog/2021/12/this-is-how-you-can-test-your-cfn-guard-rules/&#34;&gt;This is how you can test your cfn-guard rules&lt;/a&gt; for more information on how you could do that.&lt;/p&gt;
&lt;p&gt;After you deployed this rule into your accounts. Config will check all your S3 Buckets if they are compliant with this custom config rule.&lt;/p&gt;
&lt;h2 id=&#34;limitations&#34;&gt;Limitations&lt;/h2&gt;
&lt;p&gt;Currently, AWS Config does only support CloudFormation Guard 2.x.x. I am waiting on the support for 3.x. Since that brings even more powerful capabilities like the json_parse method. This allows you to also check JSON structures like for example the bucket policy.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can create custom config rules using CloudFormation Guard. Using CloudFormation Guard you don’t have to maintain dependencies from your functions. And you don’t need to upgrade your lambda runtime when it gets deprecated.&lt;/p&gt;
&lt;p&gt;Do you want to know how you can distribute your rules? Read my &lt;a href=&#34;https://conijn.io/blog/2024/02/deploy-config-rules-across-your-organization/&#34;&gt;next blog&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/selective-focus-photography-of-male-umpire-209954/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using design patterns in AWS Lambda</title>
      <link>https://conijn.io/blog/2024/01/using-design-patterns-in-aws-lambda/</link>
      <pubDate>Mon, 15 Jan 2024 13:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2024/01/using-design-patterns-in-aws-lambda/</guid>
      <description>&lt;p&gt;When you speak with software developers, they will probably tell you that they use design patterns. But when the world first shifted to the internet the general feeling was that these design patterns would not work for the web. This is not true, and today you see these patterns being used more and more.&lt;/p&gt;
&lt;p&gt;I have noticed the same behavior with serverless. In this blog post I will go over some reasons why you should be using design patterns in your Lambda functions&lt;/p&gt;
&lt;h2 id=&#34;getting-started&#34;&gt;Getting started&lt;/h2&gt;
&lt;p&gt;To get started with AWS Lambda is quite easy, and this is also the reason why some crucial steps are skipped. For example, you can use the console to create a function and type your code in an editor via your browser. Or, you create a CloudFormation template and you put your code in the template itself. Obviously, this is fine for experimentation and learning purposes. But when you start developing systems that need to be reliable you need to take a different approach.&lt;/p&gt;
&lt;p&gt;The biggest objection that I have with the editor in the console is that it does not allow you to run tests. Also using inline code that is part of a CloudFormation template has some downsides. Again you cannot run tests against your inline code and the code becomes vulnerable to indenting and syntax errors due to the lack of proper IDE highlighting.&lt;/p&gt;
&lt;p&gt;Both options do not allow you to add dependencies, or at least not in an easy way. So you default to the installed dependencies, and you have no control over these dependencies.&lt;/p&gt;
&lt;h2 id=&#34;separate-your-infrastructure-and-application-code&#34;&gt;Separate your infrastructure and application code&lt;/h2&gt;
&lt;p&gt;By storing your application code in separate files you will have all the benefits of using an IDE. You can run linters, formatters and tests and you have syntax highlighting while you develop your code. This also allows you to run these steps in the CI/CD pipeline before you actually deploy your code to production. This gives you a quality gate and a decision moment to say yes this can go to production.&lt;/p&gt;
&lt;p&gt;When you combine this with the &lt;a href=&#34;https://aws.amazon.com/serverless/sam/&#34;&gt;AWS Serverless Application Model&lt;/a&gt; you can also very easily include your dependencies. Or use a compiled language like golang for your Lambda functions. You simply run &lt;code&gt;sam build&lt;/code&gt; before you run the &lt;code&gt;aws cloudformation package&lt;/code&gt; and &lt;code&gt;aws cloudformation deploy&lt;/code&gt; commands. SAM will build the binary and update the template to point to the newly built binary. Package will then upload it to S3 and replace the local reference to the S3 location. Deploy can then create or update the stack or you can use the CloudFormation integration in CodePipeline.&lt;/p&gt;
&lt;h2 id=&#34;why-should-i-use-design-patterns&#34;&gt;Why should I use design patterns&lt;/h2&gt;
&lt;p&gt;Testing AWS calls can be challenging, I wrote a blog on this for &lt;a href=&#34;https://conijn.io/blog/2023/08/stubbing-aws-service-calls-in-golang/&#34;&gt;golang&lt;/a&gt;. By splitting the business logic from the infrastructure in your code you gain 2 important things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can focus on the business logic without having to deal with stubbing your infrastructure.&lt;/li&gt;
&lt;li&gt;The used infrastructure can be changed without the need to change your business logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The former makes it easier to add test scenarios. When it’s easy to add scenarios you are more likely to add more scenarios. Increasing the reliability of your business logic. The latter gives you flexibility, you can swap a RDS database with a DynamoDB table relatively easily. Without changing the business logic, this also means that you have less of a vendor lock-in.&lt;/p&gt;
&lt;h2 id=&#34;golang-example&#34;&gt;Golang example&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s use the Observer pattern, first you will need some interfaces:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Observer&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;NotifyCallback&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Subject&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;AddListener&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Observer&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;RemoveListener&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Observer&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;Notify&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;eventObserver&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;eventSubject&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;observers&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Map&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example we define an Event, Subject and an Observer. We can use the event to store all the relevant data. When you pass the Event in the Subject the Event is sent to all the registered observers.&lt;/p&gt;
&lt;p&gt;Next we need some logic to register the observers on the Subject:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;eventSubject&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;AddListener&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Observer&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;observers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Store&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{}{})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;eventSubject&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;RemoveListener&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Observer&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;observers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Delete&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;eventSubject&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Notify&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;observers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Range&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{}, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{}) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;.(&lt;span style=&#34;color:#a6e22e&#34;&gt;Observer&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;NotifyCallback&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see the Notify method will iterate over all observers and pass the event to all registered observers. The observer could be as simple as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;eventObserver&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;NotifyCallback&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Received from observer %d: %s after %v\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Since&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For your tests you can simply implement a testObserver:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;testObserver&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testObserver&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;NotifyCallback&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a very simplified version of the business logic:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doStuff&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Observer&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;eventSubject&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;observers&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;sync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Map&lt;/span&gt;{}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AddListener&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;observer&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Notify&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Event&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Joris Conijn&amp;#34;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We create an Event with &lt;code&gt;Joris Conijn&lt;/code&gt; as the name value. We want to test this business logic that it indeed creates this Event with &lt;code&gt;Joris Conijn&lt;/code&gt; as the name value.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TestObserver&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Run&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Run doStuff&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;obs1&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;testObserver&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;doStuff&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;obs1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;assert&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Equal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;obs1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Joris Conijn&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the actual implementation the observer would store it in DynamoDB or RDS. To have a good testing coverage you do need to test the observer. Per observer you now test the actual implementation of the database of your choice.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can use design patterns in your Lambda functions. It is a great way to separate your business logic from the used infrastructure. And it makes you business logic easier to test. However, you might have noticed that you will need some boilerplate code to implement these patterns.&lt;/p&gt;
&lt;p&gt;My advice would be to use these patterns when you need portability and/or when you have more complex business logic. For very simple Lambda functions it might not be worth it.&lt;/p&gt;
&lt;p&gt;Thanks &lt;a href=&#34;https://www.youtube.com/watch?v=Rb8s0zw1SNM&#34;&gt;Tensor Programming&lt;/a&gt; for the inspiration.
Photo by &lt;a href=&#34;https://www.pexels.com/photo/architectural-photography-of-glass-buliding-1492232/&#34;&gt;Soloman Soh&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setting up my own landing zone on AWS</title>
      <link>https://conijn.io/blog/2023/12/setting-up-my-own-landing-zone-on-aws/</link>
      <pubDate>Mon, 25 Dec 2023 12:50:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2023/12/setting-up-my-own-landing-zone-on-aws/</guid>
      <description>&lt;p&gt;As a consultant I am used to a certain level of quality that I need to deliver to our customers. For this reason I have built a landing zone for my own website, initiatives and experiments. By using the same structure that our customers have, I can test and build my ideas and apply them in customer environments if they are successful.&lt;/p&gt;
&lt;p&gt;At first it might feel like a massive overkill to set up a complete landing zone. But once you have set it up you will be capable of performing experiments very easily.&lt;/p&gt;
&lt;h2 id=&#34;my-landing-zone&#34;&gt;My landing zone&lt;/h2&gt;
&lt;p&gt;For my landing zone I used the &lt;a href=&#34;https://docs.aws.amazon.com/controltower/latest/userguide/cfct-overview.html&#34;&gt;Customizations for AWS Control Tower&lt;/a&gt; (CfCt) project. When I created my landing zone the &lt;a href=&#34;https://aws.amazon.com/solutions/implementations/landing-zone-accelerator-on-aws/&#34;&gt;Landing Zone Accelerator&lt;/a&gt; was not yet available. I still need to evaluate if I can switch, but my initial concerns are the cost of running it. CfCt has low costs and depending on your usage it might even fit in the free tier. The Landing Zone Accelerator comes with VPCs and transit gateways and some other services that will generate costs regardless of what you are doing with the landing zone.&lt;/p&gt;
&lt;p&gt;A default landing zone typically has the following accounts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Management&lt;/strong&gt; - Used for billing for all accounts in an organization, to create new accounts, and to manage access to all accounts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit&lt;/strong&gt; - A restricted account for your security and compliance teams to gain read and write access to all accounts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs&lt;/strong&gt; - Used as a repository of logs of API activities and resource configurations from all accounts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next to the default accounts I have some additional accounts setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Build&lt;/strong&gt; - Used for all &lt;a href=&#34;https://aws.amazon.com/codecommit/&#34;&gt;CodeCommit&lt;/a&gt; repositories and &lt;a href=&#34;https://aws.amazon.com/codepipeline/&#34;&gt;CodePipelines&lt;/a&gt; that are deployed within the landing zone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Domains&lt;/strong&gt; - Used to host the domain names that I have registered. If you are interested in why I use a separate account for this you can read my &lt;a href=&#34;https://conijn.io/blog/2023/03/cross-account-dns-management-on-aws/&#34;&gt;Cross account DNS management&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Catalog&lt;/strong&gt; - Used to host all my &lt;a href=&#34;https://aws.amazon.com/servicecatalog/&#34;&gt;AWS Service Catalog&lt;/a&gt; products used within my landing zone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team&lt;/strong&gt; - Used to perform &lt;a href=&#34;https://aws-samples.github.io/iam-identity-center-team/&#34;&gt;Temporary elevated access management (TEAM)&lt;/a&gt; within my landing zone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the rest I have some workload accounts that have an account per environment. Each workload has at least a development account, but it can be extended to have a test, acceptance and production account as well.&lt;/p&gt;
&lt;h2 id=&#34;account-creation&#34;&gt;Account creation&lt;/h2&gt;
&lt;p&gt;When I want to build something new, I need a new development account. For account creation I use &lt;a href=&#34;https://github.com/aws-samples/aws-control-tower-automate-account-creation&#34;&gt;aws-samples/aws-control-tower-automate-account-creation&lt;/a&gt;. I altered the solution to directly inject a record into the DynamoDB table that triggers the account creation process. I have a simple command that I can run on the command line that requires the following arguments: workload name, description and the environment type.&lt;/p&gt;
&lt;h2 id=&#34;go-build&#34;&gt;Go build!&lt;/h2&gt;
&lt;p&gt;After I created a development account I login to the build account and navigate to the Service Catalog service. Depending on the idea I require a different product but up until now I created the following products:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Generic Project&lt;/strong&gt; - Contains a CodePipeline including a CodeCommit repository, CodeBuild step and a CloudFormation deployment step for each environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;S3 Hosted Website&lt;/strong&gt; - Contains a CodePipeline including a CodeCommit repository, CodeBuild step and a CloudFormation deployment step for each environment and a S3 deployment step.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I use these Service Catalog products to create the CI/CD pipelines to host for example, my own website &lt;a href=&#34;https://conijn.io/&#34;&gt;Conijn.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/s3-hosted-website.png&#34; alt=&#34;Screenshot of the parameters needed for the S3 hosted website&#34;&gt;&lt;/p&gt;
&lt;p&gt;The CodeCommit repository will be pre-filled with the following files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.pre-commit-config.yaml&lt;/code&gt; - contains the &lt;a href=&#34;https://github.com/awslabs/cfn-python-lint&#34;&gt;cfn-lint&lt;/a&gt; and &lt;a href=&#34;https://github.com/stelligent/cfn_nag&#34;&gt;cfn_nag&lt;/a&gt; pre-commit hooks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;buildspec.yml&lt;/code&gt; - installs the &lt;a href=&#34;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html&#34;&gt;aws-sam-cli&lt;/a&gt; and builds and packages the template.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;parameters-development.json&lt;/code&gt; - The CloudFormation parameters, tags and policies for the development environment.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;parameters-production.json&lt;/code&gt; - The CloudFormation parameters, tags and policies for the production environment.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt; - Initial read me file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;template.yaml&lt;/code&gt; - Initial CloudFormation template.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After the Service Catalog product has been successfully launched. I only need to execute the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone codecommit::&amp;lt;AWS_REGION&amp;gt;://&amp;lt;MY_PROFILE_NAME&amp;gt;@&amp;lt;PROJECT_NAME&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And I am ready to start building, I can start with adding resources to my template. If I need to use parameters I can use the &lt;code&gt;parameters-development.json&lt;/code&gt; file. If I need to do additional steps I can add them in the &lt;code&gt;buildspec.yml&lt;/code&gt;. When I commit the changes and push them to the remote repository CodePipeline will be triggered and deploy the changes into the development account.&lt;/p&gt;
&lt;p&gt;For visibility of the pipelines I have set up a &lt;strong&gt;NotificationTopic&lt;/strong&gt;, this topic is a SNS Topic that has &lt;a href=&#34;https://aws.amazon.com/chatbot/&#34;&gt;AWS ChatBot&lt;/a&gt; as a subscriber. Chatbot will then send the updates to my Slack workspace that I have set up. This way when the pipeline is triggered I will get the notifications on my phone and laptop.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Setting up your own landing zone is quite some work, but you will learn a lot from it. And, when you have one it makes it very easy to start a new initiative in an empty AWS Account.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/plane-flying-over-airfield-before-arrival-3856702/&#34;&gt;Vincent Albos&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using Golang for your Serverless projects</title>
      <link>https://conijn.io/blog/2023/12/using-golang-for-your-serverless-projects/</link>
      <pubDate>Tue, 19 Dec 2023 11:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2023/12/using-golang-for-your-serverless-projects/</guid>
      <description>&lt;p&gt;In one of my &lt;a href=&#34;https://conijn.io/blog/2023/08/using-golang-for-your-aws-lambda-functions/&#34;&gt;previous blogs&lt;/a&gt; I wrote why I switched to compiled languages for my lambda functions. But using Golang for your lambda functions does add some challenges. In this blog I would like to share the challenges that I have seen and how to mitigate them.&lt;/p&gt;
&lt;h2 id=&#34;spoiler-i-use-a-makefile&#34;&gt;Spoiler, I use a Makefile&lt;/h2&gt;
&lt;p&gt;I always use a Makefile in my projects, If you are interested in why I wrote a &lt;a href=&#34;https://conijn.io/blog/2021/09/make-your-life-easier-using-makefiles/&#34;&gt;blog&lt;/a&gt; on that as well. I am mentioning this before we dive into the challenges to keep the focus on the solution and not the technology.&lt;/p&gt;
&lt;h2 id=&#34;every-function-is-its-own-module&#34;&gt;Every function is its own module?&lt;/h2&gt;
&lt;p&gt;When you develop a CLI tool, your project structure is quite simple. You have a &lt;code&gt;go.mod&lt;/code&gt; file in the project root directory, and at least a &lt;code&gt;main.go&lt;/code&gt; file that contains the business logic. When you are creating a serverless project, this changes. This is because each lambda function needs to be its own module. As a result you will need to create a folder for each function and each folder contains its own &lt;code&gt;go.mod&lt;/code&gt; and &lt;code&gt;main.go&lt;/code&gt; files, forming its own module.&lt;/p&gt;
&lt;h2 id=&#34;managing-dependencies&#34;&gt;Managing dependencies&lt;/h2&gt;
&lt;p&gt;The first challenge that I noticed is dependency management. This is now scattered over your lambda functions. This adds more maintenance to your project. It will also cause issues in your IDE. Since your IDE would expect a single module and not multiple it gets confused.&lt;/p&gt;
&lt;p&gt;You can solve this by setting up a &lt;a href=&#34;https://go.dev/doc/tutorial/workspaces&#34;&gt;multi-module workspace&lt;/a&gt;. In essence you need to create a &lt;code&gt;go.work&lt;/code&gt; file in the root directory of your project. Then you can point to your folders that contain the modules. Your IDE should now be able to understand all your modules and handle the dependencies for you.&lt;/p&gt;
&lt;p&gt;To ensure that your dependencies are up-to-date and we only include the one that we actually use you can run &lt;code&gt;go get -u&lt;/code&gt; and &lt;code&gt;go mod tidy&lt;/code&gt;. But you will need to do this for each individual module. For this reason I use the following Makefile target:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Makefile&#34; data-lang=&#34;Makefile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; tidy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tidy&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;## Run `go get -u` and `go mod tidy` for all modules
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;info &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;+&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Running &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;go get -u&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt; and &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;go mod tidy&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   find . -name go.mod -execdir go get -u &lt;span style=&#34;color:#ae81ff&#34;&gt;\;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   find . -name go.mod -execdir go mod tidy &lt;span style=&#34;color:#ae81ff&#34;&gt;\;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will look for each folder that contains a &lt;code&gt;go.mod&lt;/code&gt; file and executes those 2 commands in that folder for you. This makes it a simple and easy command to run more often.&lt;/p&gt;
&lt;h2 id=&#34;running-tests&#34;&gt;Running tests&lt;/h2&gt;
&lt;p&gt;I am a fan of &lt;a href=&#34;https://en.wikipedia.org/wiki/Test-driven_development&#34;&gt;TDD&lt;/a&gt; (test driven development), so obviously I wrote tests for my lambda functions. An example can be found in the “&lt;a href=&#34;https://conijn.io/blog/2023/08/stubbing-aws-service-calls-in-golang/&#34;&gt;Stubbing AWS Service calls in Golang&lt;/a&gt;” blog I wrote. But with this multi-module workspace setup it’s hard to run all tests.&lt;/p&gt;
&lt;p&gt;If you run &lt;code&gt;go test ./...&lt;/code&gt; it will only run the tests that you might have in the root of your project. And, not in the modules that are used by your lambda functions. An option would be to navigate into your lambda folder and execute the command there. But since it requires additional steps the likelihood of you doing that is smaller than a single simple command.&lt;/p&gt;
&lt;p&gt;For this reason I use the following Makefile target:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Makefile&#34; data-lang=&#34;Makefile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; test
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;info &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;+&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Running unit tests&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   find . -name go.mod -execdir go test ./... -coverprofile&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;coverage.out -covermode count &lt;span style=&#34;color:#ae81ff&#34;&gt;\;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again same as we did with the dependency management, we will look for each &lt;code&gt;go.mod&lt;/code&gt; file. And execute the &lt;code&gt;go test&lt;/code&gt; command in that folder. It will also generate a &lt;code&gt;coverage.out&lt;/code&gt; file. But since the file only contains the coverage of the tested module we need to combine them together:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Makefile&#34; data-lang=&#34;Makefile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;reports/coverage.out&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   find . -name coverage.out -type f -exec sh -c &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cat {} | if [ &amp;#34;$1&amp;#34; = &amp;#34;{}&amp;#34; ]; then cat {}; else tail -n +2 {}; fi&amp;#39;&lt;/span&gt; sh &lt;span style=&#34;color:#f92672&#34;&gt;{}&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\;&lt;/span&gt; &amp;gt; reports/coverage.temp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mode: count&amp;#34;&lt;/span&gt; | cat - reports/coverage.temp &amp;gt; reports/coverage.out
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   rm reports/coverage.temp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are now looking for each &lt;code&gt;coverage.out&lt;/code&gt; file and take all the content minus the first line. We are placing all those lines in a single file and then we add the first line back in there.&lt;/p&gt;
&lt;p&gt;Now if you want to &lt;a href=&#34;https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization.html&#34;&gt;visualize your coverage&lt;/a&gt; in for example GitLab. You will need a Cobertura XML file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Makefile&#34; data-lang=&#34;Makefile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;reports/coverage.xml&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; reports/coverage.out
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;info Collecting Code Coverage&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   go run github.com/boumenot/gocover-cobertura &amp;lt; reports/coverage.out &amp;gt; reports/coverage.xml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We will just use the &lt;code&gt;gocover-cobertura&lt;/code&gt; module to convert the file to the needed XML format and you are good to go.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It’s possible to have a multi-module setup and have a simple and single command to perform actions like running your tests. However, you do need to maintain some scripting in for example a Makefile. A full working example can be found on &lt;a href=&#34;https://github.com/conijnio/aws-security-posture&#34;&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/multiple-yachts-anchored-on-the-shore-6073566/&#34;&gt;alleksana&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Avoid using the default profile</title>
      <link>https://conijn.io/blog/2023/10/avoid-using-the-default-profile/</link>
      <pubDate>Sat, 21 Oct 2023 11:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2023/10/avoid-using-the-default-profile/</guid>
      <description>&lt;p&gt;When you start working with AWS one of the easiest ways to interact with the APIs is through the IAM User. Although it&amp;rsquo;s not the advised way it&amp;rsquo;s the easiest way hence the most used one and the most abused one. In this blog I will dive a little deeper into this topic.&lt;/p&gt;
&lt;h2 id=&#34;easy-to-use&#34;&gt;Easy to use&lt;/h2&gt;
&lt;p&gt;Using an IAM User is the oldest way to interact with the AWS API. You only need an AccessKey and a SecretAccessKey and you are good to go!
When you have the AWS CLI installed you only need to execute the &lt;code&gt;aws configure&lt;/code&gt; command. This will as for some inputs via prompts:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ aws configure
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AWS Access Key ID &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;None&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: AKIAIOSFODNN7EXAMPLE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AWS Secret Access Key &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;None&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Default region name &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;eu-west-1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: eu-west-1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Default output format &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;yaml&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will result in the following &lt;code&gt;~/.aws/credentials&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;default&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_access_key_id&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;AKIAIOSFODNN7EXAMPLE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_secret_access_key&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;wJalrXUtnFEMI&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;K7MDENG&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bPxRfiCYEXAMPLEKEY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And you are good to go! You can now perform any aws cli command. Assuming the IAM User has the correct policies in place to perform the needed actions.
Having these credentials in place makes your life easy. For example: the &lt;a href=&#34;https://boto3.amazonaws.com/v1/documentation/api/latest/index.html&#34;&gt;Boto3 SDK&lt;/a&gt; will use your credentials. Super convenient, super dangerous!&lt;/p&gt;
&lt;h2 id=&#34;boto3-sdk&#34;&gt;Boto3 SDK&lt;/h2&gt;
&lt;p&gt;When you are developing you often run your code to test if it works. Now I would love to think that everybody uses &lt;a href=&#34;https://en.wikipedia.org/wiki/Test-driven_development&#34;&gt;Test Driven Development&lt;/a&gt; but I know this is not true. This could have many reasons, experience is the most obvious one. When you start your development journey there are already a lot of things that you need to learn. Writing unit tests is not the highest priority for someone who wants to learn how to write code. That being said, yes it will slow you down. But only in the beginning! When you master proper testing you will increase your productivity tenfold!&lt;/p&gt;
&lt;p&gt;So why are these credentials dangerous again? Assume you are writing some code to clean-up objects from a S3 bucket. If you have a bug in your code, and you run your script it will be using the default credentials as a default. Resulting in potential data loss in your AWS Account! And this is of course one of the examples that can go wrong. Even when you do use test driven development, and you forget to stub one of the API calls. Boto3 will use the default credentials and execute the API call.&lt;/p&gt;
&lt;h2 id=&#34;avoid-using-a-default-profile&#34;&gt;Avoid using a default profile&lt;/h2&gt;
&lt;p&gt;So, should you not use the IAM User credentials then? Well there are &amp;ldquo;better&amp;rdquo; alternatives, like Single Sign On. When you configure those credentials as your default credentials, you will face the same problem!  So avoid the default profile! Either don&amp;rsquo;t configure it or use mock credentials like I used in the example. They will result in an access denied but that is better than losing data. Next to that you should use the &lt;a href=&#34;https://en.wikipedia.org/wiki/Principle_of_least_privilege&#34;&gt;principle of the least privileged&lt;/a&gt;. Because when your principal does not have the access rights, it cannot perform them!&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Make sure you use the least privileged principle. And avoid sticking your credentials in the default profile.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/photo-of-a-boy-near-leaves-1126621/&#34;&gt;Min An&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Stubbing AWS Service calls in Golang</title>
      <link>https://conijn.io/blog/2023/08/stubbing-aws-service-calls-in-golang/</link>
      <pubDate>Tue, 15 Aug 2023 09:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2023/08/stubbing-aws-service-calls-in-golang/</guid>
      <description>&lt;p&gt;I recently switched to Golang for my language of choice. (In my previous &lt;a href=&#34;https://conijn.io/blog/2023/08/using-golang-for-your-aws-lambda-functions/&#34;&gt;blog&lt;/a&gt; you can read why.) But I am also a big fan of test driven development. With Python you have a &lt;a href=&#34;https://botocore.amazonaws.com/v1/documentation/api/latest/reference/stubber.html&#34;&gt;stubber&lt;/a&gt; that helps you mock the AWS API. So how do you do this in Golang? I found 2 ways to do this. One via dependency injection and one via a stubber. In this blog I will share my experience so far.&lt;/p&gt;
&lt;h2 id=&#34;using-dependency-injection&#34;&gt;Using dependency injection&lt;/h2&gt;
&lt;p&gt;My first experiment was with dependency injection. I used the following code to do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-sdk-go-v2/config&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-sdk-go-v2/service/s3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;s3Client&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;() (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;cfg&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;LoadDefaultConfig&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TODO&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; new(&lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetS3Client&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewFromConfig&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;cfg&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;SetS3Client&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Client&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;s3Client&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Handler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []byte(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;s3Client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObject&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObjectInput&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Bucket&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;:    &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-object-key&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Body&lt;/span&gt;:   &lt;span style=&#34;color:#a6e22e&#34;&gt;bytes&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewReader&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;{}, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, I use the &lt;code&gt;SetS3Client&lt;/code&gt; method as a setter. It allows me to set the client from the outside. This is useful when doing unit tests. You can use this in your tests as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-sdk-go-v2/service/s3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;testing&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mockS3Client&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mockS3Client&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;PutObject&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObjectInput&lt;/span&gt;) (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObjectOutput&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObjectOutput&lt;/span&gt;{}, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TestHandler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetS3Client&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mockS3Client&lt;/span&gt;{})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Background&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Run&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Invoke Handler&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Handler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	    &lt;span style=&#34;color:#75715e&#34;&gt;// Perform Assertions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We inject a mocked object that acts as the client used to perform the API calls. With this approach I could now write some tests. But I realized that this approach creates another problem. For example, what if you have 2 API calls that perform a &lt;code&gt;PutObject&lt;/code&gt; call. In this example I return an empty &lt;code&gt;PutObjectOutput&lt;/code&gt;. But I want to test more than one scenarios, so how do you control this behavior in your mocked object?&lt;/p&gt;
&lt;h2 id=&#34;using-a-stubber&#34;&gt;Using a stubber&lt;/h2&gt;
&lt;p&gt;So I did some more research and I found the &lt;a href=&#34;https://github.com/awsdocs/aws-doc-sdk-examples/tree/main&#34;&gt;awsdocs/aws-doc-sdk-examples&lt;/a&gt; repo. This repository used a &lt;code&gt;testtools&lt;/code&gt; module. So I started an experiment to see how I could use this module. I refactored the code as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-sdk-go-v2/aws&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-sdk-go-v2/service/s3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;s3Client&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;cfg&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Config&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; new(&lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;s3Client&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewFromConfig&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;cfg&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Lambda&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Handler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []byte(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;s3Client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObject&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObjectInput&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Bucket&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;:    &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-object-key&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Body&lt;/span&gt;:   &lt;span style=&#34;color:#a6e22e&#34;&gt;bytes&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewReader&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;{}, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I added a &lt;code&gt;cfg&lt;/code&gt; parameter to the &lt;code&gt;New&lt;/code&gt; method, so I also need to pass this in my main method.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-lambda-go/lambda&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-sdk-go-v2/config&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;cfg&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;LoadDefaultConfig&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TODO&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Start&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;cfg&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Handler&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The test itself looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bytes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;encoding/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;errors&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-sdk-go-v2/aws&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-sdk-go-v2/service/s3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/awsdocs/aws-doc-sdk-examples/gov2/testtools&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;io&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;testing&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TestHandler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Background&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Run&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Upload a file to S3&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;stubber&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;testtools&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewStubber&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;stubber&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SdkConfig&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;stubber&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;testtools&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stub&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;OperationName&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PutObject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;Input&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObjectInput&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#a6e22e&#34;&gt;Bucket&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-sample-bucket&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;:    &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my/object.json&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#a6e22e&#34;&gt;Body&lt;/span&gt;:   &lt;span style=&#34;color:#a6e22e&#34;&gt;bytes&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewReader&lt;/span&gt;([]&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;{}),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;Output&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObjectOutput&lt;/span&gt;{},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Handler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;testtools&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ExitTest&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;stubber&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;// Perform Assertions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, we now moved the mock in the test itself. This enables you to let the AWS API react based on your test. The biggest advantage is that it&amp;rsquo;s encapsulated in the test itself. For example, If you want to add a scenario where the &lt;code&gt;PutObject&lt;/code&gt; call failed you add the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Run&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Fail on upload&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;stubber&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;testtools&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewStubber&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;stubber&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SdkConfig&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;raiseErr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testtools&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;StubError&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Err&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;errors&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ClientError&amp;#34;&lt;/span&gt;)}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;stubber&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;testtools&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stub&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;OperationName&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PutObject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Input&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s3&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PutObjectInput&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Bucket&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-sample-bucket&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;:    &lt;span style=&#34;color:#a6e22e&#34;&gt;aws&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my/object.json&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Body&lt;/span&gt;:   &lt;span style=&#34;color:#a6e22e&#34;&gt;bytes&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewReader&lt;/span&gt;([]&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;{}),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;raiseErr&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Handler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;testtools&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;VerifyError&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;raiseErr&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;testtools&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ExitTest&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;stubber&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The main advantage of the stubber is that you validate the &lt;code&gt;Input&lt;/code&gt; out of the box. But in some cases you want to ignore certain fields. For example, if you use a timestamp in your &lt;code&gt;Key&lt;/code&gt;. Or the actual &lt;code&gt;Body&lt;/code&gt; of the object you are uploading.&lt;/p&gt;
&lt;p&gt;You can ignore these fields by setting the &lt;code&gt;IgnoreFields&lt;/code&gt;. (Example: &lt;code&gt;IgnoreFields: []string{&amp;quot;Key&amp;quot;, &amp;quot;Body&amp;quot;}&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;The second thing I like about using the stubber is the &lt;code&gt;VerifyError&lt;/code&gt; method. This will verify if the error is returned that you raised in your test scenario.&lt;/p&gt;
&lt;p&gt;The last one is the &lt;code&gt;ExitTest&lt;/code&gt; method. This will make sure that all the defined stubs are actually called. In other words, if you still have a stub. Your test will fail because there is still a uncalled stub.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;testtool&lt;/code&gt; is a good replacement of the stubber I used in Python. It allows you to encapsulate scenario data in your test. Avoiding hard to maintain mock objects. The &lt;code&gt;testtool&lt;/code&gt; works from the configuration, so you don&amp;rsquo;t need to stub every client. Resulting in less code that is needs to test your implementation.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/brave-doctor-in-flying-superhero-cape-with-fist-stretched-6303759/&#34;&gt;Klaus Nielsen&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using Golang for your AWS Lambda Functions</title>
      <link>https://conijn.io/blog/2023/08/using-golang-for-your-aws-lambda-functions/</link>
      <pubDate>Fri, 04 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2023/08/using-golang-for-your-aws-lambda-functions/</guid>
      <description>&lt;p&gt;I was sparked on a &lt;a href=&#34;https://xke.xebia.com/faq/&#34;&gt;XKE&lt;/a&gt; to do a short experiment with using Golang for my &lt;a href=&#34;https://aws.amazon.com/lambda&#34;&gt;AWS Lambda Functions&lt;/a&gt;. The trigger for this was something my colleague Mark van Holsteijn said. We where talking about sustainability. During this talk Mark made the comment that we should drop Python for our functions then. (He also recently wrote a &lt;a href=&#34;https://xebia.com/blog/how-to-create-and-deploy-a-golang-aws-cloudformation-custom-provider-in-less-the-5-minutes/&#34;&gt;blog&lt;/a&gt; on Golang using Golang for custom providers.)&lt;/p&gt;
&lt;p&gt;Let me compare some numbers between Python and Go. (source: &lt;a href=&#34;https://thenewstack.io/which-programming-languages-use-the-least-electricity/&#34;&gt;Which Programming Languages Use the Least Electricity?&lt;/a&gt;))&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Energy: 75.88 vs 3.23&lt;/li&gt;
&lt;li&gt;Time: 71.90 vs 2.83&lt;/li&gt;
&lt;li&gt;Memory: 2.80 vs 1.05&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, based on these numbers Golang is clear winner. But are there other advantages? So, like always everything comes with it&amp;rsquo;s own pros and cons. Lets explore those when you do chose for Golang.&lt;/p&gt;
&lt;h2 id=&#34;pre-compiled-binary-vs-compiled-at-run-time&#34;&gt;Pre-compiled binary vs compiled at run time&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start with the obvious one, a pre compiled binary vs code that is compiled at run time. This is one of the reasons why Golang is more energy efficient as you only compile once vs every time you run it. But the advantage of Python is that you can actually see the source code in the AWS Console and tweak it. This makes it a great candidate for easy development and fast testing. This also a problem! When you are running production workloads. You don&amp;rsquo;t want your developers altering your code through the console. Yes, IAM can help you here but altering a binary is not possible through the console.&lt;/p&gt;
&lt;h2 id=&#34;you-need-to-compile&#34;&gt;You need to compile&lt;/h2&gt;
&lt;p&gt;Why is the need to compile an advantage? Next to the performance improvement you actually check if your program compiles. If it does not compile you cannot deploy it. Compare this with Python, you can upload malformed code. This would than break your workload because of an indent issue. You can build in checks to prevent this from happening, and you should! But you need to do something extra, with Golang this comes out of the box.&lt;/p&gt;
&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;
&lt;p&gt;In python you have the option to do inline code in &lt;a href=&#34;https://aws.amazon.com/cloudformation&#34;&gt;AWS CloudFormation&lt;/a&gt; templates. I would never recommend doing this. When you needs other dependencies than that there are available. You will need to bundle them yourself and upload them as a zipfile. When you are missing a dependency the invocation of your function will fail at runtime. When you use Golang the dependencies are all build into the binary. Especially when you set the &lt;code&gt;CGO_ENABLED=0&lt;/code&gt; option in your compile options. This will make sure that all the needed libraries are included.&lt;/p&gt;
&lt;h2 id=&#34;tutorial&#34;&gt;Tutorial&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s deploy a small stack including a single Lambda function. We will be using &lt;a href=&#34;https://aws.amazon.com/serverless/sam/&#34;&gt;AWS SAM&lt;/a&gt; for the deployment, so please make sure you have that installed.&lt;/p&gt;
&lt;h3 id=&#34;prepare-the-file-structure&#34;&gt;Prepare the file structure&lt;/h3&gt;
&lt;p&gt;Create the folders and files we will be needing:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir aws-lambda-sample-golang
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd aws-lambda-sample-golang
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch template.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir my-function
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch my-function/Makefile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch my-function/main.go
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sam-template&#34;&gt;SAM Template&lt;/h3&gt;
&lt;p&gt;A SAM Template is nothing more that a CloudFormation template. But it contains the &lt;code&gt;AWS::Serverless-2016-10-31&lt;/code&gt; transformation. This allows you to use the &lt;code&gt;AWS::Serverless::Function&lt;/code&gt; resource. When CloudFormation parses this resource it will converted to CloudFormation resources.&lt;/p&gt;
&lt;p&gt;Add the following to the &lt;code&gt;template.yaml&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Transform&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyFunction&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Serverless::Function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;BuildMethod&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;makefile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Runtime&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;provided.al2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;CodeUri&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;./my-function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Handler&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bootstrap&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyFunctionLogGroup&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Logs::LogGroup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;LogGroupName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub /aws/lambda/${MyFunction}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;RetentionInDays&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A few things are going on here. You can see the &lt;code&gt;BuildMethod&lt;/code&gt; set in the &lt;code&gt;Metadata&lt;/code&gt; property. With this configuration you are saying to SAM that you want to use a &lt;code&gt;Makefile&lt;/code&gt;. This &lt;code&gt;Makefile&lt;/code&gt; is used to compile the lambda function.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CodeUri&lt;/code&gt; will point to the root of your Lambda functions code.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Handler&lt;/code&gt; will point to your binary output file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lets create the &lt;code&gt;Makefile&lt;/code&gt;, we need to place it in the root folder of the function &lt;code&gt;my-function&lt;/code&gt;. Add the following content to the file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Makefile&#34; data-lang=&#34;Makefile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;build-MyFunction&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	GOOS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;linux GOARCH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;amd64 CGO_ENABLED&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; go build -o bootstrap
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	cp ./bootstrap &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;ARTIFACTS_DIR&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;/.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The syntax used here is &lt;code&gt;build-&amp;lt;LOGICAL-ID-OF-CLOUDFORMATION&amp;gt;&lt;/code&gt;. So, if your LogicalId in the template is &lt;code&gt;HelloWorld&lt;/code&gt;. Your &lt;code&gt;Makefile&lt;/code&gt; target would be: &lt;code&gt;build-HelloWorld&lt;/code&gt;. The &lt;code&gt;go build&lt;/code&gt; command will build your binary. The copy command will copy the binary in the artifacts directory. This is used when you deploy your template.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note the &lt;code&gt;GOARCH&lt;/code&gt; if you use an ARM function you need to specify it here as well.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;lambda-code&#34;&gt;Lambda Code&lt;/h3&gt;
&lt;p&gt;We need to initialize our go module, and pull in the &lt;code&gt;github.com/aws/aws-lambda-go&lt;/code&gt; dependency:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd my-function
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go mod init my-function
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go get github.com/aws/aws-lambda-go
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd ..
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can fill the &lt;code&gt;my-function/main.go&lt;/code&gt; with some business logic:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/aws/aws-lambda-go/lambda&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyEvent&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;name&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Handler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyEvent&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Welcome %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;lambda&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Start&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Handler&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And done!&lt;/p&gt;
&lt;h3 id=&#34;deploy-the-template&#34;&gt;Deploy the template&lt;/h3&gt;
&lt;p&gt;To deploy the template we first need to build the binary. AWS SAM as build in support for building and packaging Lambda functions.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: AWS SAM will create a bucket in your account used for uploading the artifacts.&lt;/p&gt;&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sam build
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sam deploy &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --stack-name aws-lambda-sample-golang &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --capabilities CAPABILITY_IAM &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --resolve-s3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;testing&#34;&gt;Testing&lt;/h3&gt;
&lt;p&gt;You can now invoke the Lambda function, with a &lt;code&gt;{&amp;quot;name&amp;quot;: &amp;quot;Joris Conijn&amp;quot;}&lt;/code&gt; payload. The logs should now show:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Welcome Joris Conijn&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;For a full working copy you could also clone my &lt;a href=&#34;https://github.com/Nr18/aws-lambda-sample-golang&#34;&gt;Nr18/aws-lambda-sample-golang&lt;/a&gt; repository.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;For me Golang would become my default choice for AWS Lambda functions. The fact that it&amp;rsquo;s &amp;ldquo;more sustainable&amp;rdquo; and that it protects you from obvious failures convinced me. That beings said I have a software engineering background. So, for me it&amp;rsquo;s relatively easy to switch languages. How about you, would you use Golang as your default choice?&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/photo-of-wind-turbines-lot-1292464/&#34;&gt;Narcisa Aciko&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Invalidate CloudFront cache for Hugo websites</title>
      <link>https://conijn.io/blog/2023/07/invalidate-cloudfront-cache-for-hugo-websites/</link>
      <pubDate>Thu, 13 Jul 2023 21:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2023/07/invalidate-cloudfront-cache-for-hugo-websites/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://gohugo.io&#34;&gt;Hugo&lt;/a&gt; is a great tool that let you build website fast and easy. It will render a complete static HTML version of your website. This makes it an ideal candidate to host it on a S3 Bucket. You can then use CloudFront to serve the content across the globe.&lt;/p&gt;
&lt;h2 id=&#34;cloudfront-cache&#34;&gt;CloudFront Cache&lt;/h2&gt;
&lt;p&gt;Cache can be a wonderful thing! But it can also be an annoying thing. So I decided to dive a little bit deeper into this topic for my &lt;a href=&#34;https://conijn.io&#34;&gt;own personal&lt;/a&gt; website. So by default, when you use S3 as an origin by default the TTL (Time To Live) is set to 24 hours. This means that when you visit your site that version is than cached for 24 hours on the edge location.&lt;/p&gt;
&lt;p&gt;This means if you would update your website, the changes will not be visible for you. This is because you will most likely end up on the same edge server. You can invalidate the cache but objects like photos and videos benefit from this cache.&lt;/p&gt;
&lt;h2 id=&#34;only-invalidate-what-has-changed&#34;&gt;Only invalidate what has changed&lt;/h2&gt;
&lt;p&gt;So that brings us to the next logical step. Invalidate only the things that have been changed. I am using &lt;a href=&#34;https://aws.amazon.com/codecommit/&#34;&gt;CodeCommit&lt;/a&gt; as a source repository you can ask git for what files are changed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git diff-tree --no-commit-id --name-only -r HEAD
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will give you a list of files changed in your last commit, with this information you can do smart things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only select the files changed in the &lt;code&gt;content&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Replace the parts that contain any &lt;code&gt;_index&lt;/code&gt; to &lt;code&gt;index&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Replace the parts that contain any &lt;code&gt;.md&lt;/code&gt; to &lt;code&gt;.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Detect where we have post aggregation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So for this blog post that would result to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ git diff-tree --no-commit-id --name-only -r HEAD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content/blog/2023/07/invalidate-cloudfront-cache-for-hugo-websites/index.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So Hugo uses &lt;code&gt;_index.md&lt;/code&gt; files to generate pages that list all child pages. These overview pages are also cached and also need to be invalidated.&lt;/p&gt;
&lt;p&gt;By using the following script I will include these pages in my &amp;ldquo;to be invalidated&amp;rdquo; list:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; List
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; subprocess
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;command &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;git&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;diff-tree&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;--no-commit-id&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;--name-only&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-r&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HEAD&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;output &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; subprocess&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;check_output(command, cwd&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getcwd())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;decode(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;changed_files &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; output&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content_changed&lt;/span&gt;(path: str) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; bool:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;convert_html_endpoints&lt;/span&gt;(path: str) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content/&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_index&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;index&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.md&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.html&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;path&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; path
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;find_list_pages&lt;/span&gt;(path: str) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; List[str]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    existing_path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list_pages &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; part &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        list_page_path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(existing_path, part, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exists(list_page_path):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            list_pages&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(existing_path, part, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        existing_path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(existing_path, part)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; list_pages
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;flatten_list&lt;/span&gt;(list_of_lists: List[List[str]]) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; List[str]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [item &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; sublist &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; list_of_lists &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; item &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; sublist]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;changed_files &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; list(flatten_list(map(find_list_pages, changed_files)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;changed_files &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromkeys(changed_files))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;changed_files &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; filter(content_changed, changed_files)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;changed_files &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(sorted(map(convert_html_endpoints, changed_files)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;list(map(print, changed_files))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s examine what that would bring us:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./retrieve_files.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/blog/2023/07/invalidate-cloudfront-cache-for-hugo-websites/index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/blog/index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The blog post itself is listed, but also the front page and the blog section of my website. Since these pages are listing my page I include them in the pages to invalidate.&lt;/p&gt;
&lt;h2 id=&#34;putting-it-together&#34;&gt;Putting it together&lt;/h2&gt;
&lt;p&gt;In my build phase I will call this script since I will have access to the commit history there. A simple command will capture these files in a &lt;code&gt;changelog.txt&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./retrieve_files.py &amp;gt; public/changelog.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because my pipeline runs in a separate AWS account I could not use a Lambda function.
Instead I upload the changelog file to the S3 bucket. And I use a &lt;a href=&#34;https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventNotifications.html&#34;&gt;S3 Event Notifications&lt;/a&gt; to trigger the &lt;a href=&#34;https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudfront/client/create_invalidation.html&#34;&gt;create_invalidation&lt;/a&gt; call.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can use your existing systems to keep track what has changed. You only need to expose it in the correct way so that it becomes usable in a downstream actions.&lt;/p&gt;
&lt;p&gt;This way you make use of a decoupled event driven pattern.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/white-clouds-2114014/&#34;&gt;Magda Ehlers&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Automate your workload insights</title>
      <link>https://conijn.io/blog/2023/06/automate-your-workload-insights/</link>
      <pubDate>Wed, 28 Jun 2023 11:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2023/06/automate-your-workload-insights/</guid>
      <description>&lt;p&gt;Managing more than one AWS accounts can become challenging. AWS Organizations provides a way to manage many accounts. You can automate account creation and management. If you use a naming schema you can leverage &lt;a href=&#34;https://github.com/binxio/landingzone-organization&#34;&gt;landingzone-organization&lt;/a&gt;! &lt;/p&gt;
&lt;h2 id=&#34;naming-schema&#34;&gt;Naming schema?&lt;/h2&gt;
&lt;p&gt;Using a naming schema improves your ability to find the correct account faster. For example, let say your company Xebia has some workloads. Each workload requires a full &lt;a href=&#34;https://en.wikipedia.org/wiki/Development,_testing,_acceptance_and_production&#34;&gt;DTAP&lt;/a&gt; environments. When you follow the best practices of AWS, you will end up using an account per environment.&lt;/p&gt;
&lt;p&gt;Using a naming schema your account name would look like this: &lt;code&gt;xebia-&amp;lt;workloadname&amp;gt;-&amp;lt;environment&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;When you make use of AWS SSO you will see a list of accounts. In each account you will have one or more roles that you can assume. Depending of your permissions that could be none or all accounts.&lt;/p&gt;
&lt;p&gt;When you are a workload engineer you will have a short list. When you are a platform operator or an auditor you might have access to all accounts. In the later, it helps when you have applied this schema. For example, you can search on the workload name. This will filter out the list only showing the environment account for that workload. Or you can search on environment, or even a combination.&lt;/p&gt;
&lt;h2 id=&#34;going-a-step-further&#34;&gt;Going a step further&lt;/h2&gt;
&lt;p&gt;Combining a naming schema with an &lt;a href=&#34;https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_ous.html&#34;&gt;OU&lt;/a&gt; layout wil result in even more flexibility. &lt;/p&gt;
&lt;p&gt;First, lets address obvious advantages. When you use a OU structure as followed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;workloads/development/xebia-myworkloadname-development&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;workloads/testing/xebia-myworkloadname-testing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;workloads/acceptance/xebia-myworkloadname-acceptance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;workloads/production/xebia-myworkloadname-production&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This allows you to place a &lt;a href=&#34;https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html&#34;&gt;SCP&lt;/a&gt; on the &lt;code&gt;workloads&lt;/code&gt; OU this one applies to all workloads. But you also have the ability to apply a SCP on each individual environment OU. This SCP is then only applied to the development accounts for example. I will not go into SCP that is a topic on its own.&lt;/p&gt;
&lt;p&gt;The other advantage is that you now know where your workloads live. Combine that with &lt;a href=&#34;https://binxio.github.io/landingzone-organization/3-user-documentation/using-python-module/&#34;&gt;landingzone-organization&lt;/a&gt; and you can do things like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; landingzone_organization &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; AWSOrganization
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Fetch the organization information and instantiate the organization object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;organization &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; AWSOrganization()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Define the OU structure where the workload accounts live&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nested_ou &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Workloads&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Iterate over all workloads in the provided OU location&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; workload &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; organization&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;workloads(nested_ou):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Display the workload name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Workload: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;workload&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Iterate over each account in the workload&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; account &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; workload&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;accounts:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# Display the environment name and id of the account (aka the workload environment)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\t&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Environment: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;account&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;environment&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; has &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;account&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;account_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;why-does-this-help-me&#34;&gt;Why does this help me?&lt;/h2&gt;
&lt;p&gt;Keeping track of workloads that are actually running is hard. But if it does not have a production account you know the workload is not in production yet. Next to that keeping track of what workloads exists within your organization is hard. Most of the times this comes from spreadsheets, how do you keep those up to date?&lt;/p&gt;
&lt;p&gt;With this tool you can write all the needed data to a CSV file for later consumption. It could become the source of truth within your company and you can automate! &lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Automation makes are life easier! So stop keeping track of your workloads the old fashion way and start using automation tools. &lt;/p&gt;
&lt;p&gt;There are various applications for this that I will hope to address in a future blog, so stay tuned!&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/16841438/&#34;&gt;Sangria Lips&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use StepFunctions to simplify your serverless applications</title>
      <link>https://conijn.io/blog/2023/06/use-stepfunctions-to-simplify-your-serverless-applications/</link>
      <pubDate>Fri, 09 Jun 2023 08:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2023/06/use-stepfunctions-to-simplify-your-serverless-applications/</guid>
      <description>&lt;h1 id=&#34;use-stepfunctions-to-simplify-your-serverless-applications&#34;&gt;Use StepFunctions to simplify your serverless applications&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html&#34;&gt;AWS StepFunctions&lt;/a&gt; is a great orchestrating tool for your serverless applications. When you write lambda functions that only contain logic to perform a single task they are easier to test. Especially when there is no orchestration logic within your function. &lt;/p&gt;
&lt;p&gt;The orchestration is then performed by StepFunctions by defining a state machine. This state machine is nothing more than a JSON definition based on &lt;a href=&#34;https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html&#34;&gt;Amazon State Language&lt;/a&gt;. It describes the flow between the functions for your specific use-case.&lt;/p&gt;
&lt;h2 id=&#34;lets-use-an-example&#34;&gt;Lets use an example&lt;/h2&gt;
&lt;p&gt;Lets say one of the input parameters is an object location on S3. We will feed this to your state machine. A lambda function can then be used to read this object and perform some business logic on this data.&lt;/p&gt;
&lt;p&gt;Or you can use the &lt;a href=&#34;https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html#use-awssdk-integ&#34;&gt;AWS SDK Service&lt;/a&gt; integration. By using the service integration you can again simplify your business logic. Instead of needing to read an object from S3 and do some actions. You only need to do the action on the data that you receive. This will remove the need of mocking S3 GetObject calls in your unit tests. Next to that you do not have to pay for the time it takes to read the data from S3. This is done outside of the scope of the lambda function, and therefor you do not have to pay for it.&lt;/p&gt;
&lt;h2 id=&#34;sounds-great-do-you-have-an-example-for-me&#34;&gt;Sounds great, do you have an example for me?&lt;/h2&gt;
&lt;p&gt;Lets assume the following payload it given:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Bucket&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket-name&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ObjectKey&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-path/to-my-file.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we have a the following task in our state machine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Read From S3&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Task&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:states:::aws-sdk:s3:getObject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Parameters&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Bucket&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.Bucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Key&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.ObjectKey&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;OutputPath&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.Body&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ResultPath&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.ObjectBody&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Next&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;My Lambda&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will perform a s3:getObject action on s3://my-bucket-name/my-path/to-my-file.json. The action returns the content of the file in the Body parameter. And we will place this content in the  ObjectBody parameter of the event. So the event will now look like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Bucket&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket-name&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ObjectKey&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-path/to-my-file.json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ObjectBody&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{\&amp;#34;Foo\&amp;#34;: \&amp;#34;Bar\&amp;#34;}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;TransformedBody&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{\&amp;#34;Foo\&amp;#34;: \&amp;#34;Bar\&amp;#34;}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can now invoke a lambda function to do something with this payload:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;My Lambda&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Task&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:states:::lambda:invoke&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;InputPath&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.ObjectBody&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Parameters&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;FunctionName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:lambda:eu-west-1:123456789012:function:my-function&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ResultPath&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.TransformedBody&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Next&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Write to S3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Assume that in the lambda function we enriched the object with an extra parameter. So the event will look like this now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Bucket&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket-name&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ObjectKey&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-path/to-my-file.json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ObjectBody&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{\&amp;#34;Foo\&amp;#34;: \&amp;#34;Bar\&amp;#34;}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;TransformedBody&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{\&amp;#34;Foo\&amp;#34;: \&amp;#34;Bar\&amp;#34;, \&amp;#34;Bar\&amp;#34;: \&amp;#34;Foo\&amp;#34;}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can now store the transformed event on S3:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Write to S3&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Task&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:states:::aws-sdk:s3:putObject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Parameters&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Bucket&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.Bucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Key&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.ObjectKey&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Body.$&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$.TransformedBody&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;End&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And done, the existing object is updated with the extra parameter in the json structure.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion &lt;/h2&gt;
&lt;p&gt;You can use StepFunctions to remove the following from your lambda functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Orchestration logic&lt;/li&gt;
&lt;li&gt;AWS API Calls&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This enables you to focus on the task at hand, and simplifies the code that you need to write and maintain.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/time-lapse-photography-of-lake-1480807/&#34;&gt;Baskin Creative Studios&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why should I set a canonical URL?</title>
      <link>https://conijn.io/blog/2023/05/why-should-i-set-a-canonical-url/</link>
      <pubDate>Wed, 10 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2023/05/why-should-i-set-a-canonical-url/</guid>
      <description>&lt;p&gt;If you like blogging like me, you probably post your content on multiple places. For example, I post my content on &lt;a href=&#34;https://xebia.com/blog&#34;&gt;xebia.com&lt;/a&gt;, &lt;a href=&#34;https://conijn.io/blog/&#34;&gt;conijn.io&lt;/a&gt; and &lt;a href=&#34;https://dev.to/aws-builders&#34;&gt;dev.to&lt;/a&gt;. When you do not provide a canonical url search engines like Google are less likely to show your blog.&lt;/p&gt;
&lt;h2 id=&#34;what-is-a-canonical-url&#34;&gt;What is a canonical URL&lt;/h2&gt;
&lt;p&gt;I describe a canonical url the same way as you do other source disclosures. For example, when you publish your blog on site A. This would be the original content. By posting it on site B you are duplicating the content. Since you are the author that is all fine. But the search engine does not know this. So by pointing to the source, the search engines know what page they should recommend.&lt;/p&gt;
&lt;h2 id=&#34;how-can-you-use-a-canonical-url&#34;&gt;How can you use a canonical URL&lt;/h2&gt;
&lt;p&gt;There are 2 relatively easy ways to set a canonical url. Via a header or using a html tag. For example the headers of your website request could return:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Link: &amp;lt;https://xebia.com/blog/why-should-i-set-a-canonical-url/&amp;gt;; rel=&amp;#34;canonical&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For this method you need control over how the headers are generated. The other method is simpler, using a html tag:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;canonical&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://xebia.com/blog/why-should-i-set-a-canonical-url&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The reason why I like this method, is because it is easy to setup using &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;what-is-hugo&#34;&gt;&lt;strong&gt;What is Hugo?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;According to the website:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hugo is one of the most popular open-source static site generators. With its amazing speed and flexibility, Hugo makes building websites fun again.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Hugo uses simple &lt;a href=&#34;https://www.markdownguide.org/&#34;&gt;Markdown&lt;/a&gt; files as input and renders a complete static website out of that. So you will have a single markdown file for your blog.&lt;/p&gt;
&lt;h2 id=&#34;setup-a-canonical-url-in-hugo&#34;&gt;Setup a canonical URL in Hugo&lt;/h2&gt;
&lt;p&gt;Each post is a separate markdown file, so you can define some metadata using &lt;a href=&#34;https://gohugo.io/content-management/front-matter/&#34;&gt;front-matter&lt;/a&gt;. In this metadata you can also add the canonical url:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Markdown&#34; data-lang=&#34;Markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;author: Joris Conijn
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;canonicalUrl: https://xebia.com/blog/why-should-i-set-a-canonical-url/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;categories:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;-&lt;/span&gt; Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: 2023-05-10 00:00:00+00:00
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;excerpt: Learn why you should set a canonical url when you do cross-posting of your
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  blogs.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;featured_image: images/banner.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;focus-keywords: set canonical url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;slug: why-should-i-set-a-canonical-url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subtitle: &amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;title: Why should I set a canonical URL?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;If you like blogging like me, you probably post your content on multiple places. For example, I post my content on [&lt;span style=&#34;color:#f92672&#34;&gt;xebia.com&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;https://xebia.com/blog&lt;/span&gt;), [&lt;span style=&#34;color:#f92672&#34;&gt;conijn.io&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;https://conijn.io/blog/&lt;/span&gt;) and [&lt;span style=&#34;color:#f92672&#34;&gt;dev.to&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;https://dev.to/aws-builders&lt;/span&gt;). When you do not provide a canonical url search engines like Google are less likely to show your blog.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, you need to specify in the layout of your site where the canonical url used. I have placed it in the &lt;code&gt;baseof.html&lt;/code&gt; file of my layout as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ if .Params.canonicalUrl }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;canonical&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ .Params.canonicalUrl }}&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ else }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;canonical&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will point the page to itself if no canonical is set. But if it is set it points to the provided url.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion &lt;/h2&gt;
&lt;p&gt;Setting the canonical url can improve the likelihood of your page being found. And when you use Hugo it is easy to put in place!&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/man-wears-gray-crew-neck-shirt-painting-on-wall-1152188/&#34;&gt;Brett Sayles&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to update your repository from a gitlab pipeline</title>
      <link>https://conijn.io/blog/2023/04/how-to-update-your-repository-from-a-gitlab-pipeline/</link>
      <pubDate>Tue, 18 Apr 2023 11:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2023/04/how-to-update-your-repository-from-a-gitlab-pipeline/</guid>
      <description>&lt;p&gt;Did you ever ran into the scenario where you wanted to update files in a repository from your pipeline? I did, a couple of times actually!&lt;/p&gt;
&lt;h2 id=&#34;updating-documentation&#34;&gt;Updating documentation&lt;/h2&gt;
&lt;p&gt;In one of my previous blogs I wrote on how you can manage &lt;a href=&#34;https://conijn.io/blog/2022/06/diagrams-as-code/&#34;&gt;diagrams as code&lt;/a&gt;. Well as a human you sometimes forget to update images that you might be using in your README. So, updating these images during the CI process will help you with that.&lt;/p&gt;
&lt;h2 id=&#34;building-containers&#34;&gt;Building containers&lt;/h2&gt;
&lt;p&gt;There are several scenarios where you might want to do this. For example, you are building a container and you want to deploy this in your environment. The approaches that I often is that you tag the container as latest and trigger a redeployment.&lt;/p&gt;
&lt;p&gt;The problem with this approach is that you might already have a cached container on your host. With the result that you deploy the previous version. And what if you need to roll back to the previous version, for whatever reason? Or you want to know what version was running at a specific moment?&lt;/p&gt;
&lt;p&gt;You can solve this by pointing to the actual digest of the container. This will always deploy the given version, or it will rollback. And if you need to do a manual rollback? You can revert the digest to the previous value. And as a bonus you can use your git history as an audit log.&lt;/p&gt;
&lt;p&gt;If you want to know how you can update your CloudFormation templates. We created a tool called &lt;a href=&#34;https://github.com/binxio/cru&#34;&gt;cru&lt;/a&gt; that can help you with that!&lt;/p&gt;
&lt;h2 id=&#34;creating-a-commit-from-ci-on-gitlab&#34;&gt;Creating a commit from CI on GitLab.&lt;/h2&gt;
&lt;p&gt;To get this to work, you do need to do some configuration. You will need to generate a SSH Key pair:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh-keygen -t ed25519 -C &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;comment&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can add the public key in your repository under &lt;strong&gt;Settings&lt;/strong&gt; and then &lt;strong&gt;Repository&lt;/strong&gt;. In the &lt;strong&gt;Deploy keys&lt;/strong&gt; section you can add your public key. Please make sure you grant write permissions to this key.&lt;/p&gt;
&lt;p&gt;Under &lt;strong&gt;Settings&lt;/strong&gt; and then &lt;strong&gt;CI/CD&lt;/strong&gt; you will have the section &lt;strong&gt;Variables&lt;/strong&gt;. Here you will need to create a &lt;em&gt;File&lt;/em&gt; type variable called &lt;em&gt;DEPLOY_SSH_KEY&lt;/em&gt; as value use the private key.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/environment_variable.png&#34; alt=&#34;Example on how to fill the environment variable&#34;&gt;&lt;/p&gt;
&lt;p&gt;The private key will be available on the runner as a file. The location of the file is available as the &lt;em&gt;DEPLOY_SSH_KEY&lt;/em&gt; environment variable.&lt;/p&gt;
&lt;p&gt;Before you can commit you first need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure the SSH client.&lt;/li&gt;
&lt;li&gt;Configure the git user.&lt;/li&gt;
&lt;li&gt;Configure the git remote.&lt;/li&gt;
&lt;li&gt;Commit and push the changes if there are any&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To make it a bit easier I placed all those actions in the following bash script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;failed &lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed:&amp;#34;&lt;/span&gt; &amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$@&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; exit 1; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GIT_USER&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GIT_EMAIL&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GIT_COMMIT_MESSAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -z &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;CI_SERVER_HOST&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; failed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The environment variable CI_SERVER_HOST is not set, this is needed to configure git and ssh to be enable to write back to the repository.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -z &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;CI_COMMIT_REF_NAME&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; failed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The environment variable CI_COMMIT_REF_NAME is not set, this is needed to configure git and ssh to be enable to write back to the repository.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -z &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;DEPLOY_SSH_KEY&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; failed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The environment variable DEPLOY_SSH_KEY is not set, this is needed to configure git to be enable to write back to the repository.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; ! -f &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;DEPLOY_SSH_KEY&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; failed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;DEPLOY_SSH_KEY&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; is not available on the filesystem, this is needed to configure git to be enable to write back to the repository.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -z &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GIT_USER&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; failed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Please use: ./commit-changes.sh &amp;#39;&amp;lt;USER NAME&amp;gt;&amp;#39; &amp;#39;&amp;lt;EMAIL&amp;gt;&amp;#39; &amp;#39;&amp;lt;COMMIT MESSAGE&amp;gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -z &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GIT_EMAIL&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; failed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Please use: ./commit-changes.sh &amp;#39;&amp;lt;USER NAME&amp;gt;&amp;#39; &amp;#39;&amp;lt;EMAIL&amp;gt;&amp;#39; &amp;#39;&amp;lt;COMMIT MESSAGE&amp;gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -z &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GIT_COMMIT_MESSAGE&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; failed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Please use: ./commit-changes.sh &amp;#39;&amp;lt;USER NAME&amp;gt;&amp;#39; &amp;#39;&amp;lt;EMAIL&amp;gt;&amp;#39; &amp;#39;&amp;lt;COMMIT MESSAGE&amp;gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run_function &lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    GREEN&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\033[1;32m&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    RED&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\033[1;31m&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    NC&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\033[0m&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# No Color&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Running &lt;/span&gt;$1&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ERROR&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt; $1 2&amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; $? -eq &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; echo -e &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[ &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GREEN&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;DONE&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;NC&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; ]&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;echo -e &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[ &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;RED&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;FAILED&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;NC&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; ]&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; failed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Unexpected error occurred: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;ERROR&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;configure_git_client&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    git config user.name &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GIT_USER&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    git config user.email &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GIT_EMAIL&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cleanup_ssh_client&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rm -rf ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mkdir ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;configure_ssh_client&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    local KNOWN_HOSTS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;~/.ssh/known_hosts
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    local CONFIG_FILE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;~/.ssh/config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ssh-keyscan $CI_SERVER_HOST &amp;gt; $KNOWN_HOSTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    chmod &lt;span style=&#34;color:#ae81ff&#34;&gt;0644&lt;/span&gt; $KNOWN_HOSTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Host &lt;/span&gt;$CI_SERVER_HOST&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; $CONFIG_FILE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;    IdentityFile &lt;/span&gt;$DEPLOY_SSH_KEY&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; $CONFIG_FILE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    chmod &lt;span style=&#34;color:#ae81ff&#34;&gt;0600&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$DEPLOY_SSH_KEY&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;configure_git_remote &lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    git remote remove update 2&amp;gt;/dev/null &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;update remote was not present yet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    git remote add update git@$CI_SERVER_HOST:$CI_PROJECT_PATH.git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;commit_and_push_changes &lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    git fetch update
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    git branch -D $CI_COMMIT_REF_NAME &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;branch does not exist&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    git switch -c $CI_COMMIT_REF_NAME update/$CI_COMMIT_REF_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    git diff --quiet &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; git diff --staged --quiet &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;git commit -am &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GIT_COMMIT_MESSAGE&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push --set-upstream update $CI_COMMIT_REF_NAME&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run_function &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;configure_git_client&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run_function &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cleanup_ssh_client&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run_function &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;configure_ssh_client&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run_function &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;configure_git_remote&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run_function &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;commit_and_push_changes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can use it as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./commit-changes.sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GitLag CI&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;noreply@acme.com&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;chore: my commit message&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion &lt;/h2&gt;
&lt;p&gt;Committing from the a GitLab pipeline works the same as you would from your own machine. The difference is that you need to configure your runners everytime and you need to grant the access to do so.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/glass-with-aqua-and-spreading-paint-flows-6402528/&#34;&gt;Jill Burrow&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Cross account DNS management on AWS</title>
      <link>https://conijn.io/blog/2023/03/cross-account-dns-management-on-aws/</link>
      <pubDate>Thu, 30 Mar 2023 11:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2023/03/cross-account-dns-management-on-aws/</guid>
      <description>&lt;p&gt;I recently created a few blog post on how you can build, deploy and host an application on AWS. This blog post is all about handling DNS records in a cross account setup. You might think AWS got you covered, but the sad reality is that they don&amp;rsquo;t. (when dealing with cross account use cases.)&lt;/p&gt;
&lt;p&gt;For example, if you have a domain called example.com. You will need to create the hosted zone in an AWS Account. Then from the context of that account you are able to manage the records through infrastructure as code. This will work until your using more than 1 AWS Account.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;Because the &lt;a href=&#34;https://aws.amazon.com/route53/&#34;&gt;Route53&lt;/a&gt; hosted zone lives within a specific account. You cannot add records to the hosted zone from another account. So there are 3 options how you can solve this problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Option 1: Manage your DNS records by hand.&lt;/li&gt;
&lt;li&gt;Option 2: Create a Route 53 hosted zone in each AWS Account. You will need entire different domains or subdomains to get this working.&lt;/li&gt;
&lt;li&gt;Option 3: Use the &lt;a href=&#34;https://github.com/binxio/cfn-cross-account-dns-provider&#34;&gt;cfn-cross-account-dns-provider&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So Option 1, I like to use infrastructure as code. So this is not an option. And for Option 2 you will need, either a different domain. Or you will need to use subdomains. Using a different domain is a bit of an overkill. So when you use subdomains you will need a hosted zone in every account. A single hosted zone will cost you $ 6 a year. Let say you have 3 workloads and 4 environments per workload. Now you pay $ 72 per year only for the hosted zone. This might be an option for your use case but not for my personal pet projects.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/thinking-of-a-solution-small.jpg&#34; alt=&#34;Thinking of a solution&#34;&gt;&lt;/p&gt;
&lt;p&gt;So that brings us to option 3. In a nutshell its my solution to option 2.&lt;/p&gt;
&lt;h2 id=&#34;my-solution&#34;&gt;My solution&lt;/h2&gt;
&lt;p&gt;I created a single AWS Account that I am using for DNS management. All the domains that I own are in this account, and are using &lt;a href=&#34;https://aws.amazon.com/route53/&#34;&gt;Route53&lt;/a&gt;. I also deployed the &lt;a href=&#34;https://github.com/binxio/cfn-cross-account-dns-provider&#34;&gt;cfn-cross-account-dns-provider&lt;/a&gt; in this account. This provider comes with a &lt;a href=&#34;https://aws.amazon.com/sns/&#34;&gt;SNS Topic&lt;/a&gt; and all member accounts are allowed to publish to this topic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/throwing-a-record-over-the-fence-small.jpg&#34; alt=&#34;Throwing dns record over the fence&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now you can use the resource definition in &lt;a href=&#34;https://aws.amazon.com/cloudformation/&#34;&gt;CloudFormation&lt;/a&gt; to create DNS records. It will use the SNS topic to invoke the Custom Cross Account DNS Provider in the AWS account that have the hosted zones. The provider will manage the records for you and will notify &lt;a href=&#34;https://aws.amazon.com/cloudformation/&#34;&gt;CloudFormation&lt;/a&gt; with the end result.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;DNSRecord&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Custom::CrossAccountDNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ServiceToken&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:sns:${AWS::Region}:${HostedZoneAccountId}:binxio-cfn-cross-account-dns-provider&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;HostedZoneId&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref HostedZoneId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysubdomain.example.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CNAME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Value&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;my-value-from-cloudfront-for-example.amazon.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cool, so now that we can create DNS records cross account you can do more fun stuff. For example, you also need DNS records for certificates that you request through &lt;a href=&#34;https://aws.amazon.com/certificate-manager/&#34;&gt;ACM&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;When you are using &lt;a href=&#34;https://aws.amazon.com/cloudfront/&#34;&gt;CloudFront&lt;/a&gt; the certificate needs to exist in the us-east-1 region. This will become a problem when your application lives in a different region. For this Mark van Holsteijn created the &lt;a href=&#34;https://github.com/binxio/cfn-certificate-provider&#34;&gt;cfn-certificate-provider&lt;/a&gt;. With this provider you can request a certificate in the us-east-1 region. While your stack lives in the eu-west-1 region. When you combine this with the &lt;a href=&#34;https://github.com/binxio/cfn-cross-account-dns-provider&#34;&gt;cfn-cross-account-dns-provider&lt;/a&gt; your certificates will be issued automatically.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can manage certificates and DNS records  across account and region. All you need is the &lt;a href=&#34;https://github.com/binxio/cfn-certificate-provider&#34;&gt;cfn-certificate-provider&lt;/a&gt; and the &lt;a href=&#34;https://github.com/binxio/cfn-cross-account-dns-provider&#34;&gt;cfn-cross-account-dns-provider&lt;/a&gt;. This will remove any manual steps from your deployment making them more predictable and reliable.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/pedestrian-crossing-the-street-2329811/&#34;&gt;BOA.vision&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Deploy S3 hosted application using CodePipeline</title>
      <link>https://conijn.io/blog/2023/03/deploy-s3-hosted-application-using-codepipeline/</link>
      <pubDate>Tue, 14 Mar 2023 11:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2023/03/deploy-s3-hosted-application-using-codepipeline/</guid>
      <description>&lt;p&gt;A while ago I wrote how you can host your own &lt;a href=&#34;https://xebia.com/blog/hosting-a-single-page-application-or-website-on-s3/&#34;&gt;single page application on S3&lt;/a&gt;. But how will you get your application on the S3 bucket? There are a couple of options here, you could upload it by hand? But we both know that is not the real solution here. No we want to automate this process! In this blog post I will show you how you can automate this using &lt;a href=&#34;https://aws.amazon.com/codepipeline/&#34;&gt;AWS CodePipeline&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-pipeline&#34;&gt;The Pipeline&lt;/h2&gt;
&lt;p&gt;AWS Codepipeline uses different stages, I often use &lt;code&gt;Source&lt;/code&gt;, &lt;code&gt;Build&lt;/code&gt; and &lt;code&gt;Deploy&lt;/code&gt; stages. In some cases I split the Deploy stage into a Development, Testing, Acceptance and Production deployment (also known as DTAP). If you want to know more about how you can set this up you can read my &lt;a href=&#34;https://conijn.io/blog/2023/01/building-applications-with-pipelines/&#34;&gt;building applications with pipelines&lt;/a&gt; blog. But in the end it is up to you and what makes sense to your use-case.&lt;/p&gt;
&lt;p&gt;When you deploy your infrastructure using &lt;a href=&#34;https://aws.amazon.com/cloudformation/&#34;&gt;CloudFormation.&lt;/a&gt; You can make use of the outputs within the CodePipeline. Another option is to use a naming convention. I like to use the outputs as it removes the need to define a name upfront. Making it more robust when you re-use snippets or deploy your infrastructure more than once.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Outputs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ApplicationBucketName&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Value&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref ApplicationBucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The next thing you need to define is a namespace on the action that deploys your infrastructure.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ExecuteChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${DevelopmentAccountId}:role/cross-account-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;DevelopmentVariables&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_EXECUTE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${DevelopmentAccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-development&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-development-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default CodePipeline will load the outputs in the given namespace. In this example that is &lt;code&gt;DevelopmentVariables&lt;/code&gt;, so the &lt;code&gt;ApplicationBucketName&lt;/code&gt; is available as: &lt;code&gt;#{DevelopmentVariables.ApplicationBucketName}&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;deploy-to-s3&#34;&gt;Deploy to S3&lt;/h2&gt;
&lt;p&gt;AWS provides a &lt;a href=&#34;https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-S3Deploy.html&#34;&gt;S3 Deploy action&lt;/a&gt; you can use this action to deploy an artifact in your pipeline to S3. You can create this artifact in a  CodeBuild Project or you can use the source artifact. &lt;/p&gt;
&lt;p&gt;I am using a cross account deployment strategy. For this reason I need to allow my &lt;em&gt;cross-account-role&lt;/em&gt; to allow uploads to the S3 buckets. I am using a BucketPolicy for this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;Sid&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AllowPipelineToUpload&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:PutObject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;AWS&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${AWS::AccountId}:role/cross-account-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ApplicationBucket.Arn}/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the role and the bucket are living in the same account. The pipeline lives in my build/deployment account. So in the pipeline we need to configure the upload to S3:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Ireland-Uploadapplication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${DevelopmentAccountId}:role/cross-account-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;InputArtifacts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;application&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;S3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;BucketName&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#{DevelopmentVariables.ApplicationBucketName}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Extract&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;CacheControl&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;max-age=0, no-cache, no-store, must-revalidate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example I will use my artifact called &lt;em&gt;application&lt;/em&gt; and extract the content in the S3 bucket. It will assume the role that we specify as &lt;code&gt;RoleArn&lt;/code&gt; to perform the upload. I will also set the &lt;code&gt;CacheControl&lt;/code&gt; so that &lt;a href=&#34;https://aws.amazon.com/cloudfront/&#34;&gt;CloudFront&lt;/a&gt; knows that it needs to serve the new content.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It is easy to use the S3 Deploy action to upload your content to a S3 bucket. It removes the need of using a CodeBuild project to upload the content. This will reduce cost and complexity, by not maintaining an extra CodeBuild project.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Cross account deployments using a Customer Managed KMS key</title>
      <link>https://conijn.io/blog/2023/02/cross-account-deployments-using-a-customer-managed-kms-key/</link>
      <pubDate>Mon, 27 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2023/02/cross-account-deployments-using-a-customer-managed-kms-key/</guid>
      <description>&lt;p&gt;In my previous &lt;a href=&#34;https://conijn.io/blog/2023/01/building-applications-with-pipelines/&#34;&gt;blog&lt;/a&gt; I wrote about application pipelines. These CodePipeline use a S3 bucket. What if you have a need for a customer managed key instead of the Amazon managed key? And you want to deploy the CloudFormation templates cross account? In this blog I will explain how you can achieve this.&lt;/p&gt;
&lt;h2 id=&#34;the-key-itself&#34;&gt;The key itself&lt;/h2&gt;
&lt;p&gt;Lets start with the key itself. When CodePipeline moves artifacts from one stage to another it uses S3 to transfer the data. For this reason we need to allow S3 and CodePipeline to decrypt and encrypt the data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#ae81ff&#34;&gt;kms:Decrypt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#ae81ff&#34;&gt;kms:DescribeKey&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#ae81ff&#34;&gt;kms:Encrypt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#ae81ff&#34;&gt;kms:ReEncrypt*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#ae81ff&#34;&gt;kms:GenerateDataKey*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;AWS&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Condition&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;StringEquals&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;aws:PrincipalOrgID&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref OrgId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;kms:ViaService&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;Fn::Sub&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;s3.${AWS::Region}.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;Fn::Sub&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;codepipeline.${AWS::Region}.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, CloudFormation will deploy these templates across account. For this reason we need to allow CloudFormation to decrypt the data.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kms:Decrypt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;AWS&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Condition&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;StringEquals&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;aws:PrincipalOrgID&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref OrgId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;kms:ViaService&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub cloudformation.${AWS::Region}.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The samples shown here will limit the use of the key to the AWS Services within your own AWS organization. The S3 bucket needs to use the KMS key for encryption.&lt;/p&gt;
&lt;h2 id=&#34;the-pipeline&#34;&gt;The pipeline&lt;/h2&gt;
&lt;p&gt;The CodePipeline needs to know that we are using KMS as an encryption method. For this reason we need to tell what key to use.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Pipeline&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::CodePipeline::Pipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ArtifactStores&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ArtifactStore&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Location&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub codepipeline-artifacts-${AWS::AccountId}-eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;S3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;EncryptionKey&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;KMS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;alias/bucket-key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-central-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ArtifactStore&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Location&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub codepipeline-artifacts-${AWS::AccountId}-eu-central-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;S3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;EncryptionKey&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;KMS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;alias/bucket-key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are using the alias so that we do not have to deal with the key id.&lt;/p&gt;
&lt;h2 id=&#34;deploy-cross-accountregion&#34;&gt;Deploy cross account/region&lt;/h2&gt;
&lt;p&gt;You might have seen it already in the previous code snippet. When you deploy across region you need to configure a bucket and key per region. When you deploy across account you will need 2 roles.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A role to assume in the other account. (cross-account-role, configured on action level)&lt;/li&gt;
&lt;li&gt;A role to use for the CloudFormation execution. (cloudformation-execution-role, configured in the configuration)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Actions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Ireland-CreateChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${DevelopmentAccountId}:role/cross-account-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;InputArtifacts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_REPLACE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${DevelopmentAccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-$development&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-development-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;TemplatePath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip::packaged-template.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Capabilities&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CAPABILITY_NAMED_IAM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ParameterOverrides&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          &amp;#34;EnvType&amp;#34;: &amp;#34;development&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Ireland-ExecuteChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${DevelopmentAccountId}:role/cross-account-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_EXECUTE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${DevelopmentAccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-development&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-development-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The cross-account-role needs the ability to perform crud operations on CloudFormation. Have access to the templates and the ability to pass the cloudformation-execution-role. The &lt;code&gt;BuildAccountId&lt;/code&gt; is the account id that holds the KMS keys, S3 buckets and pipeline. This role needs to be able to access the S3 buckets and use the KMS key for decryption.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;CrossAccountRole&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;RoleName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cross-account-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;AssumeRolePolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;2012-10-17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sts:AssumeRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;AWS&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${BuildAccountId}:root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Policies&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;PolicyName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AllowCloudFormationDeployments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;2012-10-17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;iam:PassRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:DescribeStacks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:cloudformation:eu-west-1:${AWS::AccountId}:stack/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:cloudformation:eu-central-1:${AWS::AccountId}:stack/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:CreateStack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:DeleteStack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:UpdateStack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:CreateChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:ExecuteChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:DeleteChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:DescribeChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:SetStackPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:SetStackPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation:ValidateTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:cloudformation:eu-west-1:${AWS::AccountId}:stack/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:cloudformation:eu-central-1:${AWS::AccountId}:stack/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;PolicyName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AllowArtifactAccess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;2012-10-17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:GetObject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:GetObjectVersion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${BuildAccountId}-eu-west-1/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${BuildAccountId}-eu-central-1/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kms:Decrypt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:kms:eu-west-1:${BuildAccountId}:key/02d730ed-125b-4d2e-a498-7b0187298924&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:kms:eu-central-1:${BuildAccountId}:key/e127b617-6951-42b7-83ae-12acc16d12a7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The execution role has all the needed rights to deploy the actual template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;CloudFormationServiceRole&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;RoleName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;AssumeRolePolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;2012-10-17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sts:AssumeRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;Service&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation.amazonaws.com }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ManagedPolicyArns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When using customer managed keys you need some extra configuration. The key is self, configuration where is is used and some logical permissions. Getting all this from the AWS documentation can be challenging as it is somewhat of an edge case.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Building applications with pipelines</title>
      <link>https://conijn.io/blog/2023/01/building-applications-with-pipelines/</link>
      <pubDate>Mon, 30 Jan 2023 11:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2023/01/building-applications-with-pipelines/</guid>
      <description>&lt;p&gt;In larger organizations a lot of things are in place. Think of things like deployment pipelines for your applications. But how do you set those up using the services AWS provides? In this blog post I will guide you to how I do it for my pet projects.&lt;/p&gt;
&lt;p&gt;I am a big fan of CloudFormation so the examples I will show are CloudFormation snippets.&lt;/p&gt;
&lt;h2 id=&#34;the-git-repository&#34;&gt;The git repository&lt;/h2&gt;
&lt;p&gt;I am using &lt;a href=&#34;https://aws.amazon.com/codecommit/&#34;&gt;AWS CodeCommit&lt;/a&gt; as a source repository. The reason for this choice is that it integrates with &lt;a href=&#34;https://aws.amazon.com/codepipeline/&#34;&gt;AWS CodePipeline&lt;/a&gt;. We also want the ability to test our infrastructure. So I usually use feature branches to do that. Here the first challange appears. We only need 1 repository and a pipeline to deploy our infrastructure. But we also need a pipeline that deploys our feature branch. In CloudFormation you can do this using conditions.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Parameters&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;FeatureGitBranch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Default&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ProjectName&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Default&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Conditions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;IsMainBranchPipeline&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Equals [!Ref FeatureGitBranch, &amp;#34;&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;IsFeatureBranchPipeline: !Not [Condition&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;IsMainBranchPipeline]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By providing a FeatureGitBranch parameter we can now make some choices. For example we could only create the repository in the production pipeline.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;PipelineRepo&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Condition&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;IsMainBranchPipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::CodeCommit::Repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;RepositoryName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref ProjectName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;RepositoryDescription&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;&amp;lt;repository-description&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-pipeline-requirements&#34;&gt;The pipeline requirements&lt;/h2&gt;
&lt;p&gt;The pipeline uses a couple of resources. You can share these resources across your pipelines. So they are in a separate template and I deploy these resources once per region that I am using.&lt;/p&gt;
&lt;p&gt;First you will need a S3 bucket to store the artifacts created by each stage. &lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;ArtifactsBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;DeletionPolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Retain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;UpdateReplacePolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Retain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;BucketName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub codepipeline-artifacts-${AWS::AccountId}-${AWS::Region}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;VersioningConfiguration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Status&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Enabled&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionConfiguration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionByDefault&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;SSEAlgorithm&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AES256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I am using a predictable bucket name so I can use it in my pipeline. Because bucket names need to have a globally unique name I also include the account id and region. &lt;/p&gt;
&lt;p&gt;You will also need an IAM role. You will use this role in your pipeline to deploy your CloudFormation template.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;CloudFormationServiceRole&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;RoleName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub cloudformation-execution-role-${AWS::Region}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;AssumeRolePolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sts:AssumeRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;Service&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cloudformation.amazonaws.com }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;ManagedPolicyArns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     - &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::aws:policy/AdministratorAccess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again I use a predictable name and I included the region. Since role names are unique in your account you can deploy this in every region. For the sake of simplicity I used the AdministratorAccess managed policy. You might want to limit this for your own scope.&lt;/p&gt;
&lt;h2 id=&#34;the-pipeline&#34;&gt;The pipeline&lt;/h2&gt;
&lt;p&gt;The pipeline itself also needs a role. This role can pass the CloudFormation role and use the buckets created in the previous step.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;CodePipelineExecutionRole&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;AssumeRolePolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sts:AssumeRole&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Service&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - &lt;span style=&#34;color:#ae81ff&#34;&gt;codepipeline.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Policies&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;PolicyName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CodePipelineAccess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;iam:PassRole&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub cloudformation-execution-role-${AWS::Region}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;PolicyName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CodePipelineCodeAndS3Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:GetBucketAcl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:GetBucketLocation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${AWS::AccountId}-eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${AWS::AccountId}-eu-central-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:GetObject&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:GetObjectVersion&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:PutObject&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${AWS::AccountId}-eu-west-1/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${AWS::AccountId}-eu-central-1/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;PolicyName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CodeCommitAccess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;codecommit:GitPull&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;codecommit:GetBranch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;codecommit:GetCommit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;codecommit:UploadArchive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#ae81ff&#34;&gt;codecommit:GetUploadArchiveStatus&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${ProjectName}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${ProjectName}/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;PolicyName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CodePipelineCodeBuildAndCloudformationAccess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;codebuild:StartBuild&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;codebuild:BatchGetBuilds&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt CodeBuildProjectBuildAndPackage.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:DescribeStacks&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub &amp;#34;arn:${AWS::Partition}:cloudformation:eu-*:${AWS::AccountId}:stack/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:CreateStack&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:DeleteStack&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:UpdateStack&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:CreateChangeSet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:ExecuteChangeSet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:DeleteChangeSet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:DescribeChangeSet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:SetStackPolicy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:SetStackPolicy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudformation:ValidateTemplate&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub &amp;#34;arn:${AWS::Partition}:cloudformation:eu-*:${AWS::AccountId}:stack/${ProjectName}-*/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see in the snippet the role is also  allowed to start a CodeBuild project. And it can deploy a CloudFormation template.
You might have noticed that I used 2 regions in this role. This is because in this particular example I have a pipeline that uses 2 regions.&lt;/p&gt;
&lt;p&gt;Because we use 2 regions we also need 2 artifact stores.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Pipeline&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::CodePipeline::Pipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ArtifactStores&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ArtifactStore&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Location&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub codepipeline-artifacts-${AWS::AccountId}-eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;S3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-central-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ArtifactStore&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Location&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub codepipeline-artifacts-${AWS::AccountId}-eu-central-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;S3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt CodePipelineExecutionRole.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;RestartExecutionOnUpdate&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Stages&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Actions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;SourceCodeRepo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CodeCommit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;PollForSourceChanges&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;RepositoryName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref ProjectName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;BranchName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;If [IsFeatureBranchPipeline, !Ref FeatureGitBranch, &amp;#34;main&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;OutputArtifacts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;SourceCodeAsZip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Depending on the IsFeatureBranchPipeline condition we select the correct branch for this pipeline. Depending on your project you might need a build step. I use CodeBuild to run unit tests and build and package any resources needed.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildAndPackage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Actions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CodeBuild&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CodeBuild&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;ProjectName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref CodeBuildProjectBuildAndPackage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;InputArtifacts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;SourceCodeAsZip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;OutputArtifacts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I did not include the CodeBuild project and IAM role snippet for CodeBuild. I will do a blog post on that in the future.&lt;/p&gt;
&lt;p&gt;So lets go over to the fun part. The next stage is the actual deployment of the CloudFormation template. I am using 2 regions and I only want to deploy to these 2 regions for my production environment. I also have different parameters for my production environment. Again, I am using conditions to control this behaviour.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - !&lt;span style=&#34;color:#ae81ff&#34;&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#ae81ff&#34;&gt;IsMainBranchPipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Actions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Ireland-CreateChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;InputArtifacts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_REPLACE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-production-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;TemplatePath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip::packaged-template.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Capabilities&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CAPABILITY_NAMED_IAM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ParameterOverrides&lt;/span&gt;: |-&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                      {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                        &amp;#34;EnvType&amp;#34;: &amp;#34;production&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                      }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Frankfurt-CreateChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-central-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;InputArtifacts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_REPLACE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${AWS::AccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-production-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;TemplatePath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip::packaged-template.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Capabilities&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CAPABILITY_NAMED_IAM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ParameterOverrides&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                      {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                        &amp;#34;EnvType&amp;#34;: &amp;#34;production&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                      }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Ireland-ExecuteChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_EXECUTE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-production-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Frankfurt-ExecuteChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-central-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_EXECUTE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:aws:iam::${AWS::AccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-production-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref AWS::NoValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the 1 action I create a change set. In the second action I execute it. Please note the RoleArn used here. You could also specify a role from a different account. This allows you to use a separate account for different environments. For example you could host the pipeline in a deployment account. And have separate development, testing, acceptance and production accounts.&lt;/p&gt;
&lt;h2 id=&#34;the-feature-branch&#34;&gt;The feature branch&lt;/h2&gt;
&lt;p&gt;So we now have a working pipeline for the production environment. How about the feature branch? For this we use again the conditions.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - !&lt;span style=&#34;color:#ae81ff&#34;&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#ae81ff&#34;&gt;IsFeatureBranchPipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;FeatureDevelopment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Actions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Ireland-CreateChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;InputArtifacts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_REPLACE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-${FeatureName}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-feature-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;TemplatePath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BuildArtifactAsZip::packaged-template.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Capabilities&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CAPABILITY_NAMED_IAM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ParameterOverrides&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                      {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                        &amp;#34;EnvType&amp;#34;: &amp;#34;development&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                      }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Ireland-ExecuteChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;RunOrder&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;eu-west-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;ActionTypeId&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Category&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Owner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CloudFormation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;Configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ActionMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CHANGE_SET_EXECUTE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;StackName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-${FeatureName}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;ChangeSetName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${ProjectName}-feature-ChangeSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref AWS::NoValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, the feature branch is only deployed to the eu-west-1 region. And it has set the EnvType parameter to development.&lt;/p&gt;
&lt;h2 id=&#34;pickup-new-commits&#34;&gt;Pickup new commits&lt;/h2&gt;
&lt;p&gt;When you push your commits to the CodeCommit repository. You can use an event rule to trigger a new pipeline execution. To make this happen we need an IAM role and the rule itself.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;CloudWatchEventRole&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::IAM::Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;AssumeRolePolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2012-10-17&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;Service&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;events.amazonaws.com }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sts:AssumeRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Policies&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       - &lt;span style=&#34;color:#f92672&#34;&gt;PolicyName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2012-10-17&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;codepipeline:StartPipelineExecution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;CloudWatchEventRule&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Events::Rule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Check CodeCommit Repo Changes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;EventPattern&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;detail-type&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CodeCommit Repository State Change&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;aws.codecommit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${ProjectName}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;detail&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;event&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;anything-but&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#ae81ff&#34;&gt;referenceDeleted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;referenceType&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#ae81ff&#34;&gt;branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;referenceName&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - !&lt;span style=&#34;color:#ae81ff&#34;&gt;If [IsFeatureBranchPipeline, !Ref FeatureGitBranch, &amp;#34;main&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Targets&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;Id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;codepipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Arn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;RoleArn&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${CloudWatchEventRole.Arn}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are triggering on every event except when the branch removal. &lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion &lt;/h2&gt;
&lt;p&gt;You can store the template for the pipeline in your CodeCommit repository. When you create a feature branch you can deploy that template specifying the branch name. The pipeline can then deploy the project CloudFormation template. And with every commit that you push your feature environment will receive an update.&lt;/p&gt;
&lt;p&gt;With this setup you have everything in a single repository. (Except for the shared S3 bucket and IAM role.) Having everything in a single repository makes it easier to maintain.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use EventBridge to handle API requests</title>
      <link>https://conijn.io/blog/2023/01/use-eventbridge-to-handle-api-requests/</link>
      <pubDate>Tue, 03 Jan 2023 12:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2023/01/use-eventbridge-to-handle-api-requests/</guid>
      <description>&lt;p&gt;At AWS re:Invent I attended &lt;a href=&#34;https://www.youtube.com/watch?v=qs0U0LdNkV0&#34;&gt;SVS312 Building Serverlesspresso&lt;/a&gt;. This talk was about how you can create event-driven architectures. I am a fan of event-driven architectures. So I wanted to highlight a interesting technique using &lt;a href=&#34;https://aws.amazon.com/eventbridge/&#34;&gt;EventBridge&lt;/a&gt; shown in this talk.&lt;/p&gt;
&lt;p&gt;In the past I used a similar technique that I talked about in an &lt;a href=&#34;https://www.meetup.com/aws-ams/events/268599805/&#34;&gt;AWS Meetup&lt;/a&gt;. Here I used an API Gateway to post the request to a SQS Queue. This is very useful for fire and forget patterns. Because you do not need to invoke a Lambda function to handle the request. The response to the user is faster and you do not need to pay for the Lambda invocation.&lt;/p&gt;
&lt;p&gt;But &lt;a href=&#34;https://www.linkedin.com/in/jamesbeswick/&#34;&gt;James Beswick&lt;/a&gt; showed a iteration on that pattern. Instead of posting your payload to a SQS Queue you can also create an event within &lt;a href=&#34;https://aws.amazon.com/eventbridge/&#34;&gt;EventBridge&lt;/a&gt;. The initial request can still be a fire and forget call, but James did something interesting here. &lt;/p&gt;
&lt;p&gt;Because the API request itself became an event, you are able to process the API request as an event. &lt;/p&gt;
&lt;h2 id=&#34;a-closer-look&#34;&gt;A closer look&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;All examples are coming from the actual &lt;a href=&#34;https://github.com/aws-samples/serverless-coffee/&#34;&gt;serverless-coffee&lt;/a&gt; repository from AWS&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;When you order a coffee a &lt;a href=&#34;https://github.com/aws-samples/serverless-coffee/blob/main/backends/4-order-processing/api.yaml&#34;&gt;POST&lt;/a&gt; call is executed. The API Gateway uses the &lt;code&gt;x-amazon-apigateway-integration&lt;/code&gt; extension. This enables you to pass the body of the request as the details of an EventBridge event.&lt;/p&gt;
&lt;p&gt;From here you can do all sorts of actions. For example, the &lt;a href=&#34;https://github.com/aws-samples/serverless-coffee/blob/main/backends/6-publisher-service/code/publishToIOTuserTopic.js&#34;&gt;serverless-coffee&lt;/a&gt; project used &lt;a href=&#34;https://aws.amazon.com/iot-core/&#34;&gt;IOT Core&lt;/a&gt;. With IOT Core you can notify the end-user with status updates. And notify the barista that what kind of coffee needs to be created.&lt;/p&gt;
&lt;p&gt;The advantage of this is that these two actions are completely decoupled from each other. In fact the initial request is decoupled from these two actions. This makes this pattern very extendable! You do not need to take into account what other services are there.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Using &lt;a href=&#34;https://aws.amazon.com/eventbridge/&#34;&gt;EventBridge&lt;/a&gt; to handle your API requests can be very powerful. But it does need you to poll for updates or use streamed updates using &lt;a href=&#34;https://aws.amazon.com/iot-core/&#34;&gt;IOT Core&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;When you see what kind of flexibility you get extendability it is worth it!&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/group-of-people-raise-their-hands-on-stadium-976866/&#34;&gt;Josh Sorenson&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t let security slow you down</title>
      <link>https://conijn.io/blog/2022/12/don-t-let-security-slow-you-down/</link>
      <pubDate>Fri, 16 Dec 2022 10:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2022/12/don-t-let-security-slow-you-down/</guid>
      <description>&lt;p&gt;In my previous &lt;a href=&#34;https://conijn.io/blog/2022/12/embed-security-in-your-teams/&#34;&gt;blog&lt;/a&gt; I wrote on how AWS embeds security in their teams. &lt;a href=&#34;https://www.linkedin.com/in/jorgeliauw/&#34;&gt;Jorge Liauw Calo&lt;/a&gt; challenged me to tell me a bit more on my security vision. So why not do another blog post ;-).&lt;/p&gt;
&lt;p&gt;Some background, I have been working with AWS since 2009. So most of my opinions are formed by the input from AWS. This is not a bad thing because in general AWS is doing a great job developing safe and secure services. At AWS re:Invent 2022 I also heard the following quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Trust but verify!&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I think that sums up my personal take on security as well. I mixed the input from AWS with my own experience as a developer and topped it off with my consulting experience.&lt;/p&gt;
&lt;h2 id=&#34;getting-blocked&#34;&gt;Getting blocked&lt;/h2&gt;
&lt;p&gt;Traditional security departments tent to oppose al sorts of rules and frameworks on the development teams. The problem with this approach is that there are more developers than security engineers. This means that the security engineers do not have the time to guide the developers. The end result is that your workload cannot go into production because security would block you. And to be fair, they have good reasons to do so!&lt;/p&gt;
&lt;p&gt;It is in the interest of the developer to embed security in your requirements. Based on those requirements you make sure you incorporate the security aspect in your design. Afterwards you can build your solution still with security in mind. By doing this you are not only lowering the risk of being blocked by a security team. You are also preventing the need of fixing the security issues after the fact. The effort that is needed to fix certain security issues is higher when you already build your solution than during the requirement and design phase of that same solution.&lt;/p&gt;
&lt;p&gt;You should not rely on the control mechanisms opposed by the security team. You need to think for your self. Your team knows what your solution is doing and therefor you are the perfect candidate to secure it. In general those security team controls will work but in specific cases they are not enough.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/false-security.jpg&#34; alt=&#34;False Security&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can use a lock, but you need to use it correctly to have effect!&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;
&lt;p&gt;When developing your solutions you will have to deal with dependencies. These dependencies might have vulnerabilities in them. Often you see that a vulnerability becomes a hot topic and the urgency goes up. It becomes something that needs to be fixed NOW!&lt;/p&gt;
&lt;p&gt;Having tools in place to detect and notify, will reduce problems in the future. This allows you to prioritize, plan and test the mitigation. Instead of rushing a fix and hoping nothing will break. When these things happen it&amp;rsquo;s not only the time that you spend on the actual fix. You also need to mobilize your teams on the spot. This will pull them out of there current task wasting more and more time.&lt;/p&gt;
&lt;p&gt;By enabling the developers to proactively update their dependencies. They will gain control over this process and they are less likely to get distracted by urgent required fixes. This is good for the team morale and the development velocity.&lt;/p&gt;
&lt;p&gt;By showing developers what the added value is for them. It is more likely that they will start adopting these security principles. At the same time you will improve your security posture, and increase your development velocity!&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you also focus on security during the requirement and design phase, you will safe time in the future! This time can be used to develop new features improving your developing velocity while improving your security posture.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Embed security in your teams</title>
      <link>https://conijn.io/blog/2022/12/embed-security-in-your-teams/</link>
      <pubDate>Wed, 07 Dec 2022 20:00:01 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2022/12/embed-security-in-your-teams/</guid>
      <description>&lt;p&gt;During AWS re:Invent I attended the &lt;code&gt;SEC322: Transform builder velocity with security&lt;/code&gt; chalk talk. I would have pointed you to the recording on YouTube. But unfortunately, chalk talks are not recorded! So instead you have to do it with my interpretation of the session.&lt;/p&gt;
&lt;p&gt;The title of the session was &lt;code&gt;Transform builder velocity with security&lt;/code&gt;. Most of you might recognize the feeling of being stopped by security when you want to go-live with your application. We tent to blame the security team at this point saying that they are the reason for the delay. The problem with this is that it has a huge impact on your development velocity. You are ready to deploy, but you can&amp;rsquo;t. And there is that potential risk that you need to go back to the drawing board because you made a fundamental mistake in your design.&lt;/p&gt;
&lt;p&gt;The solution seems strait forward. Include security earlier in the process. And yes, this will work in the beginning. But when your organization starts to grow you will face scaling issues in your security team.&lt;/p&gt;
&lt;p&gt;So, when you talk about scale you could use AWS itself as an example. They have a lot of teams and they seem to do a good job. And AWS always stated that there is a security person on each team. But I never heard how they actually do this. This all changed for me in this session.&lt;/p&gt;
&lt;h2 id=&#34;guardians&#34;&gt;Guardians&lt;/h2&gt;
&lt;p&gt;In this session &lt;a href=&#34;https://www.linkedin.com/in/collinpace/&#34;&gt;Collin Pace&lt;/a&gt; and &lt;a href=&#34;https://www.linkedin.com/in/anya-khlobystina/&#34;&gt;Anya Khlobystina&lt;/a&gt; explained that each team should have a guardian. The guardian will ask all relevant security questions to the team. So they will play the role of the security person on the team. So where is this guardian coming from? Well, from the team! In theory anyone can become a guardian.&lt;/p&gt;
&lt;p&gt;AWS relies on the ambitions of the individuals to fulfill this role. The reason behind this is simple. When you have someone how wants to do the job from ambition they are more invested versus an appointed person. Ambition persons bring that spark that drives them, that motivates them to be the best version of them selves.&lt;/p&gt;
&lt;p&gt;These guardians are supported by other more senior guardians. And there are training courses available to support the guardians in their journey.&lt;/p&gt;
&lt;p&gt;The whole idea of having these guardians in the actual teams is to identify potential security issues as soon as possible. In the design phase, but also in all the steps that follow. They know the workload that they are building so they have the best insights on why and how the workload is build.&lt;/p&gt;
&lt;h2 id=&#34;conflicting-interests&#34;&gt;Conflicting interests&lt;/h2&gt;
&lt;p&gt;So like in any organization you might have a conflict between the security team and a product owner that wants to ship.&lt;/p&gt;
&lt;p&gt;When working with guardians this shifts to the guardian. But a guardian can always ask the security team to confirm that the concern is legit. The only difference is that you are still in the design phase, or in the implementation phase. You are not ready for production yet, this is a big difference!&lt;/p&gt;
&lt;p&gt;The team is not stopped, only a specific area is being discussed. So the rest of the team can still continue on making sure you are ready for a release.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With the use of guardians you are embedding the security role in your teams. This will allow you to act faster on potential security risks. And prevent them from being build in the first time.&lt;/p&gt;
&lt;p&gt;The time that you save, will be used to develop your workload. And this will increase your building velocity!&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/city-man-people-woman-6670900/&#34;&gt;cottonbro studio&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Write AWS Config rules</title>
      <link>https://conijn.io/blog/2022/11/write-aws-config-rules/</link>
      <pubDate>Fri, 25 Nov 2022 11:00:00 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2022/11/write-aws-config-rules/</guid>
      <description>&lt;p&gt;AWS Config rules allow you to determine if a resource is compliant or not. Previously when you wanted to do custom checks you needed to write AWS Lambda functions to validate the configuration of a resource. Since &lt;a href=&#34;https://aws.amazon.com/about-aws/whats-new/2022/08/build-aws-config-rules-cloudformation-guard/&#34;&gt;Aug 2, 2022&lt;/a&gt; you have the ability to use &lt;a href=&#34;https://github.com/aws-cloudformation/cloudformation-guard&#34;&gt;cfn-guard&lt;/a&gt; rules to achieve the same.&lt;/p&gt;
&lt;h2 id=&#34;why-should-i-use-cfn-guard&#34;&gt;Why should I use cfn-guard&lt;/h2&gt;
&lt;p&gt;Writing a Lambda function is not difficult. But it becomes harder when you add unit testing. When you determine your compliancy status based on Lambda function, you want to ensure that it works as intended. This is the advantage of &lt;code&gt;cfn-guard&lt;/code&gt;, you write a rule and you write some tests. These tests are a couple of snippets of yaml and per snippet you define if your rule should: &lt;code&gt;PASS&lt;/code&gt;, &lt;code&gt;FAIL&lt;/code&gt; or &lt;code&gt;SKIP&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The other advantage of &lt;code&gt;cfn-guard&lt;/code&gt; is that you have a single way of writing rules. On Nov 8, 2022 I gave a talk on this topic on the &lt;a href=&#34;https://www.meetup.com/aws-ams/events/286638453/&#34;&gt;AWS User Group NL&lt;/a&gt;. In this talk I highlighted the differences between detective and preventive controls. Using &lt;code&gt;cfn-guard&lt;/code&gt; rules in AWS Config is a detective control. But the same language can be used to build preventive controls!&lt;/p&gt;
&lt;h2 id=&#34;yes-i-said-language-and-not-rules&#34;&gt;Yes, I said language and not rules&lt;/h2&gt;
&lt;p&gt;It turns out that the rules written for AWS Config cannot be used to validate AWS CloudFormation templates. They are similar but different!
Where an AWS Config rule has the following structure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1.3&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;arn&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:s3:::aws-meetup-2022-11-08-noncompliantbucket-xxxxxxxxxxxxx&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;resourceType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::S3::Bucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;resourceId&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aws-meetup-2022-11-08-noncompliantbucket-xxxxxxxxxxxxx&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;resourceName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aws-meetup-2022-11-08-noncompliantbucket-xxxxxxxxxxxxx&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;awsRegion&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eu-west-1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;supplementaryConfiguration&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ServerSideEncryptionConfiguration&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;rules&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;applyServerSideEncryptionByDefault&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;sseAlgorithm&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aws:kms&amp;#34;&lt;/span&gt;} }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A CloudFormation template has the following structure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resources&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;NonCompliantBucket&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::S3::Bucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Properties&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;BucketEncryption&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ServerSideEncryptionConfiguration&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ServerSideEncryptionByDefault&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;SSEAlgorithm&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aws:kms&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;OtherResource&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;...&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first thing that you can see is that the AWS Config rule only has the context of the resource it validates. While the CloudFormation template has one or more resources. So you can compare the to locations as followed:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;supplementaryConfiguration&lt;/code&gt; vs &lt;code&gt;Resources &amp;gt; NonCompliantBucket &amp;gt; Properties&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;But looking closer you also notice that the structure and naming varies. Meaning you are doomed to maintain 2 types of rules. 1 for AWS Config (Detective) and 1 for CloudFormation (Preventive).&lt;/p&gt;
&lt;h2 id=&#34;why-should-you-write-a-detective-and-a-preventive-rule&#34;&gt;Why should you write a detective and a preventive rule?&lt;/h2&gt;
&lt;p&gt;The answer is to decrease your feedback cycle. For example, when you only have a detective rule you need to wait until the everything is deployed. Then you need to wait until a compliance issue pops up. When you use a preventive rule you can check this on different levels:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can run the &lt;code&gt;cfn-guard&lt;/code&gt; rules at any time on your local development environment.&lt;/li&gt;
&lt;li&gt;You can use a &lt;code&gt;pre-commit&lt;/code&gt; hook, this will prevent you from committing non-compliant code.&lt;/li&gt;
&lt;li&gt;You can run it during your Continuous Integration step. This prevents merging non-compliant code in your develop/main branches.&lt;/li&gt;
&lt;li&gt;You can run it before deployment to testing, acceptance and/or production.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;cfn-guard&lt;/code&gt; is a great tool to write compliancy checks for both detective and preventive controls. Interested in a working sample? I published my demo material to the &lt;a href=&#34;https://github.com/Nr18/aws-meetup-2022-11-08&#34;&gt;Nr18/aws-meetup-2022-11-08&lt;/a&gt; GitHub repository.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/similar-cubes-with-rules-inscription-on-windowsill-in-building-4027658/&#34;&gt;Joshua Miranda&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Speedup python cli projects</title>
      <link>https://conijn.io/blog/2022/10/speedup-python-cli-projects/</link>
      <pubDate>Thu, 27 Oct 2022 11:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2022/10/speedup-python-cli-projects/</guid>
      <description>&lt;p&gt;Use our &lt;a href=&#34;https://github.com/binxio/python-cli-tool&#34;&gt;python-cli-tool&lt;/a&gt; &lt;a href=&#34;http://cookiecutter.io/&#34;&gt;cookiecutter&lt;/a&gt; project. Get an python cli project up and running in a few steps. In my previous &lt;a href=&#34;https://conijn.io/blog/2022/10/speedup-event-driven-projects/&#34;&gt;speedup-event-driven-projects&lt;/a&gt; blog. I wrote how you could use cookiecutter to spin up an event driven project for AWS. In this blog post I will show you how you could do this for python cli projects.&lt;/p&gt;
&lt;p&gt;For this blog post I assume that you have the following installed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;python3&lt;/li&gt;
&lt;li&gt;cookiecutter&lt;/li&gt;
&lt;li&gt;poetry&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;lets-create-an-cli-project&#34;&gt;Lets create an cli project&lt;/h2&gt;
&lt;p&gt;We will begin with executing the cookiecutter command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cookiecutter gh:binxio/python-cli-tool
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;./images/cookiecutter.png&#34; alt=&#34;Screenshot of the commands being executed&#34;&gt;&lt;/p&gt;
&lt;p&gt;Cookiecutter will create a folder based on the &lt;code&gt;project_name&lt;/code&gt;. In this folder you now only need to install the dependencies. You can use the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;install&lt;/code&gt; target of the &lt;a href=&#34;https://conijn.io/blog/2021/09/make-your-life-easier-using-makefiles/&#34;&gt;Makefile&lt;/a&gt; will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Perform a &lt;code&gt;poetry init&lt;/code&gt;, this will create the &lt;code&gt;pyproject.toml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Create git repository and install the &lt;a href=&#34;https://pre-commit.com&#34;&gt;pre-commit&lt;/a&gt; hooks.&lt;/li&gt;
&lt;li&gt;Perform a &lt;code&gt;poetry install&lt;/code&gt;. This will install all project dependencies based on the &lt;code&gt;pyproject.toml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Adds extra configuration to the &lt;code&gt;pyproject.toml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Commits everything to the git repository.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will end up with something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/tree-structure.png&#34; alt=&#34;Screenshot of the project tree structure&#34;&gt;&lt;/p&gt;
&lt;p&gt;The project itself has unit testing in-place:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/running-tests.png&#34; alt=&#34;Screenshot of the tests running&#34;&gt;&lt;/p&gt;
&lt;p&gt;And you can test your cli tool as well by executing the commands through poetry:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/cli-in-action.png&#34; alt=&#34;Screenshot of the CLI tool in action&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now it&amp;rsquo;s time to configure your remote &lt;a href=&#34;https://github.com&#34;&gt;GitHub&lt;/a&gt; repository:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git remote add origin git@github.com:binxio/my-example-cli.git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git branch -M main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push -u origin main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git checkout -b develop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push --set-upstream origin develop
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We pushed the &lt;code&gt;main&lt;/code&gt; branch to the remote and created a &lt;code&gt;develop&lt;/code&gt; branch as a working branch.&lt;/p&gt;
&lt;p&gt;The project itself contains the following workflows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Continuous Integration&lt;/strong&gt;, checks when you push to &lt;code&gt;main&lt;/code&gt; or open a pull request to &lt;code&gt;develop&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create Release&lt;/strong&gt;, creates an release for you when you merge to &lt;code&gt;main&lt;/code&gt; and uploads it to &lt;a href=&#34;https://pypi.org&#34;&gt;PyPi.org&lt;/a&gt;. (You need to configure a &lt;code&gt;PYPI_API_TOKEN&lt;/code&gt; secret in GitHub for this to work)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Release Notes&lt;/strong&gt;, creates a proposal for your release notes. This is based on your merged pull requests. So make sure you use correct &lt;a href=&#34;https://conijn.io/blog/2022/02/commit-messages-done-the-right-way/&#34;&gt;messages&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By using the &lt;a href=&#34;https://github.com/binxio/python-cli-tool&#34;&gt;python-cli-tool&lt;/a&gt; &lt;a href=&#34;http://cookiecutter.io/&#34;&gt;cookiecutter&lt;/a&gt; project you will speedup your python cli project!&lt;/p&gt;
&lt;p&gt;And you can focus on what matters! Your business logic!&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/carton-box-on-yellow-lines-of-an-asphalt-road-4744757/&#34;&gt;Ekaterina Belinskaya&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Speedup event driven projects</title>
      <link>https://conijn.io/blog/2022/10/speedup-event-driven-projects/</link>
      <pubDate>Tue, 11 Oct 2022 11:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2022/10/speedup-event-driven-projects/</guid>
      <description>&lt;p&gt;Use our &lt;a href=&#34;https://github.com/binxio/sam-eventbridge-project&#34;&gt;sam-eventbridge-project&lt;/a&gt; &lt;a href=&#34;http://cookiecutter.io/&#34;&gt;cookiecutter&lt;/a&gt; project. Get an event driven project up and running in a few steps. On the 3rd of October the &lt;a href=&#34;https://awscommunityday.nl&#34;&gt;AWS Community Day NL&lt;/a&gt; was held in Amersfoort. I gave a talk about CodeCommit and how you could use events to make your life easier.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/presentation-slide.png&#34; alt=&#34;Conclusion: Recognize a problem, find a trigger, and automate the wanted result!&#34;&gt;&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;hidden&amp;rdquo; message of the presentation was that you need to find a trigger. This trigger could be an event send to &lt;a href=&#34;https://aws.amazon.com/eventbridge/&#34;&gt;EventBridge&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most of the times when I start something new. I copy an other project and then change it to my likings. After I gave my presentation at the Community Day. I realized that that was a &amp;ldquo;problem&amp;rdquo;! So, I automated the wanted result.&lt;/p&gt;
&lt;p&gt;Now, when I want to start a new project I execute: (as a trigger)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cookiecutter gh:binxio/sam-eventbridge-project
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cookiecutter will then create the project structure for me. It contains the following things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://aws.amazon.com/serverless/sam/&#34;&gt;AWS SAM&lt;/a&gt;, used to build and deploy the CloudFormation stack.&lt;/li&gt;
&lt;li&gt;Sample implementation of a &lt;code&gt;CloudFormation Stack&lt;/code&gt; event&amp;rsquo;s.&lt;/li&gt;
&lt;li&gt;Unit tests, validates the sample implementation.&lt;/li&gt;
&lt;li&gt;Linting and formatting, keeps your code clean and follows style guides.&lt;/li&gt;
&lt;li&gt;Complexity scanning, prevents your code to become to complex.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://conijn.io/blog/2021/09/make-your-life-easier-using-makefiles/&#34;&gt;Makefile&lt;/a&gt; to simplify the usage of the project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By using the &lt;a href=&#34;https://github.com/binxio/sam-eventbridge-project&#34;&gt;sam-eventbridge-project&lt;/a&gt; &lt;a href=&#34;http://cookiecutter.io/&#34;&gt;cookiecutter&lt;/a&gt; project you will speedup your event driven projects!&lt;/p&gt;
&lt;p&gt;Read &lt;a href=&#34;https://conijn.io/blog/2022/10/speedup-python-cli-projects/&#34;&gt;speedup python cli projects&lt;/a&gt; to learn more&amp;hellip;&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/people-throwing-pins-433452/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to work with different browser profiles</title>
      <link>https://conijn.io/blog/2022/09/how-to-work-with-different-browser-profiles/</link>
      <pubDate>Thu, 22 Sep 2022 12:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2022/09/how-to-work-with-different-browser-profiles/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/johnste/finicky&#34;&gt;Finicky&lt;/a&gt; will help you when you use different browsers, and/or different profiles. As a consultant I often use browser profiles to split my clients. I have a &amp;ldquo;Binx.io&amp;rdquo; profile for all Binx related work. For each client I have corresponding profiles. Let&amp;rsquo;s call them &amp;ldquo;Client A&amp;rdquo; and &amp;ldquo;Client B&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/client_profiles.png&#34; alt=&#34;Example of the client profiles in Chrome&#34;&gt;&lt;/p&gt;
&lt;p&gt;Browser profiles let you manage bookmarks per profile. So you can keep all your related bookmarks together making them easier to find. The biggest advantage that I love is that the sessions are also separated. This means you can login with different users into the &lt;a href=&#34;https://console.aws.amazon.com/&#34;&gt;AWS Console&lt;/a&gt;. Allowing you to use 2 console sessions at the same time.&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;First you need to install &lt;a href=&#34;https://github.com/johnste/finicky&#34;&gt;Finicky&lt;/a&gt;. Then you configure your default browser to Finicky.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/default_browser.png&#34; alt=&#34;Example of your default browser configuration&#34;&gt;&lt;/p&gt;
&lt;p&gt;The last thing you need to do is to configure some logic. For this you need to create a &lt;code&gt;~/.finicky.js&lt;/code&gt; file. This file contains the logic when to select what browser and profile.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;module.exports&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;defaultBrowser:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Safari&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;handlers:&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;match:&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/(binx.io|xebia.com)/,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;browser:&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;name:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Google Chrome&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;profile:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Profile 1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;match:&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/(clientA.com)/,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;browser:&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;name:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Google Chrome&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;profile:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Profile 2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;match:&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/(clientB.com)/,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;browser:&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;name:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Google Chrome&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;profile:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Profile 3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you look at the code snippet. You will notice that the default browser is set to Safari.&lt;/p&gt;
&lt;p&gt;When I click on a link or I open a webpage on my machine. This logic kicks in. If the url matches &lt;a href=&#34;https://binx.io&#34;&gt;binx.io&lt;/a&gt; or &lt;a href=&#34;https://xebia.com/&#34;&gt;xebia.com&lt;/a&gt; it will use Chrome and &lt;code&gt;Profile 1&lt;/code&gt;. If the url contains &lt;code&gt;clientA.com&lt;/code&gt; it will use Chrome and &lt;code&gt;Profile 2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The profiles mentioned earlier &lt;code&gt;Client A&lt;/code&gt; and &lt;code&gt;Client B&lt;/code&gt; are the names. Unfortunately, Finicky only works with numbered profiles. So you need to do some testing what profile number is what profile name. But when you figured it out you are good to go!&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Finicky gives you the flexibility to choice your browser and profile. All based on logic that you control.&lt;/p&gt;
&lt;p&gt;And that combination is really powerful.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/portrait-of-a-woman-with-her-hands-on-her-head-9305436/&#34;&gt;Exelsius Adam&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use a help target in your Makefile</title>
      <link>https://conijn.io/blog/2022/09/implement-a-help-target-in-your-makefile/</link>
      <pubDate>Thu, 08 Sep 2022 16:30:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2022/09/implement-a-help-target-in-your-makefile/</guid>
      <description>&lt;p&gt;In one of my previous &lt;a href=&#34;https://conijn.io/blog/2021/09/make-your-life-easier-using-makefiles/&#34;&gt;blog&lt;/a&gt;. I wrote how you could make your life easier when you start using Makefile. But when you start using Makefile in many projects. The targets that you use may vary from project to project.&lt;/p&gt;
&lt;p&gt;In this blog post I will address a trick how you could write a &lt;code&gt;help&lt;/code&gt; target. Lets imagine that we have a project that allow you to &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;stop&lt;/code&gt; a docker container. These names are pretty straightforward. But without looking in the Makefile you never know for sure.&lt;/p&gt;
&lt;p&gt;By calling the &lt;code&gt;help&lt;/code&gt; target you could list all available targets:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make help
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;./images/make_help.png&#34; alt=&#34;All targets are listed with a help text&#34;&gt;&lt;/p&gt;
&lt;p&gt;When you have a look at the Makefile used for this blog post. It looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Makefile&#34; data-lang=&#34;Makefile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.DEFAULT_GOAL&lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt;help
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; help
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;help&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;## Display this help
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;info Example Makefile &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; my blog post&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;BEGIN {FS = &amp;#34;:.*##&amp;#34;; printf &amp;#34;\nUsage:\n  make \033[36m&amp;lt;target&amp;gt;\033[0m\n&amp;#34;} /^[a-zA-Z0-9_-]+:.*?##/ { printf &amp;#34;  \033[36m%-15s\033[0m %s\n&amp;#34;, $$1, $$2 } /^##@/ { printf &amp;#34;\n\033[1m%s\033[0m\n&amp;#34;, substr($$0, 5) } &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;MAKEFILE_LIST&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; build
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;build&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;## Build the container image
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;	docker build -t my-container:latest .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; stop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;stop&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;## Stop the container
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;	docker stop my-named-container &amp;gt; /dev/null 2&amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; True
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	docker rm my-named-container &amp;gt; /dev/null 2&amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; True
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; start
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; stop &lt;span style=&#34;color:#75715e&#34;&gt;## Start the container
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;	docker run --name my-named-container my-container:latest &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; shell
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;shell&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;## Start a shell session on the container
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;	docker run -it my-named-container:latest bash
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; logs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;logs&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;## Tail the logs of the running container
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;	docker logs my-named-container -f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;$(VERBOSE).SILENT&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You need to add the &lt;code&gt;help&lt;/code&gt; target. And for each target you need to supply a help text. And each target needs a prefix with &lt;code&gt;##&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;By adding a &lt;code&gt;help&lt;/code&gt; target to your Makefile you make it easier to use for others. But also for yourself because you don&amp;rsquo;t need to remember each target in the Makefile.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/man-s-hand-in-shallow-focus-and-grayscale-photography-167964/&#34;&gt;lalesh aldarwish&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Learning HashiCorp Terraform on AWS</title>
      <link>https://conijn.io/blog/2022/08/learning-terraform/</link>
      <pubDate>Mon, 22 Aug 2022 00:00:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2022/08/learning-terraform/</guid>
      <description>&lt;p&gt;I have been working as a consultant for over a year now. And I noticed that a lot of our clients are asking for experience on &lt;a href=&#34;https://www.hashicorp.com/products/terraform&#34;&gt;HashiCorp Terraform&lt;/a&gt; and AWS. So I figured it might be time to learn something new!&lt;/p&gt;
&lt;p&gt;So when I started to look at Terraform I never used it. What I did have is extensive knowledge of AWS. And like any other infrastructure as code. You need to know the cloud provider to actually build proper infrastructure. Tools like Terraform, CloudFormation and CDK are tools to build infrastructure. Those can be learned and you can switch between them depending on the project needs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/Terraform-AWS.jpg&#34; alt=&#34;HashiCorp Terraform on AWS&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;terraform-introduction&#34;&gt;Terraform introduction&lt;/h2&gt;
&lt;p&gt;So I started out with reaching out to my colleagues &lt;a href=&#34;https://xebia.com/blog/author/dean-shanahan/&#34;&gt;Dean Shanahan&lt;/a&gt; and &lt;a href=&#34;https://www.linkedin.com/in/bschaatsbergen/&#34;&gt;Bruno Schaatsbergen&lt;/a&gt;. They explained the basics on how Terraform works. Let me recap that for you in a short and simple story:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In Terraform you declare your infrastructure. The declarations are translated in API calls towards the cloud provider. So, if you know how to build infrastructure in your cloud provider. You know what is needed in Terraform, you only need to lookup the syntax.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So what is the difference between Terraform and CloudFormation then? When you remove the actual DSL (Domain Specific Language). Then the only difference is that CloudFormation manages the state for you. While in Terraform the state is managed by Terraform. Meaning you have the responsibility of storing it somewhere.&lt;/p&gt;
&lt;h2 id=&#34;so-what-is-this-state-that-i-mentioned&#34;&gt;So what is this state that I mentioned?&lt;/h2&gt;
&lt;p&gt;Imagine that we want to build a S3 bucket. When you do this using the AWS Console you follow the wizard. The console uses the &lt;a href=&#34;https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html&#34;&gt;CreateBucket&lt;/a&gt; API call to create the bucket. When you create a bucket using CloudFormation. You hand over a template to the CloudFormation service. The service reads the template. It checks if the bucket we added is already in the current version. Since we added it it is not, so CloudFormation now knows it needs to perform a &lt;a href=&#34;https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html&#34;&gt;CreateBucket&lt;/a&gt;. This lookup action is done against the state.&lt;/p&gt;
&lt;p&gt;The same principle applies to Terraform. When you add a resource that is not known in the state it will create it. When the resource is known in the state it will update it.&lt;/p&gt;
&lt;p&gt;When you are just playing around with Terraform you can have the state in a local file. But when you are dealing with teams and pipelines you do want your state to be stored somewhere central. You can do this using a S3 Bucket and a DynamoDB table. The S3 bucket will contain the state file. The DynamoDB table is used for state locking. The state locking prevents 2 deployments from happening at the same time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/terraform-remote-state.jpg&#34; alt=&#34;Remote state using a S3 Bucket and DynamoDB&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;learning-terraform&#34;&gt;Learning Terraform&lt;/h2&gt;
&lt;p&gt;I followed the &lt;a href=&#34;https://cloudacademy.com/learning-paths/terraform-associate-certification-exam-preparation-1-2814/&#34;&gt;Terraform Associate Certification Exam Preparation&lt;/a&gt; on Cloud Academy. But &lt;a href=&#34;https://acloudguru.com/course/hashicorp-certified-terraform-associate&#34;&gt;HashiCorp Certified Terraform Associate&lt;/a&gt; from A Cloud Guru also works. Or you can just play around reading the documentation.&lt;/p&gt;
&lt;p&gt;Within a couple of hours I already became comfortable enough to state. I know terraform! There are so many providers available nobody knows them all. You shouldn&amp;rsquo;t learn them by memory because the providers receive updates. So it&amp;rsquo;s always better to rely on the documentation than your memory.&lt;/p&gt;
&lt;p&gt;In the end when you join an existing team you need to learn what they are actually building. Once you know that you can have a look on how they did that in Terraform.&lt;/p&gt;
&lt;h2 id=&#34;using-terraform&#34;&gt;Using Terraform&lt;/h2&gt;
&lt;p&gt;So one thing that I really liked from Terraform. You can do pre and post actions when you create resources. This is super useful when for example you create an &lt;a href=&#34;https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html&#34;&gt;Elastic Container Registry&lt;/a&gt;. After you created the repository you can build a Docker image and upload it into the repository.&lt;/p&gt;
&lt;p&gt;But also when you delete resources. For example, when you delete a S3 Bucket. The bucket needs to be empty. With Terraform you can first remove all the objects and then let Terraform remove the bucket.&lt;/p&gt;
&lt;p&gt;Terraform also has native drift detection. For example, when you add tags to a resource using the console. And if you provision an update using Terraform you will get a notification that that tag will be removed.&lt;/p&gt;
&lt;p&gt;That being said, I usually use CodePipeline for my deployments. CodePipeline has a good integration with CloudFormation. If you want to deploy your Terraform code in a CodePipeline. You will need to use CodeBuild to perform the Terraform deployment. And there is some additional work needed to manage the state properly.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;CloudFormation and Terraform are really comparable products in what they do. If you know how to build your infrastructure in your cloud provider. Learning Terraform and/or CloudFormation is easy. It&amp;rsquo;s just declaring what you want to build.&lt;/p&gt;
&lt;p&gt;The more you use it the easier it gets because you gain experience in the tool. But in the end it&amp;rsquo;s just a tool and if a better one comes along. You can easily switch because you know the underlying cloud provider.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://www.pexels.com/photo/adult-blur-books-close-up-261909/&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>IAM User credentials with MFA</title>
      <link>https://conijn.io/blog/2022/07/iam-user-credentials-with-mfa/</link>
      <pubDate>Tue, 26 Jul 2022 09:00:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2022/07/iam-user-credentials-with-mfa/</guid>
      <description>&lt;p&gt;When you use IAM user for programmatic access. You have an &lt;code&gt;ACCESS_KEY&lt;/code&gt; and a &lt;code&gt;SECRET_ACCESS_KEY&lt;/code&gt;. With these credentials you are able to interact with AWS. A common pattern is that you will use the IAM User to assume roles in the same or even in different AWS accounts.
The CLI configuration would look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[profile my-role]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;source_profile&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;role_arn&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;arn:aws:iam::111122223333:role/Administrator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mfa_serial&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;arn:aws:iam::111122223333:mfa/my-iam-user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But when you use this role for the first time. Or when the cached credentials are expired it will prompt for MFA token. When you use many accounts and profiles this becomes annoying. You could configure and use a longer session time on your roles. But you still need to enter your MFA token per role.
In this blog post I will explain why this is and what you can do to avoid it!&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;So what happens under the hood? When you perform an cli command like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws s3 ls --profile&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;my-role
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It will first check in the &lt;code&gt;~/.aws/cli/cache/&lt;/code&gt; folder if there are credentials cached. If not or if the credentials are expired an &lt;code&gt;assume-role&lt;/code&gt; call is executed. Because we configured the &lt;code&gt;default&lt;/code&gt; source profile. It will use the &lt;code&gt;default&lt;/code&gt; credentials found in the &lt;code&gt;~/.aws/credentials&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[default]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_access_key_id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;AKIAXXXXXXXXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_secret_access_key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because we configured an &lt;code&gt;mfa_serial&lt;/code&gt; under our profile in our &lt;code&gt;~/.aws/config&lt;/code&gt; file. The AWS cli will prompt for the token and once given it will do the assume-role API call. If everything is fine temporary credentials are returned and these are cached.
&lt;img src=&#34;images/assume_role.png&#34; alt=&#34;Assume role using MFA Token&#34;&gt;
The problem with this approach is that it will be executed each time you need to assume a role. Therefor triggering an MFA prompt each time you use a role. For the first time or if the cached credentials are expired.&lt;/p&gt;
&lt;h2 id=&#34;how-can-i-avoid-it&#34;&gt;How can I avoid it?&lt;/h2&gt;
&lt;p&gt;You can avoid the MFA prompts by first fetching the temporary credentials of the IAM User using MFA. This will give you credentials that are MFA authenticated. And because they are already MFA authenticated. There is no need to provide the token during the assume-role API call.
&lt;img src=&#34;images/fetch_sts.png&#34; alt=&#34;Fetch STS Credentials then assume role&#34;&gt;
As you can see we split the process into 2 parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fetch the temporary credentials for the IAM User using MFA.&lt;/li&gt;
&lt;li&gt;Use the temporary credentials to assume the &lt;code&gt;my-role&lt;/code&gt; role.
The magic behind this approach is because we have split them. You are able to repeat step 2 with other roles. This removes the MFA token prompt because we are already authenticated using MFA.
To make this easier I wrote a small cli tool called &lt;a href=&#34;https://github.com/Nr18/aws-iam-login&#34;&gt;aws-iam-login&lt;/a&gt; that helps you with this!
It only needs to know the &lt;code&gt;mfa_serial&lt;/code&gt; and &lt;code&gt;username&lt;/code&gt;. You can retrieve these if you execute:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws-iam-login default init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will fetch the ARN of the caller identity. Based on this identity we will determine the username and mfa_serial of the IAM User. These will then be stored in the &lt;code&gt;~/.aws/credentials&lt;/code&gt; file. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[default]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_access_key_id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;AKIAXXXXXXXXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_secret_access_key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mfa_serial&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;arn:aws:iam::111122223333:mfa/my-iam-user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;my-iam-user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can use the tool as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws-iam-login default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This authenticates against the AWS API. And request temporary credentials from AWS using your MFA Token. These credentials are then stored as &lt;code&gt;&amp;lt;profile-name&amp;gt;-sts&lt;/code&gt;. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[default-sts]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_access_key_id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;ASIAXXXXXXXXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_secret_access_key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_session_token&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;expiration&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;2022-07-25 19:00:00+00:00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So if you now use the following in your &lt;code&gt;~/.aws/config&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[profile my-role]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;source_profile&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;default-sts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;role_arn&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;arn:aws:iam::111122223333:role/Administrator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will now use the temporary credentials defined in the &lt;code&gt;~/.aws/credentials&lt;/code&gt; file. When the temporary credentials become invalid. Re-run the &lt;code&gt;aws-iam-login default&lt;/code&gt; command and new credentials will be fetched.&lt;/p&gt;
&lt;h3 id=&#34;rotating-your-credentials&#34;&gt;Rotating your credentials&lt;/h3&gt;
&lt;p&gt;Next to helping you assume roles &lt;a href=&#34;https://github.com/Nr18/aws-iam-login&#34;&gt;aws-iam-login&lt;/a&gt; can help you with more! It can rotate your credentials for you! You can do this with the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws-iam-login default rotate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the tool will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;List all available keys for the user, when 1 key is active rotation is possible!&lt;/li&gt;
&lt;li&gt;Create a new &lt;strong&gt;AccessKey&lt;/strong&gt; and &lt;strong&gt;SecretAccessKey&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use the created keys to deactivate the old keys.&lt;/li&gt;
&lt;li&gt;Write the new keys to the &lt;code&gt;~/.aws/configuration&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Delete the old keys.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By using the temporary credentials of the IAM user. You are not prompted to enter your MFA token every time.
And &lt;a href=&#34;https://github.com/Nr18/aws-iam-login&#34;&gt;aws-iam-login&lt;/a&gt; can help you with that!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Working with AWS Permission Policies</title>
      <link>https://conijn.io/blog/2022/07/working-with-aws-permission-policies/</link>
      <pubDate>Wed, 13 Jul 2022 15:01:51 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2022/07/working-with-aws-permission-policies/</guid>
      <description>&lt;h2 id=&#34;policies-and-permission-controls&#34;&gt;Policies and Permission Controls&lt;/h2&gt;
&lt;p&gt;Access management is a crucial part of managing your workloads. It is also one of the primary areas where debugging can be problematic. In AWS, there are multiple ways to control access to the platform at various levels. These permission policies make it possible to optimize control of applications, programs, and resources, so day-to-day operations run as smoothly as possible. AWS has many options to create secure policies on its platform.
Deciding which policies and permission controls to use for different organizations can seem daunting. Organizations rely on AWS Identity and Access Management (IAM) for access management. But there are more options, particularly for larger, more complex organizations with many accounts. Here, we tackle the best use case scenarios for AWS access management according to the needs of different users and organizations, small and large. We also address new developments and guidance in debugging permission errors.&lt;/p&gt;
&lt;h2 id=&#34;identity-and-access-management-iam&#34;&gt;Identity and Access Management (IAM)&lt;/h2&gt;
&lt;p&gt;AWS identity access management is a web service that securely enables user or application access to AWS resources and services. It allows users to control and define what users or applications are allowed to do within an account by authenticating and authorizing the user. It also limits what users can do. Within IAM, you have users and roles; unlike a role, a user can be organized into groups. Within IAM, these entities are referred to as principals. Principals can only perform actions when granted permission.
&lt;img src=&#34;images/Identity-access-manager-900x335.png&#34; alt=&#34;Identity access manager&#34;&gt;
IAM roles are not unique to a single individual, they are like a hat that any users or resources can wear if they have permission to do so, and it can give you a temporary “superpower”. Roles permit actions executed by AWS services, and users can assume a role. For example, an AWS service can request temporary access by assuming a role. The most commonly used services are Amazon Simple Storage Service (Amazon S3) buckets, Amazon Elastic Compute Cloud (Amazon EC2), AWS Lambda, and Amazon DynamoDB. Roles are used either within the web console or through a program such as AWS CLI.
IAM policies are attached to users, groups, roles and resources. This makes managing permissions in your workload more manageable, particularly for smaller organizations with a small number of accounts. In the case of broader permissions, with multiple accounts. In these cases, you typically have a platform team managing the account vending process. And you probably have some standardized resources that you deploy. The workload teams should not alter these resources because they are created by the workload team. You can protect these resources on an organizational level using Service Control Policies (SCPs).&lt;/p&gt;
&lt;h2 id=&#34;aws-permissions&#34;&gt;AWS Permissions&lt;/h2&gt;
&lt;p&gt;Policies are created based on a user’s request and authorization. They should follow the principle of least privilege. A policy defines permissions and determines access or denial when an IAM principal (user or role) requests it. A policy is nothing more than a JSON definition. You can write them from scratch, copy from existing ones, or use the visual editor, you define the policy, but AWS does the enforcement. AWS also offers managed policies that are managed and maintained by AWS’s team. You can use them directly and let AWS maintain them or, you can copy and modify them according to your needs. Each user and role can have policies attached to it. Users can inherit policies based upon the groups they are a member of. At the time of writing, there are a maximum of 10 policies that you can attach to a user or role. This is a soft limit and you can request an increase with a maximum of 20.
There are six types of policies AWS supports which are mainly composed in the JSON policy document format:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Organization SCP: Service Control Policy is used to define the maximum permissions in an organization. SCPs are applied to organization units, and policies are inherited from the parent OU. SCPs are not for allowing actions, they are only used to deny actions. For example, you can use SCPs to prevent the development team from deploying expensive resources such as EC2 “u-12tb1.112xlarge” which costs $3,648 per day. Or you can use SCPs to limit which regions can be used in your organization by denying the rest of the regions.&lt;/li&gt;
&lt;li&gt;Identity-Based Policies: Permissions granted by IAM identities (users, groups, or roles). This controls what actions an identity can take. In policy, you can deny or allow actions related to each service involving all or specific resources with certain conditions. For example, you can have a policy to allow listing objects of S3 buckets whose name starts with app1-acc-* with the condition that the user used MFA during authentication.&lt;/li&gt;
&lt;li&gt;Resource-based Policies: You can deny or allow specific actions to users or groups by attaching inline policies to resources. Resource-based policies are available for several services; you can find the list &lt;a href=&#34;https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html&#34;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Permission Boundaries: The maximum permissions an identity-based policy can grant to an entity. Permission boundaries are the perfect solution to delegate more tasks to developers to remove overhead from the platform team without compromising security.&lt;/li&gt;
&lt;li&gt;Session Policies: Limits the permission the user or role can grant to a session when you are programmatically assuming a role. By passing a session policy, you can ensure this specific session has only the least privileges needed and not the entire role permission.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;aws-organizations&#34;&gt;AWS Organizations&lt;/h2&gt;
&lt;p&gt;When your organization grows you will face multiple challenges. One of them is that you onboard more people to your organization. You can use your existing SSO (Single Sign On) solution or use AWS Single Sign On to authenticate these users. Allowing them to assume a role in one of the AWS Accounts.
Another challenge would be that you separate your workloads across multiple AWS Accounts. This would lead to more and more AWS Accounts while you grow. A common practice is also to split environments across multiple accounts. So for a single workload you could easily have development, testing, acceptance and production accounts.
These challenges can be addressed using AWS Organizations. It allows you to centrally manage the accounts itself and access to them. It would also allow you to manage SCP (Service Control Policies).&lt;/p&gt;
&lt;h2 id=&#34;managing-permissions-in-aws-organizations-service-control-policies&#34;&gt;Managing Permissions in AWS Organizations: Service Control Policies&lt;/h2&gt;
&lt;p&gt;Service Control Policies are a policy type found in AWS. SCPs work well in large organizations because they allow the organization to split the responsibilities. With an organizational SCP you can deny complete services, or only a handful of actions. These SCPs have the same policy structure as the IAM policies with one difference. They apply to the whole AWS Account. For example, when you have a certain blueprint of how an AWS Account needs to look like when you hand it over to a workload team. You can protect these resources from being altered or deleted by the users and roles used by the workload team.
An SCP is placed on an Organization Unit (OU) and is applied to all accounts in the OU including any nested OUs. One of its primary functions is to ensure that all accounts stay within an organization’s access control guidelines. This means that while SCPs do not grant permissions, they do, however, define the limits and actions of IAM users and roles. SCPs effectively act as the guardrail of all accounts an administrator controls.
An SCP can override an IAM policy. In effect, an SCP helps organizations resolve problems that may arise in an evolving cloud infrastructure. Because organizations tend to have many moving parts, with different teams, departments, hierarchies, etc., SCPs help define the boundaries of what is allowed within the account. In the account itself you still need to define the access rights for each principal. If the principal is granted access that is denied by the SCP. The principal is not allowed to perform that action.&lt;/p&gt;
&lt;h2 id=&#34;working-with-an-scp-example&#34;&gt;Working with an SCP Example&lt;/h2&gt;
&lt;p&gt;Assume we have a platform team and a workload team. The workload team is the only team allowed to create S3 buckets that start with &lt;code&gt;xebia-workload&lt;/code&gt;. The platform team is the only one that can create buckets that start with &lt;code&gt;xebia-platform&lt;/code&gt;. You can achieve this with the following SCP:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Statement&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Sid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AllowPlatformPrefixBuckets&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Deny&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:CreateBucket&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:s3:::xebia-platform-*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ForAnyValue:StringNotLike&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;aws:PrincipalARN&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:iam::*:role/Administrator*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Sid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AllowWorkloadPrefixBuckets&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Deny&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:CreateBucket&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:s3:::xebia-workload-*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ForAnyValue:StringNotLike&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;aws:PrincipalARN&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:iam::*:role/Developer*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When you try to create a &lt;code&gt;xebia-platform-myworkload&lt;/code&gt; S3 bucket using the “Developer” role. You will get an error like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/bucket-900x332.png&#34; alt=&#34;bucket&#34;&gt;&lt;/p&gt;
&lt;p&gt;When you create a &lt;code&gt;xebia-workload-myworkload&lt;/code&gt; S3 bucket using the “Developer” role. You will be able to create the bucket.
When you have a closer look at the policy you will see that we set a Deny on the s3:CreateBucket action. For buckets that match the name &lt;code&gt;xebia-workload-*&lt;/code&gt; when the ARN of the principal does not match the Developer role. You can translate this to only the Developer role is not denied to create a bucket with the &lt;code&gt;xebia-workload-&lt;/code&gt; prefix.
This is a simple example but it illustrates the power of a SCP. From an organizational point you can prevent developers from creating platform buckets. And you prevent administrators from creating workload buckets.
This also creates a problem for the developer role. An action is denied but from the account perspective the developer role should be able to create a &lt;code&gt;xebia-platform-workload&lt;/code&gt; bucket. The solution to this is using the IAM Policy Simulator. You can select a role, action and the resource and simulate what would happen.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/settingandresults-900x159.png&#34; alt=&#34;settingandresults&#34;&gt;&lt;/p&gt;
&lt;p&gt;As you can see the action was denied. But it also states that it is denied by AWS Organizations. Now the developer knows that the action is blocked due to a SCP.&lt;/p&gt;
&lt;h2 id=&#34;permission-boundaries-why-do-you-need-to-start-using-them&#34;&gt;Permission Boundaries: Why do you need to start using them?&lt;/h2&gt;
&lt;p&gt;Imagine this scenario, you are the platform admin, and you need to create a policy for a developer that works on serverless applications. The developer needs a role for each application created, which means you need to create new roles every time there is a new application. To reduce the overhead, you want to permit the developer to independently create the roles, but you are worried if the developer decides to create a role that can do more than what is needed!
You can create a policy that allows the developer to create roles only if they attach permission boundaries to the role. Then you can define the maximum permission in the permission boundaries. For our above scenario, permission boundaries can only allow AWS S3 and AWS Lambda function actions. If the developer decides to create a role with administrator access the policy boundary will limit this to only what is allowed within the boundary.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/effective-permissions.png&#34; alt=&#34;effective permissions&#34;&gt;&lt;/p&gt;
&lt;p&gt;This is what AWS calls the “Effective permissions”. From the organizational point of view, you manage the permission boundaries. The developers can create their own roles but only when they attach one of the boundaries you made available.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Statement&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Sid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AllowRolesWithBoundary&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;iam:CreateRole&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;iam:AttachRolePolicy&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;iam:PutRolePolicy&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;iam:PutRolePermissionsBoundary&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Deny&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;StringNotEquals&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;iam:PermissionsBoundary&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:iam::111122223333:policy/xebia-default-boundary&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see in this example policy, we deny the actions when there is no permission boundary called &lt;code&gt;xebia-default-boundary&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;how-to-debug-permission-errors&#34;&gt;How to Debug Permission Errors&lt;/h2&gt;
&lt;p&gt;As a developer, you might run into permission errors. These errors might not make sense because from the developers perspective all policies are set correctly. In these cases, you might be running into a SCP that has blocked you from performing the action.
For this reason, it’s useful to use the &lt;a href=&#34;https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_testing-policies.html&#34;&gt;policy simulator&lt;/a&gt;. The simulator will allow you to select a role that you created. Followed by the service and action you want to simulate. This will tell you if the action is allowed or not. But more importantly, it will tell you why it is denied.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you are working in larger organizations it is important to have a good vision of how you are handling your permission management. As you could read in this paper there are different places where you can apply your policies. Write down how you are handling your permission management and look at it before you make future changes to your policies.
The best practice is to document functions for each role, define permission according to a function with the least privileges using the “allow” action, and avoid using the “deny” action.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Migrate resources across CDK stacks</title>
      <link>https://conijn.io/blog/2022/06/migrate-resources-across-cdk-stacks/</link>
      <pubDate>Thu, 30 Jun 2022 12:15:00 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2022/06/migrate-resources-across-cdk-stacks/</guid>
      <description>&lt;p&gt;When you start building infrastructure as code. You will run into the situation that, you want to split a stack into separate ones. Or, you deployed a resource in the wrong stack. This is all part of the development lifecycle. You try things, some succeed, some will fail. But deployed production resources are hard to rebuild. They either have a complex data migration path or you can&amp;rsquo;t afford the downtime.&lt;/p&gt;
&lt;p&gt;If you accept that some resources or located in unexpected stacks. Your complexity of your infrastructure will increase. And complexity will lead to incidents in the future.&lt;/p&gt;
&lt;p&gt;In this blog post I will explain how you can move a deployed resource from one CDK stack to another.&lt;/p&gt;
&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;First of all, it is important to realize that CDK generates CloudFormation. CloudFormation offers functionality to bring existing resources under it’s control. This means you can import an already deployed resource into a CloudFormation stack.
Now how does that relate to CDK?&lt;/p&gt;
&lt;p&gt;Lets assume you created a CDK stack that created a DynamoDB table. This table contains information or is in use. In these scenarios you cannot delete the resource and recreate it. That would lead to data loss or downtime. So in this scenario we could migrate the resource from one stack to another.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;You need to have the following command line tools in place.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://aws.amazon.com/cli/&#34;&gt;AWS CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://stedolan.github.io/jq/&#34;&gt;jq&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure you have valid credentials in the correct account.&lt;/p&gt;
&lt;h2 id=&#34;step-1--remove-the-resource-from-the-existing-stack&#34;&gt;Step 1 – Remove the resource from the existing stack&lt;/h2&gt;
&lt;p&gt;Download the existing CloudFormation template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws cloudformation get-template &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --output json &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --stack-name &amp;lt;my-cloudformation-stack-name&amp;gt; | jq -r &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.TemplateBody&amp;#39;&lt;/span&gt; &amp;gt; original-template.yml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp original-template.yml modified-template.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the template &lt;code&gt;modified-template.yml&lt;/code&gt; you need to locate the resource you want to move to your “new” stack. Once you found it you need to add the following &lt;code&gt;DeletionPolicy: Retain&lt;/code&gt; to the resource.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyTable794EDED1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::DynamoDB::Table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;KeySchema&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;AttributeName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;KeyType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;HASH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;AttributeDefinitions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;AttributeName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;AttributeType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;S&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ProvisionedThroughput&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ReadCapacityUnits&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;WriteCapacityUnits&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;TableName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Games&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;UpdateReplacePolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Retain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;DeletionPolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Retain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will preserve the resource when you delete it from the stack. Now we will update the CloudFormation stack:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws cloudformation update-stack &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --stack-name &amp;lt;my-cloudformation-stack-name&amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --template-body file://modified-template.yml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp modified-template.yml final-template.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The next step depends a bit on your situation. In some cases you can remove the complete stack because, there are no other resources in the stack. Or because they are not needed anymore. But in other cases you need to move out a resource. (A DynamoDB Table in this example)&lt;/p&gt;
&lt;p&gt;When you don’t need the other resources anymore you can delete the complete stack. If you want to preserve the rest of the resources. You will need to remove the resource from the template that you want to move. So we remove &lt;code&gt;MyTable794EDED1&lt;/code&gt; from the &lt;code&gt;final-template.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;By re-running the &lt;code&gt;update-stack&lt;/code&gt; command again. We will instruct CloudFormation to remove the resource. But, because we have the &lt;code&gt;Retain&lt;/code&gt; policy set. The resource will only be removed from the stack.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws cloudformation update-stack &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --stack-name &amp;lt;my-cloudformation-stack-name&amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --template-body file://final-template.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-2--import-the-resource&#34;&gt;Step 2 – Import the resource&lt;/h2&gt;
&lt;p&gt;When you move resources around you either move them to a new or an existing stack. When you deal with an existing stack you need the existing template first.
If you are updating an existing stack, continue with “Download the existing stack”. Otherwise you can skip that section.&lt;/p&gt;
&lt;h3 id=&#34;download-the-existing-stack&#34;&gt;Download the existing stack&lt;/h3&gt;
&lt;p&gt;Download the existing CloudFormation template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws cloudformation get-template &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --output json &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --stack-name &amp;lt;my-other-cloudformation-stack-name&amp;gt; | jq -r &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.TemplateBody&amp;#39;&lt;/span&gt; &amp;gt; template.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;import-the-existing-resource&#34;&gt;Import the existing resource&lt;/h3&gt;
&lt;p&gt;When you want to move the resource to a new stack. You need to create a new &lt;code&gt;template.yml&lt;/code&gt; file. If you want to move the resource in an existing stack you already have the existing template ready.&lt;/p&gt;
&lt;p&gt;To import the resource we need the resource definition. You can copy that from the &lt;code&gt;modified-template.yml&lt;/code&gt; file from step 1. I could recommend this because the CloudFormation definition should match the deployed resource. The second thing is it needs to have the &lt;code&gt;DeletionPolicy: Retain&lt;/code&gt; definition. So our example will look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyTable794EDED1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::DynamoDB::Table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;KeySchema&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;AttributeName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;KeyType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;HASH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;AttributeDefinitions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;AttributeName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;AttributeType&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;S&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ProvisionedThroughput&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ReadCapacityUnits&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;WriteCapacityUnits&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;TableName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Games&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;UpdateReplacePolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Retain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;DeletionPolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Retain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we need to create a file, lets call it &lt;code&gt;import.json&lt;/code&gt;. In this file we will define the following for each resource what we import:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS Resource type&lt;/strong&gt;, the type of the resource itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LogicalResourceId&lt;/strong&gt;, the identifier in the template. In this example that would be &lt;code&gt;Games&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ResourceIdentifier&lt;/strong&gt;, the identifier of the existing resource.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see this file contains the relation between the stack and what is already deployed. The file looks as followed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ResourceType&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::DynamoDB::Table&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;LogicalResourceId&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MyTable794EDED1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;ResourceIdentifier&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;TableName&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Games&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, you will need deploy the stack. You can do this with the following commands:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws cloudformation create-change-set &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --stack-name &amp;lt;my-other-cloudformation-stack-name&amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --change-set-name ImportChangeSet &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --change-set-type IMPORT &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --template-body file://template.yml &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --resources-to-import file://import.json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws cloudformation wait change-set-create-complete --change-set-name ImportChangeSet --stack-name &amp;lt;my-other-cloudformation-stack-name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws cloudformation execute-change-set --change-set-name ImportChangeSet --stack-name &amp;lt;my-other-cloudformation-stack-name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-3--redeploy-using-cdk&#34;&gt;Step 3 – Redeploy using CDK&lt;/h2&gt;
&lt;p&gt;At this point the DynamoDB table is part of the new stack. But when you redeploy the CDK stack it will not render the DynamoDB resource definition. So for this we will need to add the definition to the CDK stack.
For this example I used the following CDK snippet. But when you move from one CDK stack to another you can just move the relevant code.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dynamodb&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Table(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MyTable&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    table_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GamesTable&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    partition_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;dynamodb&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Attribute(name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;, type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;dynamodb&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AttributeType&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;STRING),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    billing_mode&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;dynamodb&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;BillingMode&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PROVISIONED
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now in theory this should render the same template you used during the import. But if there are changes CloudFormation will thread them as updates. It tries to change the resource to its new state.
After you deployed using CDK its business as usual.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By reducing complexity in your infrastructure. You reduce the chance on failures and incidents. This reduction can be achieved by moving resources to more logical stacks. The process itself does contain manual steps. But when your resources contain data or cannot afford downtime. This might be your only solution.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Diagrams as code</title>
      <link>https://conijn.io/blog/2022/06/diagrams-as-code/</link>
      <pubDate>Fri, 10 Jun 2022 08:45:00 +0000</pubDate>
      
      <guid>https://conijn.io/blog/2022/06/diagrams-as-code/</guid>
      <description>&lt;p&gt;Most of you have heard of “infrastructure as code” by now. But building cloud infrastructures is more than coding infrastructure. In this blog I will show you how I make my own live easier by generating infrastructure diagrams from code.
When you have read my previous blogs about &lt;a href=&#34;https://conijn.io/blog/2022/02/commit-messages-done-the-right-way/&#34;&gt;Commit messages done the right way&lt;/a&gt;. You are not surprised that I like to make things easy for the reader. When you are new to a team, or you are not familiar with a specific repository that your team maintains. It helps when the &lt;code&gt;README.md&lt;/code&gt; of the repository helps you understand what the repository does.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A picture is worth a thousand words!&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Nice diagrams will help you a lot! But often they are either created in a program that you don’t master. Or you don’t have a access or a license to it. This makes it harder to keep the images up to date. And easier not to update them at all.
By managing the diagrams the same way as we manage our infrastructure. We also get the same benefits! Not only that when you make a change to your infrastructure you can update the image in the same commit. This makes it easier for the reviewer. Because, next to the code change he also will see a visual presentation of the change.&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;Enough about the theory, how do you do that? Within Python there is a package named &lt;a href=&#34;https://pypi.org/project/diagrams/&#34;&gt;diagrams&lt;/a&gt;. The &lt;a href=&#34;https://diagrams.mingrammer.com/docs/getting-started/examples&#34;&gt;website&lt;/a&gt; has a lot of examples I copied a few to this article.
First we will configure a &lt;code&gt;poetry&lt;/code&gt; environment. Afterwards we will install the dependency.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir diagrams-as-code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd diagrams-as-code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;poetry init
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;poetry add diagrams
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you have done that, we can create a file called &lt;a href=&#34;https://github.com/binxio/diagrams-as-code/blob/main/grouped_workers.py&#34;&gt;&lt;code&gt;grouped_workers.py&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Diagram
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams.aws.compute &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; EC2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams.aws.database &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; RDS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams.aws.network &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ELB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; Diagram(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Grouped Workers&amp;#34;&lt;/span&gt;, show&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;, direction&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TB&amp;#34;&lt;/span&gt;, filename&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images/grouped_workers&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ELB(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lb&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; [EC2(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;worker1&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  EC2(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;worker2&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  EC2(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;worker3&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  EC2(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;worker4&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  EC2(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;worker5&amp;#34;&lt;/span&gt;)] &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; RDS(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;events&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see the syntax is pretty straight forward. We have 5 workers that are behind a load balancer. The workers can connect to an RDS database named &lt;code&gt;events&lt;/code&gt;. When you then run the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;poetry run python grouped_workers.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output looks like this:
&lt;img src=&#34;images/grouped_workers.png&#34; alt=&#34;Grouped Workers&#34;&gt;
Pretty cool right?
Let’s do another one, we will create a file called &lt;a href=&#34;https://github.com/binxio/diagrams-as-code/blob/main/clustered_web_services.py&#34;&gt;&lt;code&gt;clustered_web_services.py&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Cluster, Diagram
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams.aws.compute &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ECS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams.aws.database &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ElastiCache, RDS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams.aws.network &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ELB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diagrams.aws.network &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Route53
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; Diagram(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Clustered Web Services&amp;#34;&lt;/span&gt;, show&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;, filename&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images/clustered_web_services&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dns &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Route53(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dns&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lb &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ELB(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lb&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; Cluster(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Services&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        svc_group &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ECS(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;web1&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     ECS(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;web2&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     ECS(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;web3&amp;#34;&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; Cluster(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DB Cluster&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        db_primary &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; RDS(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;userdb&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        db_primary &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; [RDS(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;userdb ro&amp;#34;&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    memcached &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ElastiCache(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;memcached&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dns &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; lb &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; svc_group
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    svc_group &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; db_primary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    svc_group &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; memcached
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So this example is a bit more complex. We now introduced Route53, and we added a caching mechanism. To clarify the grouping we made use of the &lt;code&gt;Cluster&lt;/code&gt; option. And when we run the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;poetry run python clustered_web_services.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We will get the following output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/clustered_web_services.png&#34; alt=&#34;Clustered Web Services&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You will receive the same benefits from “infrastructure as code”. When you manage your “diagrams as code”. Version control comes out of the box.
Because you combine the infrastructure code change. With the diagram code change you make the life of you and your teammates a lot easier.
Photo by &lt;a href=&#34;https://unsplash.com/@lucabravo?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Luca Bravo&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com&#34;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Sync AWS CodeCommit repositories</title>
      <link>https://conijn.io/blog/2022/04/sync-aws-codecommit-repositories/</link>
      <pubDate>Tue, 12 Apr 2022 11:13:35 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2022/04/sync-aws-codecommit-repositories/</guid>
      <description>&lt;p&gt;In some scenarios you might have the need to replicate an &lt;a href=&#34;https://docs.aws.amazon.com/codecommit/latest/userguide/welcome.html&#34; title=&#34;Learn more about AWS CodeCommit&#34;&gt;AWS CodeCommit&lt;/a&gt; repository. When I was setting up a &lt;a href=&#34;https://conijn.io/blog/2022/01/testing-your-landing-zone-when-using-adf/&#34; title=&#34;Why do I need a test organization?&#34;&gt;test organization&lt;/a&gt; using &lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework&#34; title=&#34;Learn more about AWS Deployment Framework&#34;&gt;AWS Deployment Framework (ADF)&lt;/a&gt; I ran into this myself. Because I want to test the deployment of my landing zone I needed to have a close replica. This includes the CodeCommit setup.&lt;br&gt;
But at the same time I did not want to change the development workflow. The workflow is pretty straight forward. You create a feature branch to work in. When you are ready you merge it to a &lt;code&gt;development&lt;/code&gt; branch. When it needs to go to production you merge it into the &lt;code&gt;main&lt;/code&gt; branch.&lt;br&gt;
So we will use the &lt;code&gt;development&lt;/code&gt; branch to deploy to the test organization. But, because the test organization is a replica of production. Merging to the &lt;code&gt;development&lt;/code&gt; branch would not have effect on the test organization. For this we need to synchronize the &lt;code&gt;development&lt;/code&gt; branch to the test organization.&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work&lt;/h2&gt;
&lt;p&gt;We will use an &lt;a href=&#34;https://conijn.io/blog/2022/03/running-a-git-binary-in-aws-lambda/&#34; title=&#34;Running a git binary in AWS Lambda&#34;&gt;AWS Lambda function with a git client&lt;/a&gt; to perform the following actions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone the CodeCommit repository.&lt;/li&gt;
&lt;li&gt;Checkout the &lt;code&gt;development&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;Assume the an &lt;a href=&#34;https://github.com/binxio/aws-lambda-git/blob/main/target-template.yml&#34; title=&#34;IAM Role example&#34;&gt;IAM role&lt;/a&gt; in the target account.&lt;/li&gt;
&lt;li&gt;Perform a &lt;code&gt;git push&lt;/code&gt;.&lt;br&gt;
The name of the CodeCommit repositories in both organization are identical. By assuming a role in the AWS Account that hosts the CodeCommit repositories in the test organization. You can perform a &lt;code&gt;git push&lt;/code&gt; assuming you have the correct permissions. The changes are then pushed to the repository in the test organization.&lt;br&gt;
There is a difference between the test and production organization. The test organization is configured to listen to the &lt;code&gt;development&lt;/code&gt; branch. The production organization listens to the &lt;code&gt;main&lt;/code&gt; branch.&lt;br&gt;
With this in place you have the following workflow:&lt;/li&gt;
&lt;li&gt;Create a pull request for your feature branch into &lt;code&gt;development&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When reviewed and approved you can merge the pull request.&lt;/li&gt;
&lt;li&gt;(Automatic) an event is triggered, we use an &lt;a href=&#34;https://github.com/binxio/aws-lambda-git/blob/f29a1dbaadc5a00fd9f2aa4f963870c24bb64244/template.yml#L18&#34; title=&#34;Event Rule example&#34;&gt;event rule&lt;/a&gt; to trigger the Lambda function.&lt;/li&gt;
&lt;li&gt;(Automatic) the Lambda function will then:
&lt;ol&gt;
&lt;li&gt;Clone and pull the code from the production repository.&lt;/li&gt;
&lt;li&gt;Assume a role in the test organization.&lt;/li&gt;
&lt;li&gt;Push the code to the remote repository in the test organization.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;(Automatic) the CodePipeline for the repository is triggered.&lt;br&gt;
Now you only need to confirm whether your changes are correct in the test organization. Once satisfied you can merge the change to the &lt;code&gt;main&lt;/code&gt; branch. This will deploy the same changes in production.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Synchronizing of git repositories can help you with automated testing. See the &lt;a href=&#34;https://github.com/binxio/aws-lambda-git/&#34; title=&#34;Code example&#34;&gt;aws-lambda-git&lt;/a&gt; repository for more information.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Running a git binary in AWS Lambda</title>
      <link>https://conijn.io/blog/2022/03/running-a-git-binary-in-aws-lambda/</link>
      <pubDate>Thu, 24 Mar 2022 20:28:51 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2022/03/running-a-git-binary-in-aws-lambda/</guid>
      <description>&lt;h1 id=&#34;running-a-git-binary-in-aws-lambda&#34;&gt;Running a git binary in AWS Lambda&lt;/h1&gt;
&lt;p&gt;In some scenarios it can be easier to use a binary in a lambda function. In this blog post I will walk you through how you can run a git binary in an &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/welcome.html&#34; title=&#34;Learn more about AWS Lambda&#34;&gt;AWS Lambda&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For this blog post I will use the &lt;a href=&#34;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html&#34; title=&#34;Learn more about AWS Serverless Application Model&#34;&gt;AWS Serverless Application Model&lt;/a&gt;(AWS SAM). AWS SAM makes it easy to build, package and deploy lambda functions. The used template can be found on &lt;a href=&#34;https://github.com/binxio/aws-lambda-git/&#34;&gt;GitHub&lt;/a&gt;. AWS SAM will look at the &lt;code&gt;Runtime&lt;/code&gt; of the &lt;code&gt;AWS::Serverless::Function&lt;/code&gt; resource. In this example I used python so it will look at the &lt;a href=&#34;https://github.com/binxio/aws-lambda-git/blob/main/git_client/requirements.txt&#34;&gt;requirements.txt&lt;/a&gt;. It will look at the packages defined in the file. AWS SAM installs them in &lt;code&gt;.aws-sam/build/&amp;lt;ResourceName&amp;gt;&lt;/code&gt;. This folder is compressed and uploaded to &lt;a href=&#34;https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html&#34; title=&#34;Learn more about Amazon S3&#34;&gt;Amazon S3&lt;/a&gt; when you package and deploy the template.&lt;/p&gt;
&lt;p&gt;You can also invoke the function on your own computer. This makes it easier to develop and troubleshoot. To invoke the function we first need to build it. Afterwards you need to invoke it. I created a &lt;a href=&#34;https://github.com/binxio/aws-lambda-git/blob/main/Makefile&#34;&gt;Makefile&lt;/a&gt;, for more information on this you can read one of my previous &lt;a href=&#34;https://conijn.io/blog/2021/09/make-your-life-easier-using-makefiles/&#34;&gt;blogs&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make invoke
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will see that you will see the following error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;errorMessage&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[Errno 2] No such file or directory: &amp;#39;git&amp;#39;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;errorType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FileNotFoundError&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that makes sense right? We do not have the git client installed. You cannot install it using the &lt;code&gt;requirements.txt&lt;/code&gt; file. This is because it is not in the PyPi package manager.&lt;/p&gt;
&lt;h2 id=&#34;install-the-git-client&#34;&gt;Install the git client&lt;/h2&gt;
&lt;p&gt;If you look at the &lt;a href=&#34;https://github.com/binxio/aws-lambda-git/blob/main/Makefile&#34;&gt;Makefile&lt;/a&gt; you can see that it will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build a docker container.
&lt;ul&gt;
&lt;li&gt;Install git on the container.&lt;/li&gt;
&lt;li&gt;Move all the dependencies into the &lt;code&gt;/var/task&lt;/code&gt; structure.&lt;/li&gt;
&lt;li&gt;Zip the content of the &lt;code&gt;/var/task&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Extract the zip file from the container.&lt;/li&gt;
&lt;li&gt;Unzip the contents into the &lt;code&gt;git_client&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sam build&lt;/code&gt; will copy the content to &lt;code&gt;.aws-sam/build/GitClient&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sam build&lt;/code&gt; installs the dependencies from the &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sam invoke&lt;/code&gt; will use the &lt;code&gt;.aws-sam/build/GitClient&lt;/code&gt; content to invoke the function.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Git binary is located in &lt;code&gt;/var/task/bin&lt;/code&gt;. Because when we invoke the git command we also supply the &lt;code&gt;/var/task/bin&lt;/code&gt; as &lt;code&gt;PATH&lt;/code&gt;. This allows us to call &lt;code&gt;git&lt;/code&gt; and the container knows it needs to invoke &lt;code&gt;/var/task/bin/git&lt;/code&gt;. The libraries are located in &lt;code&gt;/var/task/lib&lt;/code&gt;. Because we have set the &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; to &lt;code&gt;/var/task/lib&lt;/code&gt; the libraries can be found.&lt;/p&gt;
&lt;p&gt;I created a &lt;a href=&#34;https://github.com/binxio/aws-lambda-git/blob/main/Dockerfile&#34;&gt;Dockerfile&lt;/a&gt; that uses &lt;code&gt;public.ecr.aws/lambda/python:3.8&lt;/code&gt; as the base. This is the same runtime of the Lambda function. I prepare 2 folders &lt;code&gt;/var/task/bin&lt;/code&gt; and &lt;code&gt;/var/task/lib&lt;/code&gt;. Then I install the &lt;code&gt;git&lt;/code&gt; and &lt;code&gt;zip&lt;/code&gt; packages within the docker context.&lt;/p&gt;
&lt;p&gt;I then copy the &lt;code&gt;/usr/bin/git&lt;/code&gt; and the &lt;code&gt;/usr/libexec/git-core/git-remote-http&lt;/code&gt; executables to the &lt;code&gt;/var/task/bin&lt;/code&gt; folder.
When you try to build the Lambda function and run it it fails on a missing library.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;errorMessage&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;git: error while loading shared libraries: libpcre2-8.so.0: cannot open shared object file: No such file or directory&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;errorType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Exception&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By using the &lt;code&gt;ldd&lt;/code&gt; command you can find all the shared libraries. We will use this to identify the needed libraries and copy them into the &lt;code&gt;/var/task/lib/&lt;/code&gt; folder.
Once you have all dependencies in place. You can fetch the &lt;code&gt;git --version&lt;/code&gt; running inside of the Lambda function.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Git version: git version 2.32.0&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;If you combine this with the &lt;a href=&#34;https://pypi.org/project/awscli/&#34;&gt;awscli&lt;/a&gt; and &lt;a href=&#34;https://pypi.org/project/git-remote-codecommit/&#34;&gt;git-remote-codecommit&lt;/a&gt; packages. You can clone a CodeCommit repository and perform your actions and commit it again.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It is possible to embed binaries in AWS Lambda and use them. It might take you some effort. But it opens new possibilities that you might not considered before.
In my next &lt;a href=&#34;https://conijn.io/blog/2022/04/sync-aws-codecommit-repositories/&#34;&gt;blog&lt;/a&gt; you will read how you can use this to synchronize two CodeCommit repositories.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://unsplash.com/@emilep?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Emile Perron&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/infrastructure-as-code?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hosting a single page application or website on S3</title>
      <link>https://conijn.io/blog/2022/03/hosting-a-single-page-application-or-website-on-s3/</link>
      <pubDate>Wed, 09 Mar 2022 16:13:58 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2022/03/hosting-a-single-page-application-or-website-on-s3/</guid>
      <description>&lt;p&gt;In this blog post I will walk you through how to configure a single page application or website. In a previous blog &lt;a href=&#34;https://xebia.com/blog/best-practices-for-s3-web-hosting-and-explaining-why/&#34;&gt;Best practices for S3 web hosting and explaining why&lt;/a&gt;. &lt;a href=&#34;https://binx.io/blog/author/tibor-hercz/&#34;&gt;Tibor Hercz&lt;/a&gt; explained the best practices for hosting a static website on S3.
As Tibor explained, there are some downsides on using a &lt;code&gt;Origin Access Identity (OAI)&lt;/code&gt;. But in some &lt;code&gt;Single Page Applications (SPA)&lt;/code&gt; you will not experience this downside.&lt;/p&gt;
&lt;h2 id=&#34;what-is-a-single-page-application&#34;&gt;What is a single page application?&lt;/h2&gt;
&lt;p&gt;A single page application loads a single web document. Based upon your interaction with this page the content is updated.
The pages are rendered on your web browser. And more API calls are performed to fetch extra input. Due to this fact an SPA can be fast!
You can use the framework of your choice: &lt;a href=&#34;https://angular.io&#34;&gt;angular&lt;/a&gt;, &lt;a href=&#34;https://reactjs.org&#34;&gt;reactjs&lt;/a&gt;, &lt;a href=&#34;https://vuejs.org&#34;&gt;vue&lt;/a&gt;, etc, etc.&lt;/p&gt;
&lt;h2 id=&#34;setting-up-the-infrastructure&#34;&gt;Setting up the infrastructure&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;For simplicity, I am providing CloudFormation snippets.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;First we will need an S3 bucket to host the application in. We need an OAI. And we need to grant it access yo the bucket:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;AccessControl&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Private&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyOriginAccessIdentity&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::CloudFront::CloudFrontOriginAccessIdentity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;CloudFrontOriginAccessIdentityConfig&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Comment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allows CloudFront to reach the bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyBucketPolicy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::BucketPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Bucket&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;Ref MyBucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;PolicyDocument&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Statement&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;Action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:GetObject*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:GetBucket*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - &lt;span style=&#34;color:#ae81ff&#34;&gt;s3:List*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Effect&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Allow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Principal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;CanonicalUser&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt MyOriginAccessIdentity.S3CanonicalUserId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Resource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt MyBucket.Arn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              - !&lt;span style=&#34;color:#ae81ff&#34;&gt;Sub ${MyBucket.Arn}/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we will need the CloudFront distribution:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyDistribution&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::CloudFront::Distribution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;DistributionConfig&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;CustomErrorResponses&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;ErrorCode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;404&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;ResponseCode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;ResponsePagePath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/index.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;DefaultCacheBehavior&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;AllowedMethods&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#ae81ff&#34;&gt;GET&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#ae81ff&#34;&gt;HEAD&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;CachedMethods&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#ae81ff&#34;&gt;GET&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            - &lt;span style=&#34;color:#ae81ff&#34;&gt;HEAD&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Compress&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;ForwardedValues&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Cookies&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;Forward&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;QueryString&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;TargetOriginId&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;origin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;ViewerProtocolPolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;redirect-to-https&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;DefaultRootObject&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;index.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Enabled&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;HttpVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;http2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;IPV6Enabled&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;Origins&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;ConnectionAttempts&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;ConnectionTimeout&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;DomainName&lt;/span&gt;: !&lt;span style=&#34;color:#ae81ff&#34;&gt;GetAtt MyBucket.RegionalDomainName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;origin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;S3OriginConfig&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;OriginAccessIdentity&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;Fn::Join&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  - - &lt;span style=&#34;color:#ae81ff&#34;&gt;origin-access-identity/cloudfront/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    - &lt;span style=&#34;color:#f92672&#34;&gt;Ref&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MyOriginAccessIdentity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you have set up the infrastructure. You need to &lt;code&gt;build&lt;/code&gt; your SPA. And upload the build to the S3 bucket.
My assumption here is that you created an app with various routes/paths.&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;When you navigate to the hostname of the created CloudFront distribution. The distribution will fall back on the &lt;code&gt;index.html&lt;/code&gt; file. This is because we have set the &lt;code&gt;DefaultRootObject&lt;/code&gt; to &lt;code&gt;index.html&lt;/code&gt;.
When you navigate in the application it looks like the browser is changing paths. But in fact it is only updating the address in the address bar.
Let say you created a &lt;code&gt;/blog&lt;/code&gt; route. You navigate to this route. And you do a hard refresh! You expect to get a &lt;code&gt;404 Not Found&lt;/code&gt; error because there is no folder called &lt;code&gt;blog&lt;/code&gt;.
But because we configured a &lt;code&gt;CustomErrorResponses&lt;/code&gt;. That will redirect all &lt;code&gt;404&lt;/code&gt; errors to the &lt;code&gt;index.html&lt;/code&gt; file with a &lt;code&gt;200&lt;/code&gt; response code.
This will make sure that the Single Page Application gets loaded again. It will see the location in the address bar and renders the correct content.
This does mean that you will need to handle unknown routes in your application.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you want to host a Single Page Application you can use CloudFront with private buckets. You will get all the benefits of CloudFront while keeping the bucket private.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Improve your pull request workflow using pull-request-codecommit</title>
      <link>https://conijn.io/blog/2022/02/improve-your-pull-request-workflow-using-pull-request-codecommit/</link>
      <pubDate>Wed, 23 Feb 2022 21:21:45 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2022/02/improve-your-pull-request-workflow-using-pull-request-codecommit/</guid>
      <description>&lt;p&gt;In my previous &lt;a href=&#34;https://conijn.io/blog/2022/02/commit-messages-done-the-right-way/&#34;&gt;blog&lt;/a&gt; post, I wrote why it is important to write good commit messages.
In this post I will highlight an example how this works in your advantage. Using an AWS CodeCommit repository.&lt;/p&gt;
&lt;h2 id=&#34;working-example&#34;&gt;Working example&lt;/h2&gt;
&lt;p&gt;Alright, lets assume we have a project that uses a &lt;code&gt;main&lt;/code&gt; branch. In combination with feature branches.
We want to add a feature to our project, so we create a branch called: &lt;code&gt;feat/my-feature&lt;/code&gt;.
Let say we manage to get the feature working in a single commit. The commit message would look something like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;feat: add support for x&lt;/p&gt;
&lt;p&gt;By adding support for x we allow x users to adopt this tool in their workflow.&lt;/p&gt;
&lt;p&gt;Issue: #123&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Now it is time to create a pull request! I have created a tool called &lt;a href=&#34;https://github.com/Nr18/pull-request-codecommit&#34;&gt;pull-request-codecommit&lt;/a&gt;.
This tool looks at the differences between the current branch. And the target branch and proposes a pull request message.
So if you want to create a pull request you type:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git pr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The default editor is opened. And in this editor you will see:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;feat: add support for x (#123)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;By adding support for x we allow x users to adopt this tool in their workflow.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Issue: #123
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first line will be used as the title and the rest as a description.
You can alter the message to your liking, but if you use a proper commit message this is spot on already.
When you save the proposal the pull request is created for you:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/terminal-900x256.png&#34; alt=&#34;git pr command example&#34;&gt;&lt;/p&gt;
&lt;p&gt;For convenience the link is displayed and the eventual message. When you open the link the pull request will look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/PullRequestExample-900x648.png&#34; alt=&#34;CodeCommit example&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;what-about-many-commits&#34;&gt;What about many commits?&lt;/h2&gt;
&lt;p&gt;When many commits are used the tool tries to propose a good pull request message. When you split your commits.
And you used proper messages it looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/terminal2-900x267.png&#34; alt=&#34;git pr example with multiple commits&#34;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the first commit is used as the title. The rest of the commit titles are shown as a list in the description.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you write proper commit messages, you help your self. It makes it easier for you to draft pull requests.
And it allows you to start using tools because you use standards.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Commit messages done the right way</title>
      <link>https://conijn.io/blog/2022/02/commit-messages-done-the-right-way/</link>
      <pubDate>Thu, 10 Feb 2022 15:08:49 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2022/02/commit-messages-done-the-right-way/</guid>
      <description>&lt;p&gt;We have all seen it. You see something in your source code. You do not know why it was added. And you look at the commit history. And then you see: &amp;ldquo;fix bug&amp;rdquo; or &amp;ldquo;add feature name&amp;rdquo;. How helpful is that message?
In this blog post I will address how you can write commit messages that are helpful. And optimized for the reader.&lt;/p&gt;
&lt;h2 id=&#34;so-why-is-this-important&#34;&gt;So, why is this important?&lt;/h2&gt;
&lt;p&gt;Most agile teams need peer reviews. The most common or well known way is via &lt;a href=&#34;https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests&#34;&gt;pull requests&lt;/a&gt;.
When you introduce a change. For a feature or fixing a bug.
The question your peer reviewer will have is: Why this change was made. And then continues to look at the changes.
You create a pull request on one or more commit messages. A commit describes the why of a change. When you create a pull request you can use these commit messages when you create your pull request.&lt;/p&gt;
&lt;h2 id=&#34;example-commit&#34;&gt;Example commit&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;fix input&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;fix: white spaces on input&lt;/p&gt;
&lt;p&gt;Remove the leading and trailing spaces. This will clean the input before we save it to the database.&lt;/p&gt;
&lt;p&gt;Issue: #123&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This is the difference between writing a commit message. And writing a useful commit message.
Useful commit messages explain why the change was made.
When you create a pull request you can use the commit messages as input. And draft a useful pull request description for your reviewer.&lt;/p&gt;
&lt;h2 id=&#34;commit-format&#34;&gt;Commit format&lt;/h2&gt;
&lt;p&gt;I am a big fan of the &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34;&gt;conventional commits&lt;/a&gt; format. But the most important thing is that you write down why the change was made.
Not what the change is.
The changes are already displayed when you do a &lt;code&gt;diff&lt;/code&gt; between commits/branches. So describing what has changed does not make sense.
The reason why that is in the authors head. And unless you add it in a commit message that information gets lost.
By doing this you create a better historical overview. And it makes it easier to create pull requests.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you optimize your commit messages for the reader. The reader does not need to go on a scavenger hunt.
And it creates a better historical overview of why changes are made in your repository.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to set up AWS Session Manager logging cross account</title>
      <link>https://conijn.io/blog/2022/01/how-to-set-up-aws-session-manager-logging-cross-account/</link>
      <pubDate>Wed, 26 Jan 2022 22:47:30 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2022/01/how-to-set-up-aws-session-manager-logging-cross-account/</guid>
      <description>&lt;p&gt;Setting up and managing access to your EC2 instances can be challenging. There are a lot of things you need to consider. How do you rotate your keys? Who logged in using that key? How do you limit the risk of thread actors?
And what did they do? What if you could log the complete session in a different AWS Account? And encrypt it using a customer managed KMS key? That would give you complete visibility and auditability on your shell sessions.
In this blog post I will address those questions using AWS Session Manager.&lt;/p&gt;
&lt;h2 id=&#34;what-is-the-aws-session-manager&#34;&gt;What is the AWS Session Manager?&lt;/h2&gt;
&lt;p&gt;Taken from the documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Session Manager is a fully managed AWS Systems Manager capability. With Session Manager, you can manage your Amazon Elastic Compute Cloud (Amazon EC2) instances, edge devices, and on-premises servers and virtual machines (VMs).&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So, what does that mean? It means that you can open an SSH shell using the AWS Console in your browser. Select the EC2 instance. Click the connect button and a shell session loads in the browser. You can also use CLI commands to do this.
The interesting part is that you grant access based on your IAM credentials. So you do not have to share private SSH keys across the team. No need to rotate them. No need to rotate them when a team member leaves. Because you don’t need a private key at all.
But this solution comes with another advantage. You do not need to open port 22 to connect. In fact, you do not need to open any inbound ports.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The SSM Agent comes pre-installed with the Amazon Linux 2 AMI.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Instead, it uses an SSM Agent. And this agent will register itself to the &lt;a href=&#34;https://aws.amazon.com/systems-manager/&#34; title=&#34;AWS Systems Manager&#34;&gt;AWS Systems Manager&lt;/a&gt;. It will use the attached instance profile to do that. From that point on when you request a shell from the &lt;a href=&#34;https://aws.amazon.com/systems-manager/features/#Session_Manager&#34; title=&#34;AWS Systems Manager Session Manager&#34;&gt;Session Manager&lt;/a&gt;. The agent will react on this response and establish the connection.&lt;/p&gt;
&lt;h2 id=&#34;what-are-the-other-advantages&#34;&gt;What are the other advantages?&lt;/h2&gt;
&lt;p&gt;Yes, there is more! &lt;a href=&#34;https://aws.amazon.com/systems-manager/features/#Session_Manager&#34; title=&#34;AWS Systems Manager Session Manager&#34;&gt;Session Manager&lt;/a&gt; has some extra features!
You can track session activity using &lt;a href=&#34;https://aws.amazon.com/eventbridge/&#34; title=&#34;Amazon EventBridge&#34;&gt;Amazon EventBridge&lt;/a&gt;. This means that you can react on these events. This creates the possibility to proactively react on these events. You could for instance: terminate the session if no access is allowed.
And that brings me to the main topic of this blog post. What if you suspect that some commands where executed on a server using a shell session? How do you prove it happened or did not happen?&lt;/p&gt;
&lt;h2 id=&#34;logging-your-session-activity&#34;&gt;Logging your session activity&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;https://aws.amazon.com/systems-manager/features/#Session_Manager&#34; title=&#34;AWS Systems Manager Session Manager&#34;&gt;Session Manager&lt;/a&gt; gives you the ability to log the session to S3. Setting this up is described on the documentation pages. But, if you want to use a centralized logging account. And you need to use a customer managed KMS key, the documentation is not that helpful.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Depending on your use-case, you might want to limit the access. You can do this per account. Or within an AWS Organization.
For this example, I will limit the scope within the same organization.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;First, you will need to create a KMS key. This key will be used to encrypt the data stored on S3. S3 needs to be able to use the KMS key add following statements to the key policy:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AWS&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:Decrypt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:Encrypt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:ReEncrypt*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:GenerateDataKey*&amp;#34;&lt;/span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;StringEquals&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;kms:ViaService&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3.eu-central-1.amazonaws.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;aws:PrincipalOrgID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;o-xxxxxxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we allow &lt;code&gt;kms:Decrypt&lt;/code&gt; and &lt;code&gt;kms:GenerateDataKey&lt;/code&gt; within the organization. These actions are needed so that the Session Manager can encrypt the session. And it can confirm that it can log to the given S3 bucket.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AWS&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:Decrypt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kms:GenerateDataKey&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;StringEquals&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;aws:PrincipalOrgID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;o-xxxxxxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We then need to create a S3 Bucket. You need to enable default encryption using the KMS key you created. You need to grant other accounts to write to this bucket. So, you need a bucket policy. Here is an example of a bucket policy that you need:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Statement&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AWS&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:GetEncryptionConfiguration&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:s3:::my-bucket-name&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;StringEquals&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;aws:PrincipalOrgID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;o-xxxxxxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;AWS&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:PutObject&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:PutObjectAcl&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:s3:::my-bucket-name/*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;StringEquals&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;aws:PrincipalOrgID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;o-xxxxxxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alright, the logging account has been prepared. It can now receive logs from all accounts within your organization. Now it is time to set up the account that will use the &lt;a href=&#34;https://aws.amazon.com/systems-manager/features/#Session_Manager&#34; title=&#34;AWS Systems Manager Session Manager&#34;&gt;Session Manager&lt;/a&gt;.
Create a JSON file locally with the following content:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Replace the name of the bucket and the KMS key! You could use the account id as a prefix.&lt;/p&gt;&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;schemaVersion&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Document to hold regional settings for Session Manager&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;sessionType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Standard_Stream&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;inputs&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;s3BucketName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[NAME OF THE BUCKET]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;s3KeyPrefix&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;s3EncryptionEnabled&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;cloudWatchLogGroupName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;cloudWatchEncryptionEnabled&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;cloudWatchStreamingEnabled&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;kmsKeyId&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[FULL ARN OF THE KMS KEY]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;runAsEnabled&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;runAsDefaultUser&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;idleSessionTimeout&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;20&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;maxSessionDuration&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;shellProfile&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;windows&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;linux&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Apply this SSM Document to the account using the &lt;a href=&#34;https://aws.amazon.com/systems-manager/features/#Session_Manager&#34; title=&#34;AWS Systems Manager Session Manager&#34;&gt;Session Manager&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws ssm update-document &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --name &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SSM-SessionManagerRunShell&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --content &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;file://SessionManagerRunShell.json&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --document-version &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\$LATEST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point everything is inplace to make this work. Except for the IAM permissions on the instance profile. So to get this to work you will need the following permissions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Attach the &lt;code&gt;arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore&lt;/code&gt; managed policy.&lt;/li&gt;
&lt;li&gt;Allow the &amp;ldquo;s3:GetEncryptionConfiguration&amp;rdquo; action on the &lt;code&gt;arn:aws:s3:::[NAME OF THE BUCKET]&lt;/code&gt; resource.&lt;/li&gt;
&lt;li&gt;Allow the &amp;ldquo;s3:PutObject&amp;rdquo; and &amp;ldquo;s3:PutObjectAcl&amp;rdquo; action on the &lt;code&gt;arn:aws:s3:::[NAME OF THE BUCKET]/*&lt;/code&gt; resource.&lt;/li&gt;
&lt;li&gt;Allow the &amp;ldquo;kms:Decrypt&amp;rdquo; and &amp;ldquo;kms:GenerateDataKey&amp;rdquo; action on the &lt;code&gt;[FULL ARN OF THE KMS KEY]&lt;/code&gt; resource.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these permissions in place you are able to start an SSH shell.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you use &lt;a href=&#34;https://aws.amazon.com/systems-manager/features/#Session_Manager&#34; title=&#34;AWS Systems Manager Session Manager&#34;&gt;Session Manager&lt;/a&gt; you not only remove the need to expose port 22. Or don’t need to rotate keys when a team-mate leaves. You get a lot more! Your SSH session is encrypted using a customer encrypted KMS key. That you manage and maintain!
Next to that, once the session is closed the complete session is stored on S3. And again encrypted using a customer encrypted KMS key. That you manage and maintain! This allows you to archive shell sessions for later use. Giving you full auditability and improving your security posture.
And because you don’t need to bother your team-mates with new keys for this week. Easier for your team to use as well.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Testing your Landing Zone when using AWS Deployment Framework</title>
      <link>https://conijn.io/blog/2022/01/testing-your-landing-zone-when-using-adf/</link>
      <pubDate>Wed, 19 Jan 2022 21:06:15 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2022/01/testing-your-landing-zone-when-using-adf/</guid>
      <description>&lt;p&gt;If you are thinking of setting up a landing zone using &lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework/&#34;&gt;ADF&lt;/a&gt; in AWS you will need to read this. And why? Because I will tell you why you need to set up two landing zones. And how you can use them to test your landing zones.&lt;/p&gt;
&lt;h2 id=&#34;what-is-a-landing-zone&#34;&gt;What is a landing zone?&lt;/h2&gt;
&lt;p&gt;A landing zone is a solution that help you to set up a secure, multi-account AWS environment. There are a couple of ways how you can do this, for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://aws.amazon.com/solutions/implementations/aws-landing-zone/&#34;&gt;AWS Landing Zone&lt;/a&gt; (predecessor of AWS Control Tower)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://aws.amazon.com/controltower/&#34;&gt;AWS Control Tower&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework/&#34;&gt;AWS Deployment Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Build your own…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post I am focussing on &lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework/&#34;&gt;ADF&lt;/a&gt;. The benefit of &lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework/&#34;&gt;ADF&lt;/a&gt; over &lt;a href=&#34;https://aws.amazon.com/controltower/&#34;&gt;AWS Control Tower&lt;/a&gt; is that you have more control over the framework. All resources run in your accounts and are under your control. &lt;a href=&#34;https://aws.amazon.com/controltower/&#34;&gt;AWS Control Tower&lt;/a&gt; is a managed service by AWS. Giving less flexibility but remove maintenance burden.&lt;/p&gt;
&lt;h2 id=&#34;what-is-adf&#34;&gt;What is ADF?&lt;/h2&gt;
&lt;p&gt;From the &lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework/&#34;&gt;ADF&lt;/a&gt; documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The AWS Deployment Framework (ADF) is an extensive and flexible framework to manage and deploy resources across multiple AWS accounts and regions within an AWS Organization.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So a simple example of this would be, if you need to provide a new &lt;a href=&#34;https://en.wikipedia.org/wiki/Development,_testing,_acceptance_and_production&#34;&gt;DTAP&lt;/a&gt; environment for a new workload. You will add some details to a YAML file and commit/push/review/merge it. Then you &lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework/&#34;&gt;ADF&lt;/a&gt; does its magic. It will create the AWS Accounts on your behalf and bootstrap them. And you end up with a couple of AWS Accounts that have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A pre-provisioned network setup.&lt;/li&gt;
&lt;li&gt;Security controls&lt;/li&gt;
&lt;li&gt;etc etc
Please note, you need to build what it bootstraps into these accounts your self. &lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework/&#34;&gt;ADF&lt;/a&gt; only makes it easy to manage what templates are deployed where.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;so-what-is-there-to-test&#34;&gt;So what is there to test?&lt;/h2&gt;
&lt;p&gt;Within &lt;a href=&#34;https://aws.amazon.com/organizations/&#34;&gt;AWS Organizations&lt;/a&gt; you can apply &lt;a href=&#34;https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html&#34;&gt;Service control policies (SCPs)&lt;/a&gt;. All AWS Accounts under the OU (Organization Unit) with the SCP will be subjective to this SCP. What if you need to make a change in this SCP? How can you test this change?
&lt;a href=&#34;https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html&#34;&gt;SCPs&lt;/a&gt; are not the only things you might want to test. Remember that I mentioned that &lt;a href=&#34;https://github.com/awslabs/aws-deployment-framework/&#34;&gt;ADF&lt;/a&gt; is also bootstrapping the accounts? That could be a VPC with subnets for networking. How do you ensure that the change that you made works as intended?
When merging to your main branch. It will trigger a rollout process depending on your configuration.&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;You can solve this by rolling out a &amp;ldquo;test&amp;rdquo; organization. It will mirror your production organization. But you can scope it down to a smaller set. As long as you cover the cases that are important to your organization. The idea is that when you make a change you first deploy it on the &amp;ldquo;test&amp;rdquo; organization. Once deployed you do some tests and roll it out in production.
Setting up a &amp;ldquo;test&amp;rdquo; organization when you already have a full-fledged landing zone. That is harder than doing it from the beginning. It also helps with the mindset of the team maintaining of the landing zone that there is a &amp;ldquo;test&amp;rdquo; environment.
And depending on your own use-case you can make a lot of different choices how to set this up. For example, moving the dev and test environments to the &amp;ldquo;test&amp;rdquo; organization.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Start thinking about how you can test your landing zone. Setting up 2 landing zones will help you with that. Use a test driven approach when building your landing zone. It will be much harder to add testing afterwards.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>This is how you can test your cfn-guard rules</title>
      <link>https://conijn.io/blog/2021/12/this-is-how-you-can-test-your-cfn-guard-rules/</link>
      <pubDate>Wed, 29 Dec 2021 21:20:51 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2021/12/this-is-how-you-can-test-your-cfn-guard-rules/</guid>
      <description>&lt;p&gt;In my previous blog, &lt;a href=&#34;https://conijn.io/blog/2021/11/how-do-you-prove-that-your-infrastructure-is-compliant/&#34;&gt;How do you prove that your infrastructure is compliant&lt;/a&gt;.
I explained how you can prove your infrastructure is compliant using &lt;a href=&#34;https://github.com/aws-cloudformation/cloudformation-guard&#34;&gt;CloudFormation Guard&lt;/a&gt;.
But, how do you write those rules? And even more important, how do you test your rules? If you look at the repository &lt;a href=&#34;https://github.com/aws-cloudformation/cloudformation-guard&#34;&gt;CloudFormation Guard&lt;/a&gt;.
You will notice that the project itself offers a testing framework.
Alright! Let’s build a ruleset and write some tests for it!&lt;/p&gt;
&lt;h2 id=&#34;lets-build-a-ruleset-for-s3&#34;&gt;Let’s build a ruleset for S3&lt;/h2&gt;
&lt;p&gt;We will start with a sample for S3, create a file called: &lt;code&gt;rules/s3.guard&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;let buckets = Resources.*[ Type == &amp;#39;AWS::S3::Bucket&amp;#39; ]

rule BucketEncryption when %buckets !empty {
  %buckets.Properties {
    BucketEncryption.ServerSideEncryptionConfiguration[*] {
      ServerSideEncryptionByDefault.SSEAlgorithm IN [&amp;#34;AES-256&amp;#34;, &amp;#34;aws:kms&amp;#34;] &amp;lt;&amp;lt;Ensure all S3 buckets use encryption-at-rest&amp;gt;&amp;gt;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we will write the test, called: &lt;code&gt;rules/s3_tests.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Skip if not present&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;: {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;expectations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;SKIP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;No encryption used&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;MyBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;expectations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;FAIL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bucket with KMS Encryption&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;MyBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionConfiguration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               - &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionByDefault&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;SSEAlgorithm&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;aws:kms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;expectations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;PASS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bucket with AES-256 Encryption&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;MyBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionConfiguration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               - &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionByDefault&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;SSEAlgorithm&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AES-256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;expectations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;PASS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bucket with UNKNOWN Encryption&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;MyBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionConfiguration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               - &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionByDefault&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#f92672&#34;&gt;SSEAlgorithm&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;UNKNOWN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;expectations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;FAIL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it’s time to run the tests:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cfn-guard test --rules-file rules/s3.guard --test-data rules/s3_tests.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will give the following output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;Skip if not present&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = SKIP, Evaluated = SKIP
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;No encryption used&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = FAIL, Evaluated = FAIL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;Bucket with KMS Encryption&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = PASS, Evaluated = PASS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;Bucket with AES-256 Encryption&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = PASS, Evaluated = PASS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;Bucket with UNKNOWN Encryption&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = FAIL, Evaluated = FAIL
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might be thinking, that looks nice! But the output is quite verbose, and you need to execute a command per rule/test set.
It gets even worse when a test does fail. For this example I altered the &lt;code&gt;rules/s3.guard&lt;/code&gt; file to use &lt;code&gt;aws:kmz&lt;/code&gt; instead of &lt;code&gt;aws:kms&lt;/code&gt;.
Then it looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;Skip if not present&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = SKIP, Evaluated = SKIP
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;No encryption used&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = FAIL, Evaluated = FAIL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;Bucket with KMS Encryption&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  FAILED Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = PASS, Evaluated = FAIL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;Bucket with AES-256 Encryption&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = PASS, Evaluated = PASS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Case #5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: &amp;#34;Bucket with UNKNOWN Encryption&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  PASS Rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BucketEncryption: Expected = FAIL, Evaluated = FAIL
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you look at &lt;code&gt;Test Case #3&lt;/code&gt; it says: &lt;code&gt;BucketEncryption: Expected = PASS, Evaluated = FAIL&lt;/code&gt;. That makes it hard
to spot what test failed.&lt;/p&gt;
&lt;h2 id=&#34;cfn-guard-test&#34;&gt;cfn-guard-test&lt;/h2&gt;
&lt;p&gt;For this reason I created a python package called &lt;a href=&#34;https://github.com/Nr18/cfn-guard-test&#34;&gt;cfn-guard-test&lt;/a&gt;. This package
makes it easier to run many rule/test sets. This is especially useful in the CI/CD pipelines.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please read the repository for the installation instructions.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Let’s run the tests again!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cfn-guard-test --verbose
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output now looks something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;===== Analyzing Results =====
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Running rules/s3_tests.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Passed 5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Failed 0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is a nice and clean overview. It lists the number of passed and failed tests. For the next example I altered the&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rules/s3.guard&lt;/code&gt; file to use &lt;code&gt;aws:kmz&lt;/code&gt; instead of &lt;code&gt;aws:kms&lt;/code&gt;. This will again trigger a failure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;===== Analyzing Results =====
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Running rules/s3_tests.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Passed 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Failed 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Rule BucketEncryption failed on #3 &amp;#34;Bucket with KMS Encryption&amp;#34; in rules/s3.guard
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Well you have to agree. That immediately sums up how many passed and failed. And more it shows that the &lt;code&gt;BucketEncryption&lt;/code&gt;
rule fails. And it failed on the test case &amp;ldquo;Bucket with KMS Encryption&amp;rdquo;.
You don’t want to spend time figuring out what is wrong. This is why we use tooling, to make our life easier.
This tool also has the ability to generate JUnit report.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cfn-guard-test --verbose &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --junit-path &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reports/cfn-guard.xml&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/Nr18/cfn-guard-test&#34;&gt;cfn-guard-test&lt;/a&gt; can help you run many tests. And get an aggregated report of
the results. This tool will help you detect bugs in your rules easier and quicker.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How do you prove that your infrastructure is compliant</title>
      <link>https://conijn.io/blog/2021/11/how-do-you-prove-that-your-infrastructure-is-compliant/</link>
      <pubDate>Tue, 23 Nov 2021 10:30:25 +0100</pubDate>
      
      <guid>https://conijn.io/blog/2021/11/how-do-you-prove-that-your-infrastructure-is-compliant/</guid>
      <description>&lt;p&gt;When you are building your cloud infrastructure. And you have to meet certain compliance standards. You can achieve this with preventive and detective controls.
In this blog post I want to focus on the preventive control. How you can stop the deployment pipeline when the infrastructure is non-compliant. And how you can visualize this using CloudWatch Reports.
Let’s say that our company has the following rule:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;S3 buckets must be KMS encrypted&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;How can you block a deployment that would deploy a S3 bucket without KMS encryption? The answer is using &lt;a href=&#34;https://github.com/aws-cloudformation/cloudformation-guard&#34;&gt;CloudFormation Guard&lt;/a&gt;.
With CloudFormation Guard you can write preventive compliance rules. You can then use to check your CloudFormation templates.
Create a &lt;code&gt;template.yaml&lt;/code&gt; file with the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install &lt;a href=&#34;https://github.com/aws-cloudformation/cloudformation-guard#installation&#34;&gt;CloudFormation Guard&lt;/a&gt; using the provided instructions.
Then you need to create a &lt;code&gt;s3.guard&lt;/code&gt; file with the following content:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Select all resources of the type AWS::S3::Bucket
let buckets = Resources.*[ Type == &amp;#39;AWS::S3::Bucket&amp;#39; ]

# Only when there are reources of the type AWS::S3::Bucket
rule S3_bucket_encryption_at_rest when %buckets !empty {
  # Check that each resource has the SSEAlgorithm set to aws:kms
  %buckets.Properties {
    BucketEncryption.ServerSideEncryptionConfiguration[*] {
      ServerSideEncryptionByDefault.SSEAlgorithm == &amp;#39;aws:kms&amp;#39; &amp;lt;&amp;lt;S3 Bucket must use KMS encryption.&amp;gt;&amp;gt;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;For more information on the the rules and the format. I recommend reading &lt;a href=&#34;https://aws.amazon.com/blogs/mt/introducing-aws-cloudformation-guard-2-0/&#34;&gt;Introducing AWS CloudFormation Guard 2.0&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Alright, now we can validate the template with the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cfn-guard validate --rules s3.guard --data template.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;images/screen1-900x312.png&#34; alt=&#34;Failed cfn-guard invocation&#34;&gt;
If you now change the &lt;code&gt;template.yml&lt;/code&gt; to the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;MyBucket&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;BucketEncryption&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionConfiguration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;ServerSideEncryptionByDefault&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;SSEAlgorithm&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;aws:kms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And you execute the command again it will now pass.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/screen2-900x312.png&#34; alt=&#34;Successful cfn-guard invocation&#34;&gt;&lt;/p&gt;
&lt;p&gt;If you execute this step in your deployment pipeline. It will stop and fail your CodeBuild project. Preventing the non-compliant infrastructure to be deployed.
But when you have a deployment that is blocked. You don’t want to look up the CloudWatch logs of the build. And you might have many guard files that you want to check.
To make it easier for yourself you can use &lt;a href=&#34;https://aws.amazon.com/blogs/devops/test-reports-with-aws-codebuild/&#34;&gt;CodeBuild Reports&lt;/a&gt;. With these reports you get a visual overview of what rule passed and what role failed.
&lt;img src=&#34;images/test-result-900x542.png&#34; alt=&#34;Example of a CodeBuild Report&#34;&gt;
But by default &lt;code&gt;cfn-guard&lt;/code&gt; does not generate compatible reports. And when you execute it, and has failures it will exit with an exit code of 1.
I have written a conversion tool to help you with this called &lt;a href=&#34;https://github.com/Nr18/report2junit&#34;&gt;report2junit&lt;/a&gt;.
It merges and coverts the JSON reports into the JUnit format used by CodeBuild Reports. By using the &lt;code&gt;|| true&lt;/code&gt; postfix the CodeBuild execution will continue.
Instead, the &lt;code&gt;report2junit&lt;/code&gt; tool will return an exit code of 1 after it generated the report. And because the report is there when it fails you can use that to see what rule failed.&lt;/p&gt;
&lt;p&gt;This removes the need to look at the CodeBuild Logs.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;phases&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;install&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;runtime-versions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;python&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;commands&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;pip install -Ur requirements.txt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;mkdir -p reports&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;commands&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;cfn-guard validate --rules s3.guard --data template.yml --output-format json --show-summary none &amp;gt; reports/s3-guard.json || true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;cfn-guard validate --rules dynamodb.guard --data template.yml --output-format json --show-summary none &amp;gt; reports/dynamodb-guard.json || true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;post_build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;commands&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;report2junit reports/s3-guard.json reports/dynamodb-guard.json --destination-file reports/cfn-guard.xml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;reports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;Tests&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;base-directory&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;./reports&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;file-format&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;JUNITXML&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;files&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;cfn-guard.xml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you use &lt;a href=&#34;https://github.com/aws-cloudformation/cloudformation-guard&#34;&gt;CloudFormation Guard&lt;/a&gt; in combination with CodeBuild Reports it makes it easier to see what rules have failed and keeps a history.
When you have a solid set of compliance rules. It gives you a report that you can use to prove that the build of the infrastructure was compliant. You are also able to prevent non-compliant code rollout in production.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Python and relative imports in AWS Lambda Functions</title>
      <link>https://conijn.io/blog/2021/10/python-and-relative-imports-in-aws-lambda-functions/</link>
      <pubDate>Mon, 25 Oct 2021 12:11:42 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2021/10/python-and-relative-imports-in-aws-lambda-functions/</guid>
      <description>&lt;p&gt;When writing an AWS Lambda function, it’s quite possible that you get to the point where the file becomes too big. So, what do you do? You create a second file and refactor your code to do a relative import. Then, the only step left is to deploy your code and run it. And then it fails… Have you been here before? Then I might have some sound advice for you.
If you look at the &lt;a href=&#34;https://www.python.org/dev/peps/pep-0008/#imports&#34;&gt;PEP 8 — Style Guide for Python Code&lt;/a&gt;.&lt;br&gt;
You will read that &lt;em&gt;absolute imports are recommended. But, explicit relative imports are an acceptable alternative to absolute imports&lt;/em&gt;.&lt;br&gt;
This means that you should favor absolute imports. And only use relative imports within a package.&lt;/p&gt;
&lt;h2 id=&#34;sample-code&#34;&gt;Sample code&lt;/h2&gt;
&lt;p&gt;Let’s create a hello world lambda function to show this. So we have the following code in a file called &lt;code&gt;hello_world/app.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda_handler&lt;/span&gt;(event, context):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;statusCode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;: json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps({&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;}),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For simplicity reasons I used the &lt;a href=&#34;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html&#34;&gt;AWS Serverless Application Model&lt;/a&gt;.&lt;br&gt;
The templates contain the following resource:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;HelloWorldFunction&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Serverless::Function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;CodeUri&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;hello_world/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Handler&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;app.lambda_handler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Runtime&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;python3.9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Events&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;HelloWorld&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Api&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/hello&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Method&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;get&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The most important thing is the &lt;code&gt;Handler&lt;/code&gt; definition. The value &lt;code&gt;app.lambda_handler&lt;/code&gt; translates to:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use the &lt;code&gt;app.py&lt;/code&gt; file and invoke the &lt;code&gt;lambda_handler&lt;/code&gt; method.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;We will now move the payload to a file called &lt;code&gt;hello_world/response.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_response&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; dict:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And update the &lt;code&gt;hello_world/app.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; .response &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; get_response
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda_handler&lt;/span&gt;(event, context):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;statusCode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;: json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(get_response()),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You run your test and it works, nice! So we will now deploy our code and that it also works. And it fails…&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ERROR] Runtime.ImportModuleError: Unable to import module &amp;#39;app&amp;#39;: attempted relative import with no known parent package
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Traceback (most recent call last):
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The content of the &lt;code&gt;hello_world&lt;/code&gt; folder ends up in the &lt;code&gt;/var/task/&lt;/code&gt; folder. And not the folder itself.&lt;br&gt;
So it might look like that the code is in a package but from the Lambda runtime it’s not.&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;You can solve this by creating a package. How you might ask? Create a &lt;code&gt;hello_world&lt;/code&gt; folder in the &lt;code&gt;hello_world&lt;/code&gt; folder.&lt;br&gt;
Move all files except the &lt;code&gt;requirements.txt&lt;/code&gt; into the created folder.
&lt;img src=&#34;images/folder_structure.png&#34; alt=&#34;Example of the folder structure&#34; title=&#34;Your folder structure should now look like this&#34;&gt;
Next you need to update the &lt;code&gt;Handler&lt;/code&gt; in the template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;Resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;HelloWorldFunction&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;AWS::Serverless::Function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;CodeUri&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;hello_world/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Handler&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;hello_world.app.lambda_handler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Runtime&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;python3.9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;Events&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;HelloWorld&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Api&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;Properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/hello&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;Method&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;get&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the value of the &lt;code&gt;Handler&lt;/code&gt; translates to:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the &lt;code&gt;hello_world&lt;/code&gt; package you use the &lt;code&gt;app.py&lt;/code&gt; file and invoke the &lt;code&gt;lambda_handler&lt;/code&gt; method.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;When you deploy you will now notice that the relative import works.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I would always tell you to deploy your python code in a package. It makes it easier to use many files and organize your code better.&lt;br&gt;
It also allows you to separate the Lambda invocation logic from your business logic. Making it easier to test and maintain.
Image by &lt;a href=&#34;https://pixabay.com/users/geralt-9301/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=6585578&#34;&gt;Gerd Altmann&lt;/a&gt; from &lt;a href=&#34;https://pixabay.com/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=6585578&#34;&gt;Pixabay&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Make your life easier using Makefiles</title>
      <link>https://conijn.io/blog/2021/09/make-your-life-easier-using-makefiles/</link>
      <pubDate>Mon, 27 Sep 2021 12:19:04 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2021/09/make-your-life-easier-using-makefiles/</guid>
      <description>&lt;p&gt;In this blog post I will show how you can use a &lt;code&gt;Makefile&lt;/code&gt; to make your life easier.
In this blog I will use CDK with python. But the principle of the &lt;code&gt;Makefile&lt;/code&gt; will work on almost any project.
When you start a new PoC you do not have a full CI/CD environment in place yet.
So how can you deploy your CDK application? And make it easy to add the CI/CD pipeline at a later point?&lt;/p&gt;
&lt;h2 id=&#34;lets-begin&#34;&gt;Let’s begin!&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I used CDK 1.124.0 (build 65761fe) for this blog post.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;First we will need a CDK project. So we will create a working folder named: &lt;code&gt;makefile_cdk_blog&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir makefile_cdk_blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd makefile_cdk_blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk init --language python
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir tests
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point you will have an empty CDK project. Most projects have dependencies. You can divide them into 2 categories:
* Project dependencies, dependencies needed for the project, located in &lt;code&gt;setup.py&lt;/code&gt;.
* Development dependencies, dependencies needed to develop on the project, located in &lt;code&gt;requirements.txt&lt;/code&gt;.
Let’s start with adding some development dependencies in the &lt;code&gt;requirements.txt&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-e .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;black
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pytest-cov
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pytest-black
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;setuptools
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we need to configure &lt;a href=&#34;https://docs.pytest.org/&#34;&gt;pytest&lt;/a&gt;. Add the following to a new file &lt;code&gt;setup.cfg&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-buildoutcfg&#34; data-lang=&#34;buildoutcfg&#34;&gt;[tool:pytest]
testpaths = tests

[coverage:run]
branch = True
source = makefile_cdk_blog

[coverage:report]
show_missing = true
fail_under = 100
exclude_lines =
    pragma: no cover
    if __name__ == .__main__.:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The only thing left is the &lt;code&gt;Makefile&lt;/code&gt; itself. Create a file called &lt;code&gt;Makefile&lt;/code&gt; in the root of your project. Make sure it has the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Makefile&#34; data-lang=&#34;Makefile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SHELL &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; /bin/bash -c
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;VIRTUAL_ENV &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;PWD&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;/.venv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export &lt;/span&gt;BASH_ENV&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;VIRTUAL_ENV&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;$(VIRTUAL_ENV)&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    python3 -m venv &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;VIRTUAL_ENV&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.PHONY&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; install clean synth diff deploy test lint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;install&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;VIRTUAL_ENV&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -d &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;VIRTUAL_ENV&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;VIRTUAL_ENV&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -d .pytest_cache &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf .pytest_cache &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -d cdk.out &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf cdk.out &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; -f .coverage &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm .coverage &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;synth&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cdk synth
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;diff&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cdk diff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;deploy&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; test
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cdk deploy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; lint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pytest --cov --cov-report term-missing
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;lint&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    black .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;VERBOSE&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;.SILENT:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alright we are good to go! First we want to commit our changes, but before we can do that we should test them.
We added dependencies, so we need to install those first! But since we have a make target for that now we can use it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I will walk you through what is happening on the background:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First the &lt;code&gt;Makefile&lt;/code&gt; will select bash as a shell.&lt;/li&gt;
&lt;li&gt;Define the location of the virtual environment.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;BASH_ENV&lt;/code&gt; variable has a dependency on the &lt;code&gt;$(VIRTUAL_ENV)&lt;/code&gt; target. It will create a virtual environment on the defined location.&lt;/li&gt;
&lt;li&gt;The virtual environment is then exported in the &lt;code&gt;BASH_ENV&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;Then we will execute the &lt;code&gt;install&lt;/code&gt; target. All commands are now executed in the context of the virtual environment.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip install&lt;/code&gt; will install all dependencies in the &lt;code&gt;requirements.txt&lt;/code&gt; and in the &lt;code&gt;setup.py&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can now run the &lt;code&gt;test&lt;/code&gt; target to see if everything works as expected.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make test
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You would see something like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/pytest-result-900x334-1.png&#34; alt=&#34;You can see that the test succeeded but the command failed&#34; title=&#34;PyTest results&#34;&gt;&lt;/p&gt;
&lt;p&gt;So what happened? We invoked the &lt;code&gt;test&lt;/code&gt; target. This target has a dependency on the &lt;code&gt;lint&lt;/code&gt; target.
The &lt;code&gt;lint&lt;/code&gt; target contains &lt;code&gt;black .&lt;/code&gt; and this command formats all the python code. Afterwards, it will continue and execute the commands in the &lt;code&gt;test&lt;/code&gt; target.
So the test is failing because we did not meet the configured code coverage threshold. That is fine for now so let’s commit!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;chore: setup project using a Makefile&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;deployment-time&#34;&gt;Deployment time!&lt;/h2&gt;
&lt;p&gt;Before we can deploy I assume that you configured your &lt;a href=&#34;https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html&#34;&gt;AWS credentials properly&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export AWS_PROFILE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;my-profile-name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make deploy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And it failed? Because the &lt;code&gt;deploy&lt;/code&gt; target has a dependency on the &lt;code&gt;test&lt;/code&gt; target. So the test target is being executed first but that fails.
This is the real power of using a &lt;code&gt;Makefile&lt;/code&gt;. You have simple names for actions that you want to do. For example: test, deploy, etc.
And in this case you should not deploy if your test fail.
You can extend these targets with different checks/commands.
For example, you could synthesize the template and then use the following tools:
* &lt;a href=&#34;https://github.com/stelligent/cfn_nag&#34;&gt;cfn_nag&lt;/a&gt;
* &lt;a href=&#34;https://github.com/aws-cloudformation/cloudformation-guard&#34;&gt;cloudformation-guard&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Makefiles allow you to standardize actions across projects. They can execute commands in virtual environments for you. And if kept simple they are easy to read!&lt;/p&gt;
&lt;p&gt;Update: Improve your Makefile even more by &lt;a href=&#34;https://conijn.io/blog/2022/09/implement-a-help-target-in-your-makefile/&#34;&gt;adding a help target&lt;/a&gt;!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Increasing your development speed with AWS Lambda Powertools</title>
      <link>https://conijn.io/blog/2021/08/increasing-your-development-speed-with-aws-lambda-powertools/</link>
      <pubDate>Tue, 24 Aug 2021 21:04:05 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2021/08/increasing-your-development-speed-with-aws-lambda-powertools/</guid>
      <description>&lt;p&gt;It is not possible to remember all methods and properties from all classes that you write and use.
That is the reason why I like to have type hinting in my IDE. In fact I will spend time to make sure it works. I even wrote a &lt;a href=&#34;https://conijn.io/blog/2021/07/use-sam-go-and-vs-code/&#34;&gt;blog post&lt;/a&gt; about how to set it up when I started experimenting with a new language.
In this post I will provide you with a couple of tips that help you develop faster. By leveraging type hinting using the &lt;a href=&#34;https://awslabs.github.io/aws-lambda-powertools-python/latest/&#34;&gt;AWS Lambda Powertools&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;lambdacontext&#34;&gt;LambdaContext&lt;/h2&gt;
&lt;p&gt;When you are developing AWS Lambda functions you receive an &lt;code&gt;event&lt;/code&gt; and a &lt;code&gt;context&lt;/code&gt; property. If you need metadata from the context you could read the documentation or use type hinting.
&lt;img src=&#34;images/utilities_data_classes-900x310.png&#34; alt=&#34;LambdaContext type hinting example&#34;&gt;
As you can see in the example, the IDE will help you find the correct method or property. But what about the &lt;code&gt;event&lt;/code&gt;? The example tells you that we expect a &lt;code&gt;Dict[str, Any]&lt;/code&gt; but this depends on how you invoke the Lambda function.&lt;/p&gt;
&lt;h2 id=&#34;event-source-data-classes&#34;&gt;Event Source Data Classes&lt;/h2&gt;
&lt;p&gt;Most of the time you know how you invoke your Lambda function. For example an API Gateway proxy integration could trigger the function:
&lt;img src=&#34;images/utilities_data_classes-900x310.png&#34; alt=&#34;API Gateway proxy integration type hinting example&#34;&gt;
Next to using the data class you could also use a decorator. Then use the type hint in the method signature.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.data_classes &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; event_source, APIGatewayProxyEvent
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@event_source&lt;/span&gt;(data_class&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;APIGatewayProxyEvent)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda_handler&lt;/span&gt;(event: APIGatewayProxyEvent, context):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;helloworld&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;http_method &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        do_something_with(event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;body, user)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For a full list see the &lt;a href=&#34;https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/data_classes/&#34;&gt;supported event sources&lt;/a&gt; page.&lt;/p&gt;
&lt;h2 id=&#34;what-about-my-custom-events&#34;&gt;What about my custom events?&lt;/h2&gt;
&lt;p&gt;You could also invoke a Lambda function with a custom payload. This event is not supported as a data class.
Let say we receive an order with one or more items. By using the &lt;a href=&#34;https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/parser/&#34;&gt;parser&lt;/a&gt; you can define the event in a model. And you can use that model for type hinting.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.parser &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; event_parser, BaseModel, ValidationError
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; LambdaContext
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderItem&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id: int
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    quantity: int
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description: str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Order&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id: int
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description: str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    items: List[OrderItem] &lt;span style=&#34;color:#75715e&#34;&gt;# use nesting models&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    optional_field: Optional[str] &lt;span style=&#34;color:#75715e&#34;&gt;# this field may or may not be available when parsing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@event_parser&lt;/span&gt;(model&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Order)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;(event: Order, context: LambdaContext):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;description)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;items)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    order_items &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [items &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; item &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;items]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;but-my-custom-event-is-in-an-existing-event-source&#34;&gt;But my custom event is in an existing event source&lt;/h2&gt;
&lt;p&gt;Well in that case you need to supply the event source as an envelope.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.parser &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; event_parser, parse, BaseModel, envelopes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; LambdaContext
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;UserModel&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    username: str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    password1: str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    password2: str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Same behavior but using our decorator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@event_parser&lt;/span&gt;(model&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;UserModel, envelope&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;envelopes&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;EventBridgeEnvelope)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;(event: UserModel, context: LambdaContext):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;assert&lt;/span&gt; event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;password1 &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;password2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you found this blog useful I recommend reading the official &lt;a href=&#34;https://awslabs.github.io/aws-lambda-powertools-python/latest/&#34;&gt;documentation&lt;/a&gt;. It’s a small investment that will pay out in nice and clean code.
No need to remember all the methods and properties and what types the expect and return. An IDE can do that for you so that you can focus on the actual business logic.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to use AWS SAM with Go in Visual Studio Code</title>
      <link>https://conijn.io/blog/2021/07/use-sam-go-and-vs-code/</link>
      <pubDate>Tue, 27 Jul 2021 20:25:47 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2021/07/use-sam-go-and-vs-code/</guid>
      <description>&lt;p&gt;Setup &lt;a href=&#34;https://code.visualstudio.com&#34; title=&#34;Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications. Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows.&#34;&gt;Visual Studio Code&lt;/a&gt; as your IDE to build and deploy an AWS SAM stack using &lt;a href=&#34;https://golang.org&#34; title=&#34;Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.&#34;&gt;Go&lt;/a&gt;.
I recently started to experiment with &lt;a href=&#34;https://golang.org&#34; title=&#34;Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.&#34;&gt;Go&lt;/a&gt; and the best way to learn is to play with it and build something. So I decided to build a simple application. During this process I ran into the problem that my IDE did not like having many go modules within one project.&lt;/p&gt;
&lt;h2 id=&#34;project-layout&#34;&gt;Project layout&lt;/h2&gt;
&lt;p&gt;With AWS SAM you can build and deploy your stack or via a CI/CD pipeline, the process is the same. First you perform a &lt;code&gt;sam build&lt;/code&gt;. This will compile the source code for each &lt;code&gt;AWS::Serverless::Function&lt;/code&gt; in the &lt;code&gt;template.yaml&lt;/code&gt;. Followed by &lt;code&gt;sam deploy&lt;/code&gt;. This will upload the artifacts and deploy the template using AWS CloudFormation.
The problem that I faced was that each function needs a &lt;code&gt;go.mod&lt;/code&gt; in the source folder. Visual Studio Code expects a single &lt;code&gt;go.mod&lt;/code&gt; file in the project.
&lt;img src=&#34;images/folder-structure-900x281.png&#34; alt=&#34;Project Structure&#34;&gt;
As a result imports in the unit test do not get recognized:
&lt;img src=&#34;images/test-example-900x123.png&#34; alt=&#34;Sample code of the unit test without workspace&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;RequestEvent&lt;/code&gt; struct.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;handler&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ResponseEvent&lt;/code&gt; struct.
The reason for this is that the IDE has set the &lt;code&gt;GOPATH&lt;/code&gt; to the root of the project. Therefor the imports are not correct. By using &lt;a href=&#34;https://github.com/golang/tools/blob/master/gopls/doc/workspace.md&#34; title=&#34;Workspace folders allow you to scope the modules within one project.&#34;&gt;Workspace Folders&lt;/a&gt; you can change this behavior so that each folder has it’s own scope.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;From Go 1.16+ the default build mode has switched from &lt;code&gt;GOPATH&lt;/code&gt; to &lt;a href=&#34;https://golang.org/ref/mod&#34; title=&#34;Modules are how Go manages dependencies.&#34;&gt;Go modules&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;To do this you need to create a &lt;code&gt;workspace.code-workspace&lt;/code&gt; file. Place it in the root of your project with the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;folders&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;functions/api&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;functions/confirm&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;functions/ship&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;functions/stock&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now because each Go module lives within it’s own scope the imports are correct. As a result you are able to navigate through the code using the &lt;code&gt;⌘ click&lt;/code&gt; action.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you deal with many Go modules within one project it is important to configure your IDE. This will improve readability, reduces errors and increases your development speed!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to search and find related logs in AWS Log Groups</title>
      <link>https://conijn.io/blog/2021/07/searching-across-aws-log-groups/</link>
      <pubDate>Fri, 09 Jul 2021 15:22:09 +0200</pubDate>
      
      <guid>https://conijn.io/blog/2021/07/searching-across-aws-log-groups/</guid>
      <description>&lt;p&gt;You can search across log groups by setting up a correlation id on your log lines. You can then use CloudWatch Logs Insights to query all related log messages.
If you are dealing with different lambda functions in a project, you might not have a logging system yet. Finding and troubleshooting using Cloudwatch Logs could become cumbersome. Especially if you try to track a flow through the different log groups.
In &lt;a href=&#34;https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AnalyzingLogData.html&#34;&gt;CloudWatch Logs Insights&lt;/a&gt; you can execute queries across log groups. In our case we want to collect related log lines across log groups. To achieve this we need to annotate the log lines with a &lt;code&gt;correlation_id&lt;/code&gt; that ties them together.&lt;/p&gt;
&lt;h2 id=&#34;sample-application&#34;&gt;Sample application&lt;/h2&gt;
&lt;p&gt;We will use the following sample application to show how to setup a correlation id on your log lines. The application looks as followed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;API Gateway that is invoking an AWS Lambda function&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;api&lt;/code&gt; Lambda function places the &lt;code&gt;body&lt;/code&gt; in a SQS Queue&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;callback&lt;/code&gt; Lambda function is receiving messages from the SQS Queue
&lt;img src=&#34;images/architecture-900x413-1.png&#34; alt=&#34;Architecture of the Sample Application&#34;&gt;
Every time that an API call executes, the &lt;code&gt;api&lt;/code&gt;function is invoked. We can annotate the log lines by using the &lt;a href=&#34;https://awslabs.github.io/aws-lambda-powertools-python/latest/&#34;&gt;Lambda Powertools for Python&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Logger
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.logging &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; correlation_paths
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; LambdaContext
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.data_classes &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    event_source,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    APIGatewayProxyEvent,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logger &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Logger(service&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@event_source&lt;/span&gt;(data_class&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;APIGatewayProxyEvent)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@logger.inject_lambda_context&lt;/span&gt;(correlation_id_path&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;correlation_paths&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;API_GATEWAY_REST)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda_handler&lt;/span&gt;(event: APIGatewayProxyEvent, context: LambdaContext) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; dict:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;This message now has a correlation_id set&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We take the &lt;code&gt;requestContext.requestId&lt;/code&gt; from the incoming event. We can use that as a &lt;code&gt;correlation_id&lt;/code&gt;. (This is also returned to the caller as a &lt;code&gt;x-amzn-requestid&lt;/code&gt; header by default by API Gateway.)
To be able to search across log groups using the same correlation id. We need to pass it along with the message as an attribute when we send it to SQS.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send_message(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        QueueUrl&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;QUEUE_URL&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        MessageBody&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;body),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        MessageAttributes&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;correlation_id&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DataType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;String&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;StringValue&amp;#34;&lt;/span&gt;: event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;request_context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;request_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I used a &lt;code&gt;correlation_id&lt;/code&gt; attribute to pass the correlation id with the SQS message.
The queue will trigger the callback function with a batch of messages. You need to loop through the messages in the batch. Process them and remove them from the queue.
Or you use the &lt;a href=&#34;https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/batch/&#34;&gt;sqs_batch_processor&lt;/a&gt; from the powertools.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Logger
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.typing &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; LambdaContext
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.batch &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqs_batch_processor
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_lambda_powertools.utilities.data_classes.sqs_event &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; SQSRecord
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logger &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Logger(service&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;callback&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;record_handler&lt;/span&gt;(record: dict) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    record &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; SQSRecord(record)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Get the correlation id from the message attributes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    correlation_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; record&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;message_attributes[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;correlation_id&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;string_value
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_correlation_id(correlation_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Processing message with &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;correlation_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; as correlation_id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@sqs_batch_processor&lt;/span&gt;(record_handler&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;record_handler)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lambda_handler&lt;/span&gt;(event: dict, context: LambdaContext) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_correlation_id(&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Received a SQSEvent with &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;len(list(event&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;records))&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; records&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;sqs_batch_processor&lt;/code&gt; decorator will call the &lt;code&gt;record_handler&lt;/code&gt; method for each record received. No need for you to handle the deletion of messages.
In the end we do set the correlation id to &lt;code&gt;None&lt;/code&gt;. We do this because the rest of the log lines are not related to the message received.&lt;/p&gt;
&lt;h2 id=&#34;show-me&#34;&gt;Show me!&lt;/h2&gt;
&lt;p&gt;If you call the API using a simple curl command, you can see the &lt;code&gt;correlation_id&lt;/code&gt; (Note: this is also in the headers)
&lt;img src=&#34;images/initial-request-900x129-1.png&#34; alt=&#34;initial request using curl&#34;&gt;
So imagine that something went wrong. We know the &lt;code&gt;correlation_id&lt;/code&gt; so we will use CloudWatch Logs Insights:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fields level, service, message
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| filter correlation_id=&amp;#34;263021ca-fb82-41ff-96ad-2e933aadbe0a&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| sort @timestamp desc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;images/show-logs-900x590-1.png&#34; alt=&#34;Sample query of AWS CloudWatch Logs Insights&#34;&gt;
As you can see there are 3 results 2 lines originated from the &lt;code&gt;api&lt;/code&gt; service and 1 from the &lt;code&gt;callback&lt;/code&gt; service. If have an interest in how to create queries you can read the &lt;a href=&#34;https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html&#34;&gt;CloudWatch Logs Insights query syntax&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;https://awslabs.github.io/aws-lambda-powertools-python/latest/&#34;&gt;Lambda Powertools for Python&lt;/a&gt; make it easy
to add a &lt;code&gt;correlation_id&lt;/code&gt;. By using the structured logs from the powertools you can use each property in the log line to
query on. It has a lot more utilities, so I encourage go check it out!
Not a python developer? There is a &lt;a href=&#34;https://awslabs.github.io/aws-lambda-powertools-java/&#34;&gt;Java&lt;/a&gt;
version and there are some initiatives on the &lt;a href=&#34;https://github.com/awslabs/aws-lambda-powertools-roadmap/projects/1&#34;&gt;roadmap&lt;/a&gt;
to add other languages as well.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>