IAM Policy Analyzer & Fixer
Project description
iamarmor
ESLint for AWS IAM — catch over-permissioned policies in your Terraform PRs before they merge.
Why iam-armor?
IAM misconfigurations are the #1 cause of AWS breaches — wildcards on Action and
Resource, forgotten AdministratorAccess attachments, and overly-permissive
assume_role_policy are easy to ship and hard to spot in code review.
iamarmor is a static linter for Terraform IAM resources. It runs entirely
offline — no AWS credentials, no Terraform plan needed — and integrates as a
pre-commit hook or a CI step in seconds. Think ESLint, but for your IAM policies.
$ iamarmor lint modules/iam/
modules/iam/main.tf
✘ IAM001 [HIGH] resource 'aws_iam_policy.app' has Action: "*" — grant least-privilege actions instead.
✘ IAM005 [HIGH] resource 'aws_iam_policy.deployer' grants iam:PassRole on Resource: "*" — scope to specific role ARNs.
✘ IAM010 [HIGH] resource 'aws_iam_role_policy_attachment.admin' attaches AdministratorAccess — grant only required permissions.
3 findings (3 high, 0 medium, 0 low) in 0.12 s
exit 1
Demo
Regenerate with:
vhs docs/demo.tape
Quickstart
# Install (requires Python 3.11+)
pip install iamarmor
# Lint the current directory
iamarmor lint .
# Lint a specific file
iamarmor lint modules/iam/main.tf
# Machine-readable output for CI pipelines
iamarmor lint . --format json
iamarmor lint exits 0 when clean, 1 when findings meet the fail_on
threshold (default: medium), 2 on usage/config errors, and 3 on
internal errors — making CI integration trivial.
Configuration
Place a .iamarmor.yml in the root of your Terraform repository:
version: 1
severity_threshold: low # report findings at or above this level (default: info)
fail_on: high # exit 1 only for high/critical (default: medium)
rules:
ignore: [IAM004] # skip noisy rules for your environment
overrides:
IAM002:
severity: critical # escalate a rule's severity
paths:
exclude:
- "modules/legacy/**" # skip paths you're not ready to fix yet
iamarmor auto-discovers .iamarmor.yml by walking upward from the linted path
(same pattern as .eslintrc). Pass --no-config to skip loading.
See docs/config.md for the full configuration reference.
Default rule pack
iamarmor ships with 10 default IAM rules covering the most common misconfigurations:
| ID | Name | Severity |
|---|---|---|
| IAM001 | No Action: "*" |
High |
| IAM002 | No Resource: "*" with sensitive actions |
High |
| IAM003 | No inline policies | Medium |
| IAM004 | IAM roles must set max_session_duration |
Low |
| IAM005 | No iam:PassRole with Resource: "*" |
High |
| IAM006 | No wildcard Principal in resource-based policies |
High |
| IAM007 | assume_role_policy must specify a concrete principal |
High |
| IAM008 | No NotAction in Allow statements |
Medium |
| IAM009 | No NotResource in Allow statements |
Medium |
| IAM010 | Do not attach AdministratorAccess managed policy |
High |
See STARTER_RULES.md for full documentation of each rule, including rationale, examples, and configuration options.
Pre-commit hook
Add to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/iam-armor/iamarmor
rev: v0.1.1
hooks:
- id: iamarmor
See docs/pre-commit.md for details.
GitHub Actions CI
Add to your workflow to lint Terraform in every PR:
- name: Lint IAM policies
run: |
pip install iamarmor
iamarmor lint . --fail-on high
Or pin a specific version:
- name: Lint IAM policies
run: |
pip install iamarmor==0.1.1
iamarmor lint modules/iam/ --format json > iam-findings.json
Python API
The CLI is the recommended entry point for most users. For embedding iamarmor in other tools, the Python API is also public:
from iamarmor import extract_from_directory, RuleEngine, load_default_rules
resources = extract_from_directory("path/to/terraform/")
engine = RuleEngine(rules=load_default_rules())
findings = engine.run(resources)
for finding in findings:
print(f"[{finding.rule_id}] {finding.severity.value.upper()} — {finding.message}")
Regenerating the demo GIF
The demo GIF was recorded with VHS. To regenerate it:
# Install VHS (requires Go)
go install github.com/charmbracelet/vhs@latest
# Re-record
vhs docs/demo.tape
The resulting docs/demo.gif is committed to the repository. The tape script
exercises iamarmor lint against the bundled tests/fixtures/ directory.
Roadmap
iamarmor (this repo) is the OSS linter engine. The hosted GitHub App at
iamarmor.dev (coming soon) will add:
- 🔌 GitHub App with inline PR annotations
- 📦 Premium rule packs: SOC 2, PCI-DSS, HIPAA, AWS Well-Architected Security
- 📊 Findings dashboard and trend tracking
- 🔧 One-click auto-fix suggestions
Contributing
Contributions are welcome! Please open an issue before submitting a PR for new features or rules. See STARTER_RULES.md for the existing rule inventory and docs/ for design docs.
git clone https://github.com/iam-armor/iamarmor.git
cd iamarmor
pip install -e ".[dev]"
pytest
ruff check src/ tests/
License
MIT © 2026 iam-armor
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file iamarmor-0.1.1.tar.gz.
File metadata
- Download URL: iamarmor-0.1.1.tar.gz
- Upload date:
- Size: 43.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c7a9f9186d38befe424aef0ea663ed2771159397c5badd6d7dfc29fd112d158
|
|
| MD5 |
c1a3ae27e1f3385c8175f9bc1a777323
|
|
| BLAKE2b-256 |
d7bb76c9ced46ef1794a8d6a70b0a9f08b3138b73f4ec0d855ffe1af1980b321
|
Provenance
The following attestation bundles were made for iamarmor-0.1.1.tar.gz:
Publisher:
publish.yml on iam-armor/iamarmor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
iamarmor-0.1.1.tar.gz -
Subject digest:
8c7a9f9186d38befe424aef0ea663ed2771159397c5badd6d7dfc29fd112d158 - Sigstore transparency entry: 1414397894
- Sigstore integration time:
-
Permalink:
iam-armor/iamarmor@ed86c2ad8e89c256f9b3cb5e206cfcd03f05b52b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/iam-armor
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ed86c2ad8e89c256f9b3cb5e206cfcd03f05b52b -
Trigger Event:
push
-
Statement type:
File details
Details for the file iamarmor-0.1.1-py3-none-any.whl.
File metadata
- Download URL: iamarmor-0.1.1-py3-none-any.whl
- Upload date:
- Size: 22.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f4c68142db325dec73b4046908a423110182b239f54869243f618f7b0b50926
|
|
| MD5 |
30b3d1085f56d084ca6c6bd6b67a1ad8
|
|
| BLAKE2b-256 |
b44a0e8fd406e0806fd8653106b5530fe3f3ff679f8c6b3af2e2e969b0b6999a
|
Provenance
The following attestation bundles were made for iamarmor-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on iam-armor/iamarmor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
iamarmor-0.1.1-py3-none-any.whl -
Subject digest:
9f4c68142db325dec73b4046908a423110182b239f54869243f618f7b0b50926 - Sigstore transparency entry: 1414398025
- Sigstore integration time:
-
Permalink:
iam-armor/iamarmor@ed86c2ad8e89c256f9b3cb5e206cfcd03f05b52b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/iam-armor
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ed86c2ad8e89c256f9b3cb5e206cfcd03f05b52b -
Trigger Event:
push
-
Statement type: