<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Neil</title>
    <description>The latest articles on DEV Community by Neil (@neil_4e5a9c0f3c99).</description>
    <link>https://dev.to/neil_4e5a9c0f3c99</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3918418%2Fab14c422-10be-4b5d-804d-0cd9cee89d14.png</url>
      <title>DEV Community: Neil</title>
      <link>https://dev.to/neil_4e5a9c0f3c99</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/neil_4e5a9c0f3c99"/>
    <language>en</language>
    <item>
      <title>Stop Putting AWS Access Keys in GitHub Secrets. Use OIDC Instead.</title>
      <dc:creator>Neil</dc:creator>
      <pubDate>Thu, 07 May 2026 17:05:14 +0000</pubDate>
      <link>https://dev.to/neil_4e5a9c0f3c99/stop-putting-aws-access-keys-in-github-secrets-use-oidc-instead-5c13</link>
      <guid>https://dev.to/neil_4e5a9c0f3c99/stop-putting-aws-access-keys-in-github-secrets-use-oidc-instead-5c13</guid>
      <description>&lt;p&gt;I rotated a leaked AWS access key at 2 AM last year. A contractor had pushed a workflow that printed environment variables for "debugging," GitHub's secret scanner caught it about four minutes later, and by the time I'd revoked the key and audited CloudTrail, I'd lost an hour of sleep I still resent.&lt;/p&gt;

&lt;p&gt;That was the night I went all-in on OIDC for GitHub Actions. If you're still using long-lived &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; in your repo secrets, this post is for you. I'll walk through what OIDC actually does, why static keys are a rake to be stood on, and exactly how to wire it up — IAM trust policy, workflow YAML, all of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Static Access Keys
&lt;/h2&gt;

&lt;p&gt;Let's be honest about what an AWS access key in GitHub Secrets really is: &lt;strong&gt;a permanent credential sitting in a system you don't fully control&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A few things go wrong in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;They never rotate.&lt;/strong&gt; I've audited orgs with five-year-old keys still in use. Nobody wants to be the person who breaks a deploy by rotating the wrong one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They leak.&lt;/strong&gt; Logs, screenshots, a misconfigured &lt;code&gt;set -x&lt;/code&gt;, a fork's PR workflow — the surface area is huge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They're over-scoped.&lt;/strong&gt; Most teams attach &lt;code&gt;PowerUserAccess&lt;/code&gt; and move on, because writing tight IAM policies is annoying and "we'll fix it later."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They have no context.&lt;/strong&gt; When a key is used, CloudTrail sees an IAM user. It can't tell you &lt;em&gt;which&lt;/em&gt; repo, &lt;em&gt;which&lt;/em&gt; workflow, &lt;em&gt;which&lt;/em&gt; commit triggered the call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Static keys treat your CI/CD pipeline like a trusted human user. It isn't. It's a robot that runs whatever YAML lands in &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What OIDC Actually Is
&lt;/h2&gt;

&lt;p&gt;OpenID Connect is just an identity layer on top of OAuth 2.0. The piece that matters for us: &lt;strong&gt;GitHub Actions can hand out short-lived JWT tokens that prove a workflow is running&lt;/strong&gt;, and AWS knows how to verify those tokens and trade them for temporary credentials.&lt;/p&gt;

&lt;p&gt;The flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your workflow runs and requests an OIDC token from GitHub.&lt;/li&gt;
&lt;li&gt;GitHub mints a JWT signed with its keys. The token's claims include the repo, branch, environment, and workflow that requested it.&lt;/li&gt;
&lt;li&gt;The workflow sends that JWT to AWS STS via &lt;code&gt;AssumeRoleWithWebIdentity&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;AWS validates the signature against GitHub's public keys, checks the claims against your IAM role's trust policy, and returns temporary credentials (typically 1 hour).&lt;/li&gt;
&lt;li&gt;Your workflow uses those credentials. They expire. Nothing to rotate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No long-lived secret ever leaves AWS. No static key ever enters GitHub. And every API call in CloudTrail is tied to a specific workflow run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up OIDC for AWS
&lt;/h2&gt;

&lt;p&gt;There are two pieces: an &lt;strong&gt;identity provider&lt;/strong&gt; in IAM (one-time setup per AWS account), and an &lt;strong&gt;IAM role&lt;/strong&gt; with a trust policy that scopes who can assume it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create the OIDC Provider in IAM
&lt;/h3&gt;

&lt;p&gt;You only do this once per AWS account. Either click through the IAM console or use Terraform/CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam create-open-id-connect-provider &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; https://token.actions.githubusercontent.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--client-id-list&lt;/span&gt; sts.amazonaws.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--thumbprint-list&lt;/span&gt; 6938fd4d98bab03faadb97b34396831e3780aea1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWS now also accepts the provider without an explicit thumbprint for &lt;code&gt;token.actions.githubusercontent.com&lt;/code&gt;, but I still pass one for explicitness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Write a Trust Policy That Actually Constrains Things
&lt;/h3&gt;

&lt;p&gt;This is where most tutorials fall apart. They show you a trust policy with &lt;code&gt;"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"&lt;/code&gt; and call it done. &lt;strong&gt;Don't do that.&lt;/strong&gt; A wildcard &lt;code&gt;:*&lt;/code&gt; on the sub claim means &lt;em&gt;any&lt;/em&gt; branch, &lt;em&gt;any&lt;/em&gt; tag, &lt;em&gt;any&lt;/em&gt; PR from a fork can assume your role.&lt;/p&gt;

