How-To Guides/AWS IAM & GitHub Permissions
AWS & GitHub 60–90 min for first review · 30 min ongoing

How to Track and Review Permissions in AWS IAM and GitHub

AWS IAM and GitHub are the two highest-risk permission surfaces for most software companies. AWS admin access means the ability to create resources, access all data, and destroy infrastructure. GitHub admin access means the ability to exfiltrate your entire codebase, modify CI/CD pipelines, and inject malicious code into your releases. Both are tested directly in SOC 2 CC6.1 and CC6.3 audits. This guide covers the full review process for both platforms, including the AWS IAM Access Analyzer, CloudTrail evidence, and GitHub Teams vs direct collaborator access.

Prerequisites

  • AWS: IAM permissions — iam:GenerateCredentialReport, iam:GetCredentialReport, iam:ListUsers, iam:ListRoles, iam:ListAttachedUserPolicies
  • AWS CLI v2 installed and configured with appropriate credentials (aws configure)
  • GitHub: Organisation Owner or Admin access
  • GitHub CLI (gh) installed — optional but recommended
  • Our free SOC 2 Access Review Template to document findings

What evidence you'll produce

  • • AWS IAM Credential Report (all users, MFA status, access key ages, last activity) — CC6.1
  • • AWS privileged policy holders (AdministratorAccess, PowerUser, custom policies) — CC6.1
  • • AWS IAM roles with high-privilege policies (Lambda, EC2, cross-account roles) — CC6.1
  • • AWS IAM Access Analyzer findings (over-permissive policies, public-access findings) — CC6.1
  • • GitHub org member list with roles (Owner vs Member) — CC6.1
  • • GitHub outside collaborators list — CC6.3 completeness
  • • GitHub repo-level admin access (who can change branch protection) — CC6.1
  • • Completed access review spreadsheet (decisions and reviewer names) — CC6.3

Part 1: AWS IAM

1

Generate and review the IAM Credential Report

10 minutes

The IAM Credential Report is AWS's built-in audit export — one CSV covering every IAM user in the account. It includes password status, MFA status, access key ages, and last-used dates.

Via the AWS Console:

AWS Console → IAM → Credential report (left sidebar) → Download Report

Via CLI (recommended — allows scripting):

# Generate the report (takes ~30 seconds to prepare)

aws iam generate-credential-report

# Download and decode the report

aws iam get-credential-report --query 'Content' --output text | \

base64 --decode > iam-credential-report-$(date +%Y%m%d).csv

Key columns to flag in your review:

  • mfa_active = FALSE + password_enabled = TRUE: This user can log in to the AWS Console without MFA. Immediate CC6.1 finding. Every human IAM user with console access must have MFA.
  • access_key_1_last_rotated: Any access key not rotated in 90+ days is a SOC 2 CC6.1 finding under key rotation policy.
  • access_key_1_last_used_date = N/A: An access key that has never been used is either orphaned or was never needed. Candidate for deletion.
  • password_last_used = no_information: User has never logged into the console. If they also have access keys, this is an account that only uses programmatic access — document this as a service account.
  • user_creation_time vs password_last_used gap: A user created 2 years ago who last signed in 300 days ago is stale.
Critical — root account: The credential report includes the root account. The root account should have MFA enabled, no active access keys, and no regular usage. Any recent root account sign-in that isn't a documented emergency is a CC6.1 finding.
2

Identify over-privileged users and policies

15–20 minutes

Find all users with AdministratorAccess:

# Users directly attached to AdministratorAccess policy

aws iam list-entities-for-policy \

--policy-arn arn:aws:iam::aws:policy/AdministratorAccess \

--entity-filter User --output table

# Groups attached to AdministratorAccess (indirect admin access)

aws iam list-entities-for-policy \

--policy-arn arn:aws:iam::aws:policy/AdministratorAccess \

--entity-filter Group --output table

# Check what policies are attached to a specific user

aws iam list-attached-user-policies --user-name alice --output table

aws iam list-user-policies --user-name alice --output table

Find users with wildcard policies (Action: "*"):

