
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Python on Bastab C</title>
    <link>https://bastabc.com/</link>
    <description>Recent content in Python on Bastab C</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 11 May 2025 00:00:00 +0000</lastBuildDate>
    
	<atom:link href="https://bastabc.com/tags/python/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Infrastructure as Code with AWS CDK - Series Overview</title>
      <link>https://bastabc.com/posts/aws-cdk-infrastructure-as-code/</link>
      <pubDate>Mon, 21 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-infrastructure-as-code/</guid>
      <description>&lt;p&gt;AWS CDK lets you define cloud infrastructure in familiar programming languages - Python, TypeScript, Java - and synthesise it into CloudFormation. Compared to writing raw CloudFormation or Terraform HCL, CDK gives you loops, conditionals, type safety, and the ability to share constructs across teams as libraries.&lt;/p&gt;
&lt;p&gt;This series works through practical CDK patterns using Python, using a consistent use case across all parts to make trade-offs easy to compare.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;use-case&#34;&gt;Use case&lt;/h2&gt;
&lt;p&gt;Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).&lt;/p&gt;
&lt;p&gt;Simple infrastructure by design. The focus is on the CDK patterns, not the resources.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-series&#34;&gt;The series&lt;/h2&gt;
&lt;h3 id=&#34;chapter-1---project-setup-and-bootstrapping&#34;&gt;&lt;a href=&#34;https://bastabc.com/posts/aws-cdk-project-setup-and-bootstrapping/&#34;&gt;Chapter 1 - Project setup and bootstrapping&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Getting a CDK project off the ground - directory structure, virtual environments, bootstrapping an AWS account, and understanding the synth/deploy cycle.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-2---managing-configuration-and-context&#34;&gt;&lt;a href=&#34;https://bastabc.com/posts/aws-cdk-managing-configuration-and-context/&#34;&gt;Chapter 2 - Managing configuration and context&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;How environment-specific values flow into CDK stacks - four approaches covering local static config, local dynamic config, SSM Parameter Store, and Secrets Manager, with trade-offs and code examples for each.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-3---writing-and-sharing-constructs-coming-soon&#34;&gt;Chapter 3 - Writing and sharing constructs &lt;em&gt;(coming soon)&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;Building reusable constructs - L1, L2, L3 abstractions, packaging as a library, and sharing across projects or teams.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-4---testing-cdk-stacks-coming-soon&#34;&gt;Chapter 4 - Testing CDK stacks &lt;em&gt;(coming soon)&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;Unit testing stacks with &lt;code&gt;assertions&lt;/code&gt;, snapshot testing, and integration testing patterns.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-5---cicd-with-cdk-coming-soon&#34;&gt;Chapter 5 - CI/CD with CDK &lt;em&gt;(coming soon)&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;Automating deployments with CDK Pipelines - self-mutating pipelines, promoting changes across environments, and handling rollbacks.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;All examples use Python and CDK v2&lt;/li&gt;
&lt;li&gt;Steps are tested on WSL2/Ubuntu on Windows and native macOS terminal&lt;/li&gt;
&lt;li&gt;More chapters added as I work through these patterns&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>GitHub PR Fix: &#34;Commits must have verified signatures&#34; blocking PR merge</title>
      <link>https://bastabc.com/posts/fix-gpg-signature-verification-blocking-pr-merge/</link>
      <pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/fix-gpg-signature-verification-blocking-pr-merge/</guid>
      <description>&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;p&gt;GitHub branch protection requires all commits to have verified GPG signatures. Two commits at the base of the branch (made before GPG signing was configured) were unsigned, blocking the merge.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;root-cause&#34;&gt;Root cause&lt;/h2&gt;