&lt;p&gt;Here's a trust policy I'd actually deploy. It pins the role to a specific repo and only allows it to be assumed from the &lt;code&gt;main&lt;/code&gt; branch or from a &lt;code&gt;production&lt;/code&gt; environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringLike"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"repo:my-org/my-repo:ref:refs/heads/main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"repo:my-org/my-repo:environment:production"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth flagging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;aud&lt;/code&gt; check prevents tokens issued for other audiences from being accepted. Always include it.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;sub&lt;/code&gt; claim is the lever. You can pin to branches (&lt;code&gt;ref:refs/heads/main&lt;/code&gt;), tags (&lt;code&gt;ref:refs/tags/v*&lt;/code&gt;), pull requests (&lt;code&gt;pull_request&lt;/code&gt;), or environments (&lt;code&gt;environment:production&lt;/code&gt;). I use environments for production roles because they integrate with GitHub's required-reviewer gates.&lt;/li&gt;
&lt;li&gt;For staging or per-PR ephemeral environments, I create a &lt;strong&gt;separate role&lt;/strong&gt; with a looser sub pattern and weaker permissions. Don't use one role for everything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attach whatever permissions policy your workflow actually needs. Be tight here too — &lt;code&gt;s3:PutObject&lt;/code&gt; on one bucket beats &lt;code&gt;s3:*&lt;/code&gt; every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: The Workflow YAML
&lt;/h3&gt;

&lt;p&gt;Two things have to happen in the workflow itself: you need to grant the job permission to mint an OIDC token, and you need to use &lt;code&gt;aws-actions/configure-aws-credentials@v4&lt;/code&gt; with &lt;code&gt;role-to-assume&lt;/code&gt; instead of access keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;   &lt;span class="c1"&gt;# required to request the OIDC JWT&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;    &lt;span class="c1"&gt;# required for actions/checkout&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS credentials via OIDC&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::123456789012:role/github-actions-deploy&lt;/span&gt;
          &lt;span class="na"&gt;role-session-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gha-${{ github.run_id }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;aws s3 sync ./dist s3://my-app-prod --delete&lt;/span&gt;
          &lt;span class="s"&gt;aws cloudfront create-invalidation \&lt;/span&gt;
            &lt;span class="s"&gt;--distribution-id E1ABCDEFGHIJKL \&lt;/span&gt;
            &lt;span class="s"&gt;--paths "/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;permissions&lt;/code&gt; block at the job level is the bit people miss. Without &lt;code&gt;id-token: write&lt;/code&gt;, the action can't request a token and you'll get a confusing 403. Setting &lt;code&gt;contents: read&lt;/code&gt; explicitly is good hygiene because declaring &lt;code&gt;permissions&lt;/code&gt; at all switches the job from the default permissive token to a least-privilege one.&lt;/p&gt;

&lt;p&gt;I also set &lt;code&gt;role-session-name&lt;/code&gt; to include the run ID. That single line has saved me hours during incident review — CloudTrail now shows me exactly which workflow run made every API call.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like in CloudTrail
&lt;/h2&gt;

&lt;p&gt;This is the part that sells it. With static keys, every event has &lt;code&gt;userIdentity.userName: github-actions-ci-user&lt;/code&gt;. With OIDC, the &lt;code&gt;assumedRole&lt;/code&gt; session name is &lt;code&gt;gha-7384920183&lt;/code&gt;, and the underlying &lt;code&gt;userIdentity.sessionContext.sessionIssuer&lt;/code&gt; shows the role plus the federated subject — meaning you can grep CloudTrail for a specific workflow run and see every call it made.&lt;/p&gt;

&lt;p&gt;When a deploy goes sideways, that traceability is worth its weight.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Note on Azure
&lt;/h2&gt;

&lt;p&gt;If you're on Azure, the same pattern works — federated credentials on an App Registration plus the &lt;code&gt;azure/login@v2&lt;/code&gt; action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;azure/login@v2&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;client-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AZURE_CLIENT_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;tenant-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AZURE_TENANT_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;subscription-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AZURE_SUBSCRIPTION_ID }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IDs aren't secrets in any meaningful sense (they identify the app, they don't authenticate as it), but GitHub still wants them in the secrets store. No &lt;code&gt;client-secret&lt;/code&gt; anywhere. Same story on GCP via Workload Identity Federation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Tips
&lt;/h2&gt;

&lt;p&gt;If you're moving an existing repo off static keys, here's the order I'd go in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stand up the OIDC provider and a new role&lt;/strong&gt; alongside the existing IAM user. Don't delete the user yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test on a non-production branch first.&lt;/strong&gt; Create a role scoped to &lt;code&gt;ref:refs/heads/oidc-test&lt;/code&gt; and prove the deploy works end-to-end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switch one workflow at a time.&lt;/strong&gt; Update the YAML, run it, watch CloudTrail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Once everything is green for a week, delete the IAM user's access keys.&lt;/strong&gt; Not the user — just the keys. That way if something breaks you can restore quickly without recreating IAM resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;After another week, delete the user.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't try to migrate everything in one PR. The blast radius isn't worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Static AWS access keys in GitHub Secrets are a 2018 pattern. OIDC takes about 30 minutes to set up properly per repo, eliminates an entire class of credential-leak incidents, and gives you actual auditability when something goes wrong. There's no reason not to make the switch.&lt;/p&gt;

&lt;p&gt;I use this OIDC pattern in all 10 workflows in my GitHub Actions Pack — [&lt;a href="https://neilwave182.gumroad.com/l/fafaq" rel="noopener noreferrer"&gt;https://neilwave182.gumroad.com/l/fafaq&lt;/a&gt;].&lt;/p&gt;

</description>
      <category>devops</category>
      <category>githubactions</category>
      <category>aws</category>
      <category>azure</category>
    </item>
  </channel>
</rss>