# Get all inline policies for a user and inspect for wildcards

for user in $(aws iam list-users --query 'Users[*].UserName' --output text); do

policies=$(aws iam list-user-policies --user-name $user --query 'PolicyNames' --output text)

if [ ! -z "$policies" ]; then

echo "=== $user ==="

for policy in $policies; do

aws iam get-user-policy --user-name $user --policy-name $policy \

--query 'PolicyDocument' --output json | grep -i 'Action.*\*'

done

fi

done

CC6.1 least privilege: For each user with AdministratorAccess, document: what specific admin capabilities they use (not "admin for everything"), whether a scoped policy would be sufficient, and who approved this access level. The goal is fewer than 10% of IAM users holding admin-equivalent access.
3

Review IAM roles and service account permissions

15 minutes

IAM roles (used by EC2, Lambda, ECS, and cross-account access) are frequently overlooked in access reviews. A Lambda function with AdministratorAccess is the same risk as an admin IAM user.

# List all IAM roles and their attached policies

aws iam list-roles --query 'Roles[*].[RoleName,Arn]' --output table

# Find roles with AdministratorAccess

aws iam list-entities-for-policy \

--policy-arn arn:aws:iam::aws:policy/AdministratorAccess \

--entity-filter Role --output table

# Check who can assume a specific role (trust policy)

aws iam get-role --role-name MyRole \

--query 'Role.AssumeRolePolicyDocument' --output json

Use AWS IAM Access Analyzer to find over-permissive access:

AWS Console → IAM → Access Analyzer → Create analyzer (if not already set up) → Review findings

Access Analyzer identifies: S3 buckets with public access, IAM roles assumable by external accounts, KMS keys with cross-account access, and Lambda functions with public access. Each finding is a potential CC6.1 scope-creep issue.

Common mistake: Reviewing IAM users but not IAM roles. A Lambda execution role with AdministratorAccess attached is a critical misconfiguration — it means any Lambda function can do anything in your account. These often aren't noticed until an audit or incident.

Part 2: GitHub

4

Export organisation members, owners, and outside collaborators

10–15 minutes

Via the GitHub web interface:

  • Org owners: github.com/orgs/[org]/people?filter=owners
  • All members: github.com/orgs/[org]/people
  • Outside collaborators: github.com/orgs/[org]/settings/outside_collaborators

Via the GitHub CLI (most efficient for large orgs):

# List all org owners

gh api "/orgs/YOUR-ORG/members?role=admin&per_page=100" \

--jq '.[] | [.login, .html_url] | @csv' > github-owners.csv

# List all org members

gh api "/orgs/YOUR-ORG/members?per_page=100" \

--jq '.[] | [.login, .html_url] | @csv' > github-members.csv

# List outside collaborators (the most commonly missed group)

gh api "/orgs/YOUR-ORG/outside_collaborators?per_page=100" \

--jq '.[] | [.login, .html_url] | @csv' > github-outside-collaborators.csv

Outside collaborators — the biggest gap: Outside collaborators are non-org members with direct repository access. This list is never visible in the standard org member view. It frequently contains former employees, contractors who've ended their engagement, and freelancers from years ago. Auditors specifically ask for this list — it's one of the first places they look for unrevoked leavers.
5

Review repository-level admin access

15–20 minutes

Org-level role (Owner vs Member) doesn't tell you everything. Repo-level Admin access — which allows changing branch protection rules, managing deploy keys, and deleting the repo — must also be reviewed.

# List collaborators for a specific repo with their permission level

gh api "/repos/YOUR-ORG/YOUR-REPO/collaborators?affiliation=all&per_page=100" \

--jq '.[] | [.login, .permissions.admin, .permissions.maintain, .permissions.push, .permissions.pull] | @csv'

# Find all repo admins across all repos in the org

for repo in $(gh repo list YOUR-ORG --limit 100 --json name --jq '.[].name'); do

echo "=== $repo ==="

gh api "/repos/YOUR-ORG/$repo/collaborators?permission=admin" \

--jq '.[] | [.login, "admin", "$repo"] | @csv'

done

