This demo shows an end-to-end security workflow: Prowler scans the Terraform code in your repo on every push/PR, sends findings to Prowler Cloud, and then Claude Code consumes those findings via the Prowler MCP Server to automatically fix the issues and open a pull request. Merging that PR triggers the next scan, closing the loop.
The workflow is a continuous loop, not a one-shot pipeline: every push or PR is scanned, findings land in Prowler Cloud, Claude Code turns them into a remediation PR, and merging that PR triggers the next scan that verifies the fixes.
flowchart TD
tf["Terraform code<br/>(terraform/)"]
gha["GitHub Actions<br/>prowler iac --scan-path ./terraform"]
cloud["Prowler Cloud<br/>(findings)"]
cc["Claude Code<br/>(auto-remediation)"]
pr["Pull request<br/>(security fixes)"]
tf -- "push / PR" --> gha
gha -- "--push-to-cloud" --> cloud
cloud -- "Prowler MCP Server" --> cc
cc -- "fix + open PR" --> pr
pr -- "merge re-triggers the scan" --> tf
Key point: Prowler scans the IaC files directly (prowler iac --scan-path ./terraform) — no AWS credentials needed for the scan. It detects misconfigurations in the Terraform code itself before anything is deployed.
- Prowler Cloud account with an API key
- GitHub repository with Actions enabled
- Claude Code CLI installed
No AWS credentials are needed — Prowler's iac provider scans the code statically.
- Fork/clone this repo
- Add the
PROWLER_CLOUD_API_KEYGitHub secret - Push to
mainor open a PR (triggers Prowler IaC scan) - Set
PROWLER_APP_API_KEYenv var and open Claude Code in this repo - Ask Claude Code to query findings and fix the Terraform
Go to Settings > Secrets and variables > Actions and add:
| Secret | Description |
|---|---|
PROWLER_CLOUD_API_KEY |
API key from Prowler Cloud (needs "Manage Ingestions") |
That's it — no AWS credentials required for IaC scanning.
Note: If
PROWLER_CLOUD_API_KEYis not set,--push-to-cloudis silently skipped and results are only saved locally as OCSF JSON underoutput/. Make sure the secret is configured before running the workflow.
The workflow is at .github/workflows/prowler-scan.yml. It triggers when Terraform files change:
name: Prowler IaC Scan
on:
push:
branches: [main]
paths:
- "terraform/**"
- ".github/workflows/**"
pull_request:
branches: [main]
paths:
- "terraform/**"
workflow_dispatch:
permissions:
contents: read
jobs:
prowler-iac-scan:
name: Scan IaC with Prowler
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2.19.1
with:
egress-policy: audit
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Install Trivy
run: |
sudo apt-get install -y wget apt-transport-https gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install -y trivy
- name: Install Prowler
run: pip install prowler
- name: Run Prowler IaC Scan
env:
PROWLER_CLOUD_API_KEY: ${{ secrets.PROWLER_CLOUD_API_KEY }}
run: prowler iac --scan-path ./terraform --push-to-cloud --provider-uid https://github.com/prowler-cloud/iac-scan-pipeline-testingWhat it does:
- Hardens the runner with StepSecurity harden-runner (audits outbound network calls)
- Checks out the repo code
- Installs Trivy — Prowler's IaC provider uses it as the scanning engine
- Installs Prowler
- Runs
prowler iac --scan-path ./terraformto scan the Terraform files for misconfigurations --push-to-cloudsends findings to Prowler Cloud (requiresPROWLER_CLOUD_API_KEY— without it the push is silently skipped and results are only saved locally)--provider-uididentifies this repo as the IaC source in Prowler Cloud
Note: Third-party actions are pinned to a commit SHA (with the version in a comment) as a supply-chain best practice. When you fork, update
--provider-uidto point at your own repository.Forks: Pull requests from forks don't have access to repository secrets, so
PROWLER_CLOUD_API_KEYwon't be available and the cloud push is skipped for fork PRs. Push to your own fork'smain(or useworkflow_dispatch) to push findings to Prowler Cloud.
Triggers:
- Push to
main— scans on every merge that changesterraform/files - Pull requests — scans the IaC in the PR before merge
- Manual trigger — via the Actions tab (
workflow_dispatch)
- Go to the Actions tab in your GitHub repo
- Select "Prowler IaC Scan" and click "Run workflow" (or push a change to
terraform/) - Once complete, go to Prowler Cloud to see the findings
The Prowler MCP Server lets Claude Code query and analyze your security findings directly.
This repo includes a pre-configured .mcp.json that points at the managed Prowler Cloud MCP endpoint and reads your API key from the PROWLER_APP_API_KEY environment variable:
{
"mcpServers": {
"prowler": {
"type": "http",
"url": "https://mcp.prowler.com/mcp",
"headers": {
"Authorization": "Bearer ${PROWLER_APP_API_KEY}"
}
}
}
}You just need to set the API key before launching Claude Code:
export PROWLER_APP_API_KEY="pk_your_key_here"Generate an API key at: Prowler Cloud > Settings > API Keys (needs full access permissions).
If you prefer to run the MCP server locally:
docker pull prowlercloud/prowler-mcpUpdate .mcp.json to:
{
"mcpServers": {
"prowler": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "PROWLER_APP_API_KEY", "prowlercloud/prowler-mcp"],
"env": {
"PROWLER_APP_API_KEY": "pk_your_key_here"
}
}
}
}docker run --rm -p 8000:8000 \
-e PROWLER_APP_API_KEY="pk_your_key_here" \
prowlercloud/prowler-mcp --transport http --host 0.0.0.0 --port 8000Update .mcp.json to:
{
"mcpServers": {
"prowler": {
"type": "url",
"url": "http://localhost:8000/mcp"
}
}
}cd iac-scan-pipeline-testing
claude mcp listYou should see the prowler server listed and connected.
The terraform/main.tf contains resources with deliberate security misconfigurations that Prowler's IaC scanner will detect:
| Resource | Issue | Expected Finding |
|---|---|---|
| S3 Bucket | No server-side encryption | S3 bucket without default encryption |
| S3 Bucket | No versioning | S3 bucket without versioning enabled |
| S3 Bucket | No public access block | S3 bucket without public access block |
| Security Group | SSH open to 0.0.0.0/0 |
Security group allows SSH from internet |
| Security Group | RDP open to 0.0.0.0/0 |
Security group allows RDP from internet |
| EC2 Instance | IMDSv1 enabled | EC2 instance not using IMDSv2 |
| EC2 Instance | EBS volume not encrypted | EBS volume without encryption |
| RDS Instance | Publicly accessible | RDS instance is publicly accessible |
| RDS Instance | Storage not encrypted | RDS instance storage not encrypted |
| RDS Instance | No backup retention | RDS instance without backup enabled |
Step 1: Push the vulnerable Terraform and trigger the scan
Push the repo to GitHub. The workflow triggers automatically when terraform/ files change on main, or you can manually trigger it from the Actions tab.
Step 2: View findings in Prowler Cloud
Open Prowler Cloud and review the findings. You should see multiple FAIL results from the IaC scan matching the table above.
Step 3: Open Claude Code
cd iac-scan-pipeline-testing
export PROWLER_APP_API_KEY="pk_your_key_here"
claudeStep 4: Query findings via MCP
Ask Claude Code to pull in the findings:
> List all FAIL findings from Prowler
> Show me the critical and high severity findings
> What S3 security issues were found in the IaC scan?
Step 5: Fix the Terraform and open a PR
> Fix all the security findings in the terraform/ directory and open a PR with the changes
Claude Code will:
- Analyze each finding from Prowler
- Edit
terraform/main.tfto add encryption, versioning, public access blocks, restrict security group CIDRs, enforce IMDSv2, etc. - Create a branch, commit, and open a PR
Step 6: Review the PR
Review the diff. You should see changes like:
aws_s3_bucket_server_side_encryption_configurationresource addedaws_s3_bucket_versioningresource addedaws_s3_bucket_public_access_blockresource added- Security group CIDRs restricted from
0.0.0.0/0to specific ranges metadata_optionsblock withhttp_tokens = "required"(IMDSv2)encrypted = trueon EBS volumespublicly_accessible = falseandstorage_encrypted = trueon RDSbackup_retention_periodincreased
Step 7: Merge and re-scan
Merge the PR. The push to main triggers a new Prowler IaC scan. Verify in Prowler Cloud that the findings are now resolved.
| Prompt | Purpose |
|---|---|
List all FAIL findings from Prowler |
Get a summary of all issues |
Summarize the security posture of my Terraform code based on Prowler findings |
High-level overview |
What are the critical and high severity findings? |
Prioritize remediation |
Fix the S3 bucket in terraform/main.tf to address all Prowler findings |
Targeted fix |
Fix all Prowler findings in this repo and open a PR |
Full auto-remediation |
Explain the finding about SSH being open to the internet |
Learn about a specific issue |
Prowler's IaC scanner supports more than just Terraform:
| Format | File Extensions |
|---|---|
| Terraform | *.tf, *.tf.json, *.tfvars, plan files |
| CloudFormation | *.yml, *.yaml, *.json |
| Kubernetes | *.yml, *.yaml, *.json |
| Docker | Dockerfile, Containerfile |
| Helm | *.yml, *.yaml, *.tpl, *.tar.gz |
| Azure ARM Templates | *.json |
| Ansible | *.yml, *.yaml, *.json |
| Issue | Solution |
|---|---|
| MCP server not connecting | Verify PROWLER_APP_API_KEY is set and valid. Run claude mcp list to check. |
| GitHub Actions failing | Verify the PROWLER_CLOUD_API_KEY secret is set in the repo settings. |
| No findings in Prowler Cloud | Check the workflow logs for "Push to Prowler Cloud skipped: no API key configured" — this means PROWLER_CLOUD_API_KEY is not set. |
| Claude Code can't create a PR | Ensure gh CLI is authenticated: gh auth status. |
This project is licensed under the Apache License 2.0 — see the LICENSE file for details.