&lt;p&gt;Commits made before &lt;code&gt;commit.gpgsign=true&lt;/code&gt; was configured in git have no GPG signature. GitHub requires all commits in a PR to be verified - one unsigned commit blocks the merge regardless of approvals.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;steps-to-fix&#34;&gt;Steps to fix&lt;/h2&gt;
&lt;h3 id=&#34;1-identify-unsigned-commits&#34;&gt;1. Identify unsigned commits&lt;/h3&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 log --format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%G? %ad %s&amp;#34;&lt;/span&gt; --date&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;short origin/main..HEAD
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Look for lines starting with &lt;code&gt;N&lt;/code&gt; (no signature). Note the hash of the oldest unsigned commit&amp;rsquo;s parent on main.&lt;/p&gt;
&lt;h3 id=&#34;2-re-sign-all-branch-commits-using-filter-branch&#34;&gt;2. Re-sign all branch commits using filter-branch&lt;/h3&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 filter-branch -f --commit-filter &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;git commit-tree -S &amp;#34;$@&amp;#34;&amp;#39;&lt;/span&gt; -- origin/main..HEAD
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Rewrites all branch commits adding a GPG signature - no content changes, no conflicts.&lt;/p&gt;
&lt;h3 id=&#34;3-verify-no-unsigned-commits-remain&#34;&gt;3. Verify no unsigned commits remain&lt;/h3&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 log --format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%G?&amp;#34;&lt;/span&gt; origin/main..HEAD | Select-String &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;^N&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;p&gt;Should return nothing.&lt;/p&gt;
&lt;h3 id=&#34;4-force-push&#34;&gt;4. Force push&lt;/h3&gt;
&lt;p&gt;Attempt force push via terminal using the configured remote:&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 push --force origin &amp;lt;branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this fails with &amp;ldquo;repository not found&amp;rdquo; (HTTPS remote, no credentials) or &amp;ldquo;Could not read from remote repository&amp;rdquo; (SSH not configured), use a classic PAT with repo scope directly in the 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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push --force https://&amp;lt;username&amp;gt;:&amp;lt;PAT&amp;gt;@github.com/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt;.git &amp;lt;branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;5-update-local-tracking-ref-if-github-desktop-shows-stale-state&#34;&gt;5. Update local tracking ref (if GitHub Desktop shows stale state)&lt;/h3&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 update-ref refs/remotes/origin/&amp;lt;branch&amp;gt; &amp;lt;tip-commit-hash&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Avoid &lt;code&gt;git rebase --exec &amp;quot;git commit --amend -S&amp;quot;&lt;/code&gt; if the branch has merge commits - it drops them and causes conflicts&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;git rebase --rebase-merges&lt;/code&gt; - re-applying old merge commits causes conflicts from the original merges&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filter-branch&lt;/code&gt; rewrites commit objects without replaying merges, which is what&amp;rsquo;s needed here&lt;/li&gt;
&lt;li&gt;Fine-grained PATs do not work for org repositories - use classic PAT only&lt;/li&gt;
&lt;li&gt;After force pushing, avoid &lt;code&gt;git pull&lt;/code&gt; / IDE auto-sync until the PR is merged - it will re-introduce the old unsigned commits via a new merge&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>AWS CDK - Project Setup and Bootstrapping</title>
      <link>https://bastabc.com/posts/aws-cdk-project-setup-and-bootstrapping/</link>
      <pubDate>Sun, 11 May 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-project-setup-and-bootstrapping/</guid>
      <description>&lt;p&gt;This is Chapter 1 of the &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;Infrastructure as Code with AWS CDK&lt;/a&gt; series. Before writing any stack code, the project needs to be initialised and the AWS account needs to be bootstrapped. This covers both.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.9+&lt;/li&gt;
&lt;li&gt;Node.js 18+ (CDK CLI is a Node package)&lt;/li&gt;
&lt;li&gt;AWS CLI v2 configured with valid credentials - see &lt;a href=&#34;https://bastabc.com/posts/connect-to-aws-sso-ssh-into-ec2-instance/&#34;&gt;Connect to AWS SSO and SSH into EC2 Instance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;An AWS account with permissions to create IAM roles, S3 buckets, and CloudFormation stacks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install the CDK CLI globally:&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;npm install -g aws-cdk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;initialise-project&#34;&gt;Initialise project&lt;/h2&gt;
&lt;p&gt;Create a new directory and initialise a CDK Python project inside 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;mkdir my-cdk-app &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cd my-cdk-app
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk init app --language python
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This scaffolds the project with a virtual environment, a sample stack, and a &lt;code&gt;cdk.json&lt;/code&gt; config file.&lt;/p&gt;
&lt;p&gt;Activate the virtual environment and install dependencies:&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;# macOS / Linux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;source .venv/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:#75715e&#34;&gt;# Windows (PowerShell)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.venv&lt;span style=&#34;color:#ae81ff&#34;&gt;\S&lt;/span&gt;cripts&lt;span style=&#34;color:#ae81ff&#34;&gt;\a&lt;/span&gt;ctivate.ps1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;directory-structure&#34;&gt;Directory structure&lt;/h2&gt;
&lt;p&gt;After initialisation the project 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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my-cdk-app/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── app.py                  # CDK app entry point
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── cdk.json                # CDK config and context
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── requirements.txt        # Python dependencies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── requirements-dev.txt    # Dev dependencies (e.g. pytest)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── .venv/                  # Python virtual environment
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── my_cdk_app/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── my_cdk_app_stack.py # Default stack
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;app.py&lt;/code&gt; is where the CDK app is defined and stacks are instantiated. &lt;code&gt;my_cdk_app_stack.py&lt;/code&gt; is where resources are defined. The folder and class names match the project name passed to &lt;code&gt;cdk init&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;bootstrap-aws-account&#34;&gt;Bootstrap AWS account&lt;/h2&gt;
&lt;p&gt;CDK needs a one-time setup in each AWS account and region it deploys to. Bootstrapping creates an S3 bucket (for assets) and IAM roles (for deployments) in the 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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk bootstrap aws://123456789012/us-east-1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Replace &lt;code&gt;123456789012&lt;/code&gt; with your account ID and &lt;code&gt;us-east-1&lt;/code&gt; with your target region. If credentials are already configured via AWS CLI or SSO, the account ID can be omitted:&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;cdk bootstrap
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This only needs to run once per account/region combination. Re-running it is safe - it updates the bootstrap stack if needed.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;synth-and-deploy&#34;&gt;Synth and deploy&lt;/h2&gt;
&lt;p&gt;CDK compiles Python into CloudFormation before deploying. Run &lt;code&gt;synth&lt;/code&gt; to see the generated 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;cdk synth
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This outputs a CloudFormation template to &lt;code&gt;cdk.out/&lt;/code&gt;. Use it to verify what CDK will create before deploying.&lt;/p&gt;
&lt;p&gt;Deploy 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;cdk deploy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CDK shows a diff of changes and prompts for confirmation before creating or modifying any IAM resources.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;verify&#34;&gt;Verify&lt;/h2&gt;
&lt;p&gt;After deploying, the stack appears in CloudFormation in the AWS console. Check:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CloudFormation &amp;gt; Stacks &amp;gt; &lt;code&gt;MyCdkAppStack&lt;/code&gt; - status should be &lt;code&gt;CREATE_COMPLETE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;S3 &amp;gt; Buckets - the CDK bootstrap bucket should be visible (&lt;code&gt;cdk-hnb659fds-assets-...&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To tear down 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;cdk destroy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;The bootstrap stack itself is a CloudFormation stack - it can be viewed and managed in the console under &lt;code&gt;CDKToolkit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Each AWS account + region combination needs its own bootstrap - e.g. deploying to both &lt;code&gt;us-east-1&lt;/code&gt; and &lt;code&gt;ap-southeast-2&lt;/code&gt; requires bootstrapping both&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cdk synth&lt;/code&gt; is useful for debugging - the generated CloudFormation is in &lt;code&gt;cdk.out/&lt;/code&gt; and can be inspected directly&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.venv/&lt;/code&gt; folder and &lt;code&gt;cdk.out/&lt;/code&gt; should be in &lt;code&gt;.gitignore&lt;/code&gt; - they are added automatically by &lt;code&gt;cdk init&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Install CloudWatch agent in EC2 Instance</title>
      <link>https://bastabc.com/posts/install-cloudwatch-agent-in-ec2/</link>
      <pubDate>Thu, 03 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/install-cloudwatch-agent-in-ec2/</guid>
      <description>&lt;p&gt;EC2 does not send memory or disk metrics to CloudWatch by default - only CPU, network and status checks. The CloudWatch agent runs inside the instance and collects system-level metrics and logs directly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EC2 instance running (Amazon Linux 2 or Ubuntu)&lt;/li&gt;
&lt;li&gt;SSH access to the instance - see &lt;a href=&#34;https://bastabc.com/posts/connect-to-aws-sso-ssh-into-ec2-instance/&#34;&gt;Connect to AWS SSO and SSH into EC2 Instance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;attach-iam-role-to-the-instance&#34;&gt;Attach IAM role to the instance&lt;/h2&gt;
&lt;p&gt;The agent needs permission to write metrics and logs to CloudWatch. In the AWS console:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;EC2 &amp;gt; Instances &amp;gt; select your instance&lt;/li&gt;
&lt;li&gt;Actions &amp;gt; Security &amp;gt; Modify IAM role&lt;/li&gt;
&lt;li&gt;Attach a role that has &lt;code&gt;CloudWatchAgentServerPolicy&lt;/code&gt; managed policy&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If no such role exists, create one:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;IAM &amp;gt; Roles &amp;gt; Create role&lt;/li&gt;
&lt;li&gt;Trusted entity: AWS service &amp;gt; EC2&lt;/li&gt;
&lt;li&gt;Add permission: &lt;code&gt;CloudWatchAgentServerPolicy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Name it (e.g. &lt;code&gt;ec2-cloudwatch-agent-role&lt;/code&gt;) and create&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;install-the-agent&#34;&gt;Install the agent&lt;/h2&gt;
&lt;p&gt;SSH into the instance, then run the appropriate command for your OS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon Linux 2:&lt;/strong&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;sudo yum install -y amazon-cloudwatch-agent
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Ubuntu:&lt;/strong&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;sudo apt-get install -y amazon-cloudwatch-agent
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;configure-the-agent&#34;&gt;Configure the agent&lt;/h2&gt;
&lt;p&gt;Run the configuration wizard - it steps through metrics and log collection interactively:&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;sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternatively, create a config file manually at &lt;code&gt;/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json&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-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;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;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;run_as_user&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;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;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;namespace&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CWAgent&amp;#34;&lt;/span&gt;,
&lt;/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;mem&amp;#34;&lt;/span&gt;: {
&lt;/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 style=&#34;color:#e6db74&#34;&gt;&amp;#34;mem_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;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 style=&#34;color:#e6db74&#34;&gt;&amp;#34;disk_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 style=&#34;color:#f92672&#34;&gt;&amp;#34;resources&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;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;Collects memory and disk usage every 60 seconds under the &lt;code&gt;CWAgent&lt;/code&gt; namespace.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;start-the-agent&#34;&gt;Start the agent&lt;/h2&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;sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl &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;  -a fetch-config &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;  -m ec2 &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;  -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.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;  -s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;verify&#34;&gt;Verify&lt;/h2&gt;
&lt;p&gt;Check the agent is 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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Expected 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-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;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt;,
&lt;/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;starttime&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;version&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the AWS console, go to CloudWatch &amp;gt; Metrics &amp;gt; All metrics &amp;gt; CWAgent to confirm metrics are coming in. Give it 2-3 minutes after starting for metrics to show up.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;If the instance had no IAM role before, restart the agent after attaching the role - it picks up credentials on start&lt;/li&gt;
&lt;li&gt;The wizard config is saved to &lt;code&gt;/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json&lt;/code&gt; by default&lt;/li&gt;
&lt;li&gt;To collect logs, add a &lt;code&gt;logs&lt;/code&gt; block to the config pointing to the log file paths&lt;/li&gt;
&lt;li&gt;Agent logs are at &lt;code&gt;/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log&lt;/code&gt; - check here if metrics are not appearing&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Connect to AWS SSO and SSH into EC2 Instance</title>
      <link>https://bastabc.com/posts/connect-to-aws-sso-ssh-into-ec2-instance/</link>
      <pubDate>Mon, 17 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/connect-to-aws-sso-ssh-into-ec2-instance/</guid>
      <description>&lt;p&gt;Two paths depending on your setup - follow one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Without SSO&lt;/strong&gt;: personal AWS account, direct IAM credentials&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;With SSO&lt;/strong&gt;: enterprise/team setup with an SSO start URL (e.g. &lt;code&gt;https://company.awsapps.com/start&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Without SSO:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS CLI v2&lt;/li&gt;
&lt;li&gt;AWS IAM user credentials (Access Key ID and Secret Access Key)&lt;/li&gt;
&lt;li&gt;Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With SSO:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS CLI v2&lt;/li&gt;
&lt;li&gt;AWS SSO start URL and access&lt;/li&gt;
&lt;li&gt;Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;files-you-will-need&#34;&gt;Files you will need&lt;/h2&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;~/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── .aws/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── credentials
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── .ssh/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── ec2-key.pem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;path-a-without-sso&#34;&gt;Path A: Without SSO&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;.aws&lt;/code&gt; folder if it doesn&amp;rsquo;t exist. Run from your home directory (&lt;code&gt;~&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 -p ~/.aws
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create &lt;code&gt;~/.aws/credentials&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-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;YOUR_ACCESS_KEY_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:#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;YOUR_SECRET_ACCESS_KEY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create &lt;code&gt;~/.aws/config&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-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;region&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;ap-southeast-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:#a6e22e&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Test your config.&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 sts get-caller-identity
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;path-b-with-sso&#34;&gt;Path B: With SSO&lt;/h2&gt;
&lt;p&gt;Add a profile block to &lt;code&gt;~/.aws/config&lt;/code&gt; for each environment (e.g. &lt;code&gt;aws-dev&lt;/code&gt;, &lt;code&gt;aws-prod&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-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 aws-prod]&lt;/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;sso_session&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;aws-prod&lt;/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;sso_account_id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;123456789123&lt;/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;sso_role_name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;aws-prod-SystemAdmin&lt;/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;region&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;ap-southeast-4&lt;/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;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#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:#a6e22e&#34;&gt;credential_process&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;aws configure export-credentials --profile aws-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sample &lt;code&gt;.aws&lt;/code&gt; folder structure:
&lt;img loading=&#34;lazy&#34; src=&#34;sso.png&#34; alt=&#34;aws-folder&#34;  /&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;setup-ssh-config&#34;&gt;Setup SSH config&lt;/h2&gt;
&lt;p&gt;Download the &lt;code&gt;.pem&lt;/code&gt; keypair when launching the EC2 instance and move it here.&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 -p ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mv ~/Downloads/ec2-prod.pem ~/.ssh/ec2-prod.pem
&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;400&lt;/span&gt; ~/.ssh/ec2-prod.pem
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Basic SSH config (no SSO)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Add to &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Host ec2-prod
    HostName 1.2.3.4             # Replace with your EC2&amp;#39;s public IP
    User ec2-user                # Use &amp;#39;ubuntu&amp;#39; for Ubuntu instances
    IdentityFile ~/.ssh/ec2-prod.pem
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;SSO ProxyCommand config&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Use this if connecting via AWS SSM Session Manager with an SSO profile. Add to &lt;code&gt;~/.ssh/config&lt;/code&gt; (or a separate file under &lt;code&gt;~/.ssh/config.d/&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Host ec2-prod
  User ec2-user
  IdentityFile ~/.ssh/ec2-prod.pem
  ProxyCommand aws ssm start-session --target i-01xyz7659824a123q --profile aws-prod --document-name AWS-StartSSHSession --parameters portNumber=22
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sample &lt;code&gt;.ssh&lt;/code&gt; folder structure:
&lt;img loading=&#34;lazy&#34; src=&#34;ssh.png&#34; alt=&#34;ssh-folder&#34;  /&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;connect&#34;&gt;Connect&lt;/h2&gt;
&lt;p&gt;If using SSO, log in first:&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 sso login --profile aws-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then SSH in:&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;ssh ec2-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;ec2.png&#34; alt=&#34;ec2-connect&#34;  /&gt;
&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>