Cloud Security Posture skill for Claude Code
Find the public bucket before someone else does. Claude audits cloud accounts with read-only roles across every region: it confirms logging baselines, runs Prowler against a CIS framework, adds targeted CLI checks for public data, open admin ports and risky identities, then collapses findings into root causes. Fixes go into Terraform or CDK after usage evidence and confirmation, each paired with an SCP or org policy guardrail.
- name
cloud-security-posture - size231 lines · ≈2,900 tokens
- files1
- licenseMIT
- updated
What Claude sees before loading it
This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.
description: Audits AWS, GCP, and Azure accounts for posture risks with read-only access - public buckets and snapshots, open security groups and firewall rules, IAM sprawl, stale keys, missing root MFA, disabled CloudTrail or GuardDuty - using Prowler, native services (Security Hub, IAM Access Analyzer, Security Command Center, Defender for Cloud), and targeted CLI checks, then ranks findings and fixes them in IaC with preventive guardrails. Use when asked to audit a cloud account, run Prowler or a CIS benchmark, find public resources, or clean up IAM. Try asking
- Run a read-only security audit of our AWS org and tell me what is publicly exposed
- Prowler gave us 2,400 failed checks. Turn this CSV into a prioritized fix plan
- Find GCP resources shared with allUsers and firewall rules open to the internet
Use it when
- You inherited cloud accounts and need to know their worst exposures
- A CIS benchmark or Prowler scan is required for an audit or customer questionnaire
- You suspect public buckets, snapshots, or open admin ports
- IAM users, keys, and roles have grown without review
Skip it when
- You are writing or debugging a specific IAM policy — use aws-iam instead
- Signs of active compromise are present — use incident-response instead
- The concern is container images or pod specs — use container-security instead
How to check the result
Re-run the same Prowler command and targeted CLI checks to show fixed items now pass, and prove each guardrail by attempting the blocked action in a sandbox account and getting AccessDenied.
The common mistake it prevents
Closing a security group or blocking public access on a bucket straight from the console without checking flow or access logs, breaking a production dependency and drifting from Terraform.
An audit is done when you know every account and region in scope, the internet-exposed and publicly shared resources are listed with owners, identity risks (root, long-lived keys, wildcard admin, cross-account trust) are ranked, and each fix lands in IaC with a guardrail that stops the same misconfiguration from returning. A 3,000-row Prowler CSV handed over unread is not an audit.
Ground rules
- Audit only accounts the user owns or is authorized to assess. Use read-only
credentials: AWS managed policies
SecurityAuditplusViewOnlyAccess, GCProles/iam.securityReviewerplusroles/cloudasset.viewer, AzureReaderplusSecurity Reader. - Do not change resources during the audit. Remediation is a separate step, and any change that can break traffic or delete data (closing a security group, deleting keys, blocking public access on a bucket serving a website) requires explicit user confirmation after naming the resource and its current use.
- Scan every enabled region. Attackers launch resources in regions you never look at.
- Treat each finding as a lead: a “public” bucket may be an intentional static site behind CloudFront. Confirm intent with the owner before calling it a breach.
- If you find evidence of active compromise (unknown IAM users, crypto-mining instances, unfamiliar access keys in use), stop the audit and switch to incident-response.
Workflow
- [ ] 1. Inventory: org, accounts/projects/subscriptions, enabled regions, owners
- [ ] 2. Confirm logging and detection baselines exist (you need them to judge anything)
- [ ] 3. Run a CSPM scan (Prowler) and pull native findings
- [ ] 4. Targeted checks for the highest-impact risks
- [ ] 5. Triage: exposure x data sensitivity x exploitability; dedupe by root cause
- [ ] 6. Remediate in IaC with confirmation; add preventive guardrails
- [ ] 7. Re-scan and record residual risk
1. Inventory
aws organizations list-accounts --query 'Accounts[?Status==`ACTIVE`].[Id,Name]' --output table
aws ec2 describe-regions --query 'Regions[].RegionName' --output text
gcloud projects list --format='table(projectId,name,parent.id)'
az account list --query '[].{name:name,id:id,state:state}' -o table
Note accounts with no owner, no IaC, or created by a former employee: they carry the most drift.
2. Logging and detection baselines
| Cloud | Must be on | Check |
|---|---|---|
| AWS | Org or multi-region CloudTrail with log file validation; GuardDuty in every region; Config recorder | aws cloudtrail describe-trails --query 'trailList[].[Name,IsMultiRegionTrail,IsOrganizationTrail,LogFileValidationEnabled]', aws guardduty list-detectors --region <r> |
| GCP | Admin Activity audit logs (always on), Data Access logs for sensitive services, Security Command Center | gcloud logging sinks list, org IAM audit config |
| Azure | Activity Log exported to Log Analytics; Defender for Cloud plans on critical resource types | az monitor diagnostic-settings subscription list |
Missing baselines are findings in their own right and go near the top of the report.
3. CSPM scan
Default: Prowler (open source, AWS/Azure/GCP/Kubernetes/M365/GitHub, maps to CIS and other frameworks).
pipx install prowler # official container images are also published; see the Prowler docs
prowler -v
prowler aws --list-compliance # exact framework IDs vary by version
prowler aws --profile audit --compliance cis_4.0_aws --output-formats csv json-ocsf html -o ./prowler-out
prowler aws --profile audit --services s3 iam ec2 --severity critical high
prowler gcp --project-ids prod-123 staging-456
prowler azure --az-cli-auth
Flags differ across major releases; run prowler <provider> --help and use the IDs
from --list-compliance rather than guessing. For multi-account AWS, run per account by
assuming a read-only audit role (--role arn:aws:iam::<acct>:role/ProwlerAudit) from a
central account. Record intentional exceptions in a mutelist file (--mutelist-file)
with a reason per entry instead of deleting rows from the report.
Also pull what the platform already knows: AWS Security Hub CSPM findings and IAM Access Analyzer external-access findings, GCP Security Command Center findings, Azure Defender for Cloud recommendations. ScoutSuite still works for a one-off multi-cloud HTML report but releases have slowed; prefer Prowler or the native services for anything you plan to rerun.
4. Targeted high-impact checks
Run these even when the scanner is green; they cover the incidents that actually happen.
AWS public data:
ACCT=$(aws sts get-caller-identity --query Account --output text)
aws s3control get-public-access-block --account-id "$ACCT" # all four flags should be true
for b in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
s=$(aws s3api get-bucket-policy-status --bucket "$b" --query PolicyStatus.IsPublic --output text 2>/dev/null)
[ "$s" = "True" ] && echo "PUBLIC POLICY: $b"
done
aws ec2 describe-snapshots --owner-ids self --restorable-by-user-ids all --query 'Snapshots[].SnapshotId'
aws ec2 describe-images --owners self --executable-users all --query 'Images[].ImageId'
aws rds describe-db-instances --query 'DBInstances[?PubliclyAccessible==`true`].DBInstanceIdentifier'
AWS network exposure (repeat per region):
aws ec2 describe-security-groups \
--filters Name=ip-permission.cidr,Values=0.0.0.0/0 \
--query 'SecurityGroups[].{id:GroupId,name:GroupName,ports:IpPermissions[].[FromPort,ToPort,IpProtocol]}'
aws ec2 describe-security-groups --filters Name=ip-permission.ipv6-cidr,Values=::/0 --query 'SecurityGroups[].GroupId'
Flag 0.0.0.0/0 or ::/0 on 22, 3389, database ports (3306, 5432, 1433, 27017, 6379,
9200), and protocol -1 (all traffic). Then check whether the group is attached to
anything (aws ec2 describe-network-interfaces --filters Name=group-id,Values=<sg>).
AWS identity:
aws iam get-account-summary --query 'SummaryMap.[AccountMFAEnabled,AccountAccessKeysPresent]'
aws iam generate-credential-report >/dev/null; sleep 5
aws iam get-credential-report --query Content --output text | base64 --decode > cred.csv
# columns to read: password_enabled, mfa_active, access_key_1_last_rotated, access_key_1_last_used_date
aws iam list-policies --scope Local --query 'Policies[].Arn' --output text # then inspect for "Action":"*","Resource":"*"
aws accessanalyzer list-analyzers
Rank: root access keys present or root without MFA; IAM users with console access and no
MFA; access keys older than 90 days or unused for 90 days; customer policies granting
*:*; role trust policies that allow any principal or an external account without
sts:ExternalId; GitHub OIDC trust without a sub condition pinning repo and branch.
Least-privilege rewrites belong to aws-iam.
GCP:
gcloud asset search-all-iam-policies --scope=organizations/<ORG_ID> \
--query='policy:(allUsers OR allAuthenticatedUsers)' --format='table(resource,policy.bindings.role)'
gcloud compute firewall-rules list --filter='direction=INGRESS AND sourceRanges:0.0.0.0/0' \
--format='table(name,network,allowed[].map().firewall_rule().list())'
gcloud iam service-accounts keys list --iam-account=<sa-email> --managed-by=user
Also look for primitive roles (roles/owner, roles/editor) granted to users or default
compute service accounts.
Azure:
az storage account list --query "[?allowBlobPublicAccess==\`true\`].[name,resourceGroup]" -o table
az graph query -q "Resources | where type =~ 'microsoft.network/networksecuritygroups' | mv-expand r=properties.securityRules | where r.properties.direction=='Inbound' and r.properties.access=='Allow' and r.properties.sourceAddressPrefix in ('*','0.0.0.0/0','Internet') | project name, resourceGroup, port=r.properties.destinationPortRange"
az role assignment list --all --query "[?roleDefinitionName=='Owner'].[principalName,scope]" -o table
5. Triage
| Priority | Pattern |
|---|---|
| Critical | Public read/write on buckets or snapshots holding customer data; admin ports open to the internet on live hosts; root keys in use; active anomalous GuardDuty/SCC findings |
| High | Users without MFA holding admin; long-lived keys with broad permissions; cross-account trust to unknown accounts; audit logging disabled |
| Medium | Unused but permissive roles; unencrypted volumes by default; missing GuardDuty in unused regions |
| Low | Benchmark hygiene with no exposure (password policy length, tagging) |
Collapse repeats into root causes: 40 open security groups from one Terraform module are one finding with one fix.
6. Remediate and prevent
- Fix in the IaC source (Terraform, CDK, Pulumi), not the console, or drift reappears on
the next apply. Check
git logon the resource before assuming it is unintended. - Before closing a port or blocking public access, check usage: VPC Flow Logs, S3 server access logs or CloudTrail data events, load balancer logs. Present the evidence and get confirmation.
- Rotate rather than delete keys in use: create new, deploy, watch
last_used, then deactivate, then delete after a wait. - Add a guardrail per finding class:
| Risk | AWS | GCP | Azure |
|---|---|---|---|
| Public buckets | Account-level S3 Block Public Access; SCP denying s3:PutAccountPublicAccessBlock changes | Org policy storage.publicAccessPrevention | Azure Policy denying allowBlobPublicAccess |
| Long-lived keys | SCP denying iam:CreateAccessKey except break-glass; OIDC for CI | Org policy iam.disableServiceAccountKeyCreation | Workload identity federation; policy on secrets expiry |
| Region sprawl | SCP denying actions outside approved regions | Org policy gcp.resourceLocations | Allowed locations policy |
| Drift in PRs | Checkov, Trivy config, or tfsec-style scan in CI on IaC | same | same |
7. Verify
Re-run the same Prowler command and diff FAIL counts per check; re-run each targeted CLI check for fixed items. Confirm guardrails by attempting the blocked action in a sandbox account (expect AccessDenied) rather than assuming the SCP or org policy applied.
Report template
# Cloud posture audit: <org> (<date>)
**Scope:** <accounts/projects/subscriptions>, regions: <all enabled | list>
**Method:** read-only; Prowler <version> (<frameworks>), native findings, targeted CLI checks
**Not covered:** <workloads inside VMs, Kubernetes in-cluster config, SaaS>
## Summary
| Priority | Findings | Root causes |
## Findings
### [C-1] Customer export bucket readable by anyone (AWS 1234-prod, us-east-1)
**Evidence:** `get-bucket-policy-status` IsPublic=True; policy grants `s3:GetObject` to `*`
**Impact:** <data class, object count> **Owner:** <team>
**Fix:** remove statement in `terraform/s3_exports.tf`; enable account Block Public Access
**Guardrail:** SCP + Access Analyzer alert on new external access
## Baselines
| Control | Status per account |
## Accepted / intentional exceptions (mutelist)
Gotchas
- Prowler PASS on “Block Public Access” at the bucket level does not mean the account setting is on; check both.
- Security groups referencing other groups are not exposure; a load balancer group open to 0.0.0.0/0 on 443 is usually intended. Focus on management and data ports.
- Credential reports are cached for up to four hours; regenerate before final numbers.
- GuardDuty, Security Hub, and Access Analyzer are regional on AWS; one disabled region is a blind spot.
- Prowler and Security Hub both bill API calls or findings in large orgs; scope with
--serviceswhile iterating.
About this skill
Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.