GitHub Teams — the structured approach:

If your org uses GitHub Teams, review access at the team level rather than per-repo per-user:

# List all teams in the org

gh api "/orgs/YOUR-ORG/teams?per_page=100" --jq '.[] | [.name, .privacy, .members_count] | @csv'

# List members of a specific team

gh api "/orgs/YOUR-ORG/teams/TEAM-SLUG/members?per_page=100" --jq '.[].login'

# List repos a team has admin access to

gh api "/orgs/YOUR-ORG/teams/TEAM-SLUG/repos?per_page=100" \

--jq '.[] | select(.permissions.admin == true) | [.name, "admin"] | @csv'

CC6.1 for GitHub: Your auditor wants to see: Who has Org Owner? Who has repo Admin specifically on production repositories? What is the justification for each? Repo Admin on your main application repo is the equivalent of production system admin — document it accordingly.
6

Review GitHub Actions secrets and deploy keys

10 minutes

GitHub Actions secrets and deploy keys are frequently missed in access reviews. They provide programmatic access to your repositories and connected systems (AWS, Docker Hub, npm, etc.).

Via the web interface:

  • Org-level secrets: github.com/orgs/[org]/settings/secrets/actions
  • Repo-level secrets: github.com/[org]/[repo]/settings/secrets/actions
  • Deploy keys: github.com/[org]/[repo]/settings/keys

For each secret or deploy key, document:

  • What it provides access to (AWS account, npm registry, Docker Hub)
  • Named owner — a human who is responsible for this key
  • Last rotated date — keys not rotated in 90+ days should be reviewed
  • Which workflows use it — if a workflow is deleted or changed, is the secret still needed?
Supply chain risk: GitHub Actions secrets with access to your production AWS account or npm publishing are supply-chain attack vectors. A compromised workflow can exfiltrate the secret and publish malicious packages. Include these as rows in your access review with "Service Account" as the User Type.
7

Populate and archive the access review

15 minutes + reviewer time

Recommended review structure in the spreadsheet:

  • One section per system (AWS IAM, GitHub Org, GitHub Repos — grouped by repo)
  • Separate sections for service accounts and deploy keys
  • Separate section for outside collaborators (these often need immediate action)

Archive per review cycle:

  • iam-credential-report-2025-01-10.csv
  • iam-admin-users-2025-01-10.csv (output of list-entities-for-policy)
  • github-members-2025-01-10.csv
  • github-outside-collaborators-2025-01-10.csv
  • access-review-aws-github-2025-Q1.xlsx (completed review with decisions)
  • Screenshots/ (for any Revoked rows — IAM user disabled, GitHub member removed)

Evidence quality checklist — AWS IAM & GitHub

IAM Credential Report generated within the review period
Root account has MFA and no active access keys
Every IAM user with console access has MFA (mfa_active = TRUE)
No access keys older than 90 days without documented justification
Every user with AdministratorAccess has a specific written justification
IAM roles (Lambda, EC2) reviewed — not just human users
IAM Access Analyzer findings reviewed and addressed
GitHub Org Owners list reviewed and justified
Outside collaborators list reviewed and current
Repo-level Admin access reviewed for production repos
GitHub Actions secrets have named owners and rotation dates
Deploy keys reviewed and stale ones removed

Common mistakes that cause audit findings

  • Reviewing IAM users but not IAM roles

    Lambda functions with AdministratorAccess are the same risk level as admin users. Include application roles in your review.

  • Not checking GitHub outside collaborators

    This list is invisible in the standard People view. It must be checked separately — it's where most former-employee access is found.

  • IAM access keys that were rotated but old keys not deleted

    AWS allows two access keys per user. The old key must be deleted, not just disabled. A disabled key can be re-enabled.

  • GitHub Actions secrets not included in the access review

    Secrets with production AWS access are equivalent to admin access. Include as service account rows.

Track this properly every 90 days

AllowNow gives you a central place to record every AWS and GitHub permission, assign ownership, and run structured access reviews. Add your services, map users to roles, and export audit-ready PDF reports in one click. Every review decision is timestamped.

Start your free review