AWS Security Agent Verification — On-Demand Penetration Testing Detection Quality and Cost
Table of Contents
Introduction
On March 31, 2026, AWS announced the general availability of AWS Security Agent's on-demand penetration testing. It's a "frontier agent" that autonomously discovers and validates application vulnerabilities, claiming to "transform security testing from weeks to hours."
AWS Security Agent offers three capabilities:
- Penetration Testing — On-demand AI-driven pen testing ($50/task-hour) [GA]
- Design Security Review — Real-time security feedback on design documents (free, 200/month) [Preview]
- Code Security Review — Automated PR security reviews (free, 1,000/month) [Preview]
Only penetration testing reached GA with this launch — Design Security Review and Code Security Review remain in Preview. This article verifies the GA feature: penetration testing. It claims to detect not only OWASP Top 10 but also business logic flaws — but does the detection quality justify $50/task-hour? This article deploys a Flask app with 5 intentional vulnerabilities and evaluates detection rate, finding quality, and cost. Official docs: AWS Security Agent User Guide.
Prerequisites:
- AWS CLI v2 (verified with 2.34.21)
- IAM role creation permissions, EC2/VPC access
- Test region: ap-northeast-1 (Tokyo)
- Pricing: $50/task-hour (per-second billing). New customers get a 2-month free trial (200 task-hours/month)
Available in 6 regions: us-east-1, us-west-2, eu-west-1, eu-central-1, ap-southeast-2, ap-northeast-1.
Skip to Summary for results only.
Verification 1: CLI Setup (Agent Space to Pentest Creation)
Security Agent has a 3-layer resource model. Application is the account-wide Security Agent configuration (one per account, auto-created during initial console setup). Agent Space is a logical container that groups test targets, associating VPCs, IAM roles, and Target Domains. Target Domain is a registered domain authorized for testing, requiring ownership proof.
This verification created an Agent Space and completed the full flow from target registration to pentest creation using CLI only. Note that steps 3-5 require the EC2 private DNS and VPC information, so run the "EC2 deployment steps" in Verification 2 first to obtain $VPC_ID, $SUBNET_ID, $SG_ID, and $PRIVATE_DNS, then return to this section. Setup consists of 5 steps:
- IAM role creation (for Security Agent service principal)
- Agent Space creation
- Target Domain registration and verification (domain ownership proof)
- Associate VPC, IAM role, and Target Domain with Agent Space
- Pentest creation
IAM role and Agent Space creation
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=ap-northeast-1
cat > securityagent-trust-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "securityagent.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "aws:SourceAccount": "${ACCOUNT_ID}" }
}
}]
}
EOF
aws iam create-role \
--role-name SecurityAgentPentestRole \
--assume-role-policy-document file://securityagent-trust-policy.json
# VPC access policy (required for ENI creation)
aws iam attach-role-policy \
--role-name SecurityAgentPentestRole \
--policy-arn arn:aws:iam::aws:policy/AmazonVPCFullAccess
# CloudWatch Logs policy (undocumented requirement)
cat > logs-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:${REGION}:${ACCOUNT_ID}:log-group:/aws/securityagent/*"
}]
}
EOF
aws iam put-role-policy \
--role-name SecurityAgentPentestRole \
--policy-name SecurityAgentLogsAccess \
--policy-document file://logs-policy.jsonAGENT_SPACE_ID=$(aws securityagent create-agent-space \
--name "pentest-verification" \
--region $REGION \
--query "agentSpaceId" --output text)Target Domain registration and verification
You must register and verify ownership of test target domains. Two verification methods: DNS_TXT (add DNS record) and HTTP_ROUTE (place token at HTTP endpoint).
TARGET_DOMAIN_ID=$(aws securityagent create-target-domain \
--target-domain-name "$PRIVATE_DNS" \
--verification-method HTTP_ROUTE \
--region $REGION \
--query "targetDomainId" --output text)Important: the verify-target-domain CLI command performs HTTP_ROUTE verification from public internet. Private DNS names (*.compute.internal) return UNREACHABLE. However, when using VPC Config for pentesting, the PREFLIGHT step performs a separate VPC-internal domain verification which succeeds (logged as "Verifying ownership of private network domains → Completed"). In other words, CLI domain verification being UNREACHABLE does not block pentest execution via VPC Config.
Agent Space update and pentest creation
aws securityagent update-agent-space \
--agent-space-id $AGENT_SPACE_ID \
--name "pentest-verification" \
--aws-resources "{
\"vpcs\": [{
\"vpcArn\": \"$VPC_ID\",
\"securityGroupArns\": [\"$SG_ID\"],
\"subnetArns\": [\"$SUBNET_ID\"]
}],
\"iamRoles\": [\"$SERVICE_ROLE_ARN\"]
}" \
--target-domain-ids "$TARGET_DOMAIN_ID" \
--region $REGIONPENTEST_ID=$(aws securityagent create-pentest \
--title "vulnerable-flask-app-pentest" \
--agent-space-id $AGENT_SPACE_ID \
--service-role $SERVICE_ROLE_ARN \
--assets '{"endpoints": [{"uri": "http://'"$PRIVATE_DNS"'"}]}' \
--vpc-config "{
\"vpcArn\": \"$VPC_ID\",
\"securityGroupArns\": [\"$SG_ID\"],
\"subnetArns\": [\"$SUBNET_ID\"]
}" \
--region $REGION \
--query "pentestId" --output text)Note: specifying --code-remediation-strategy AUTOMATIC returns "Code remediation is not supported in ap-northeast-1". Automatic code fix generation is not available in Tokyo region (as of April 1, 2026).
Setup Results
| Step | Duration |
|---|---|
| IAM role creation + policy attachment | ~8s |
| Agent Space creation | ~3s |
| Target Domain registration | ~2s |
| Agent Space update (VPC, IAM, Target Domain) | ~3s |
| Pentest creation | ~2s |
| Total | ~18s |
CLI-only setup completes in 18 seconds. Key constraints to note:
- 1 Application per account (if already set up via console, no CLI creation needed)
- Service role must be pre-registered in Agent Space's
aws-resources.iamRoles - Service role needs CloudWatch Logs permission (
logs:CreateLogGroupetc.) — undocumented --code-remediation-strategy AUTOMATICnot available in ap-northeast-1
Verification 2: Penetration Test Execution and Finding Quality
Test Target
Deployed a Flask app with 5 intentional vulnerabilities on EC2 (t3.small). Since production applications are commonly placed in private subnets, we chose VPC Config-based private endpoint testing over public endpoints. Security Agent accesses via private DNS through VPC Config.
The 5 vulnerabilities intentionally mix "2 standard patterns detectable by traditional DAST tools" with "3 that require endpoint discovery or understanding of authentication context/business logic" — designed to measure how far Security Agent's "context-aware" approach extends beyond standard pattern matching.
| # | Vulnerability | Category | Expected Detection Difficulty |
|---|---|---|---|
| 1 | SQL Injection (login form) | Injection | Low (standard pattern) |
| 2 | Stored XSS (comment feature) | XSS | Low (standard pattern) |
| 3 | IDOR (view other users' profiles) | Access Control | Medium (auth context needed) |
| 4 | JWT verification bypass (no signature check) | Authentication | Medium (business logic) |
| 5 | Path Traversal (file download) | Injection | Medium (standard pattern but no HTML links to endpoint) |
Flask app source code (full app.py)
"""
Deliberately vulnerable Flask application for Security Agent penetration testing.
WARNING: Do NOT deploy in production environments.
"""
import os, sqlite3, hashlib, json, base64
from flask import Flask, request, jsonify, g, send_file
app = Flask(__name__)
app.config["SECRET_KEY"] = "hardcoded-secret-key-123"
DATABASE = "/tmp/vuln_app.db"
UPLOAD_DIR = "/tmp/uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)
def get_db():
if "db" not in g:
g.db = sqlite3.connect(DATABASE)
g.db.row_factory = sqlite3.Row
return g.db
@app.teardown_appcontext
def close_db(exception):
db = g.pop("db", None)
if db is not None:
db.close()
def init_db():
db = sqlite3.connect(DATABASE)
db.executescript("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT, role TEXT DEFAULT 'user');
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER, content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
""")
db.execute("INSERT OR IGNORE INTO users (username, password, email, role) VALUES (?, ?, ?, ?)",
("admin", hashlib.md5(b"admin123").hexdigest(), "admin@example.com", "admin"))
db.execute("INSERT OR IGNORE INTO users (username, password, email, role) VALUES (?, ?, ?, ?)",
("alice", hashlib.md5(b"password1").hexdigest(), "alice@example.com", "user"))
db.execute("INSERT OR IGNORE INTO users (username, password, email, role) VALUES (?, ?, ?, ?)",
("bob", hashlib.md5(b"password2").hexdigest(), "bob@example.com", "user"))
db.commit()
db.close()
# --- Vulnerability 1: SQL Injection ---
@app.route("/api/login", methods=["POST"])
def login():
data = request.get_json()
username, password = data.get("username", ""), data.get("password", "")
password_hash = hashlib.md5(password.encode()).hexdigest()
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password_hash}'"
try:
user = get_db().execute(query).fetchone()
except Exception as e:
return jsonify({"error": str(e)}), 500
if user:
payload = base64.b64encode(json.dumps(
{"user_id": user["id"], "username": user["username"], "role": user["role"]}
).encode()).decode()
return jsonify({"token": f"header.{payload}.nosignature", "user_id": user["id"]})
return jsonify({"error": "Invalid credentials"}), 401
# --- Vulnerability 2: Stored XSS ---
@app.route("/api/comments", methods=["GET"])
def get_comments():
comments = get_db().execute(
"SELECT c.id, c.content, c.created_at, u.username "
"FROM comments c JOIN users u ON c.user_id = u.id ORDER BY c.created_at DESC"
).fetchall()
return jsonify([dict(c) for c in comments])
@app.route("/api/comments", methods=["POST"])
def post_comment():
token = request.headers.get("Authorization", "")
user = decode_token(token)
if not user:
return jsonify({"error": "Unauthorized"}), 401
content = request.get_json().get("content", "")
get_db().execute("INSERT INTO comments (user_id, content) VALUES (?, ?)", (user["user_id"], content))
get_db().commit()
return jsonify({"message": "Comment posted"}), 201
# --- Vulnerability 3: IDOR ---
@app.route("/api/users/<int:user_id>", methods=["GET"])
def get_user_profile(user_id):
token = request.headers.get("Authorization", "")
user = decode_token(token)
if not user:
return jsonify({"error": "Unauthorized"}), 401
profile = get_db().execute("SELECT id, username, email, role FROM users WHERE id = ?",
(user_id,)).fetchone()
return jsonify(dict(profile)) if profile else (jsonify({"error": "Not found"}), 404)
# --- Vulnerability 4: JWT signature not verified ---
def decode_token(auth_header):
try:
token = auth_header.split("Bearer ")[1]
payload = json.loads(base64.b64decode(token.split(".")[1] + "=="))
return payload
except Exception:
return None
# --- Vulnerability 5: Path Traversal ---
@app.route("/api/files/download", methods=["GET"])
def download_file():
filename = request.args.get("filename", "")
filepath = os.path.join(UPLOAD_DIR, filename)
return send_file(filepath) if os.path.exists(filepath) else (jsonify({"error": "Not found"}), 404)
@app.route("/health")
def health():
return jsonify({"status": "ok"})
@app.route("/")
def index():
comments = get_db().execute(
"SELECT c.content, u.username FROM comments c JOIN users u ON c.user_id = u.id"
).fetchall()
comments_html = "".join(f"<div class='comment'><b>{c['username']}</b>: {c['content']}</div>" for c in comments)
return f"<!DOCTYPE html><html><head><title>Vulnerable App</title></head><body>" \
f"<h1>Comment Board</h1>{comments_html}</body></html>"
if __name__ == "__main__":
init_db()
app.run(host="0.0.0.0", port=80)EC2 deployment steps
Uses the default VPC. Security Agent runs 13 attack categories in parallel, so Flask's development server (single-threaded) will crash under the load. gunicorn (multi-worker) is required.
REGION=ap-northeast-1
VPC_ID=$(aws ec2 describe-vpcs --filters "Name=isDefault,Values=true" \
--query "Vpcs[0].VpcId" --output text --region $REGION)
VPC_CIDR=$(aws ec2 describe-vpcs --vpc-ids $VPC_ID \
--query "Vpcs[0].CidrBlock" --output text --region $REGION)
SUBNET_ID=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" \
--query "Subnets[0].SubnetId" --output text --region $REGION)
AMI_ID=$(aws ssm get-parameters \
--names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query "Parameters[0].Value" --output text --region $REGION)# Role for SSM connectivity
cat > ec2-trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role --role-name SecurityAgentPentestEC2Role \
--assume-role-policy-document file://ec2-trust-policy.json
aws iam attach-role-policy --role-name SecurityAgentPentestEC2Role \
--policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
aws iam create-instance-profile \
--instance-profile-name SecurityAgentPentestEC2Profile
aws iam add-role-to-instance-profile \
--instance-profile-name SecurityAgentPentestEC2Profile \
--role-name SecurityAgentPentestEC2Role
sleep 10 # Wait for IAM propagation# SG: Allow HTTP 80 from within the VPC only
SG_ID=$(aws ec2 create-security-group \
--group-name security-agent-pentest-target \
--description "Pentest target - HTTP from VPC only" \
--vpc-id $VPC_ID --region $REGION --query "GroupId" --output text)
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID --protocol tcp --port 80 \
--cidr $VPC_CIDR --region $REGION
# Launch EC2 instance
INSTANCE_ID=$(aws ec2 run-instances \
--image-id $AMI_ID --instance-type t3.small \
--subnet-id $SUBNET_ID --security-group-ids $SG_ID \
--iam-instance-profile Name=SecurityAgentPentestEC2Profile \
--associate-public-ip-address \
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=security-agent-pentest-target}]" \
--region $REGION --query "Instances[0].InstanceId" --output text)
aws ec2 wait instance-running --instance-ids $INSTANCE_ID --region $REGION
PRIVATE_DNS=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID \
--query "Reservations[0].Instances[0].PrivateDnsName" --output text --region $REGION)
echo "Private DNS: $PRIVATE_DNS"# Wait for SSM Agent to come online
while true; do
STATUS=$(aws ssm describe-instance-information \
--filters "Key=InstanceIds,Values=${INSTANCE_ID}" \
--query "InstanceInformationList[0].PingStatus" --output text --region $REGION 2>/dev/null)
[ "$STATUS" = "Online" ] && break
echo "Waiting for SSM Agent..." && sleep 10
done
# Transfer app.py to EC2 via base64 encoding
APP_B64=$(base64 -w0 app.py)
aws ssm send-command --instance-ids $INSTANCE_ID \
--document-name "AWS-RunShellScript" \
--parameters "{\"commands\":[
\"sudo mkdir -p /opt/vulnerable-app\",
\"echo '$APP_B64' | base64 -d | sudo tee /opt/vulnerable-app/app.py > /dev/null\"
]}" --region $REGION
# Install gunicorn + initialize DB + start the app
aws ssm send-command --instance-ids $INSTANCE_ID \
--document-name "AWS-RunShellScript" \
--timeout-seconds 120 \
--parameters '{"commands":[
"sudo python3 -m ensurepip --upgrade",
"sudo python3 -m pip install flask==3.1.0 gunicorn",
"cd /opt/vulnerable-app && sudo python3 -c \"exec(open('app.py').read().split('if __name__')[0]); init_db()\"",
"sudo systemd-run --unit=vuln-app --working-directory=/opt/vulnerable-app gunicorn --bind 0.0.0.0:80 --workers 4 --timeout 120 app:app"
]}' --region $REGION
sleep 5
# Verify the app is running
aws ssm send-command --instance-ids $INSTANCE_ID \
--document-name "AWS-RunShellScript" \
--parameters '{"commands":["curl -s http://localhost/health"]}' \
--region $REGIONTest Execution
aws securityagent start-pentest-job \
--pentest-id $PENTEST_ID \
--agent-space-id $AGENT_SPACE_ID \
--region $REGIONMonitor progress with batch-get-pentest-jobs. The test took approximately 2 hours 43 minutes to complete.
aws securityagent batch-get-pentest-jobs \
--agent-space-id $AGENT_SPACE_ID \
--pentest-job-ids <pentestJobId> \
--region $REGION \
--query "pentestJobs[0].{status:status,steps:steps[*].{name:name,status:status}}"The test progresses through 4 steps:
- PREFLIGHT — Builds container environment inside the VPC, verifies endpoint reachability
- STATIC_ANALYSIS — Crawls endpoints, runs network and TLS scans
- PENTEST — Executes multi-step attack scenarios
- FINALIZING — Aggregates results, CVSS scoring, report generation
CloudWatch Logs (/aws/securityagent/<agent-space-name>/<pentest-id>) capture detailed execution logs. Observed internal tasks:
| Task | Duration | Description |
|---|---|---|
| Setup Testing Environment | ~26 min | Builds compute, networking, and container inside VPC |
| Endpoint Accessibility Validation | seconds | HTTP connectivity check |
| SCANNER | ~7s | Network scan (port/service discovery) |
| TLS SCANNER | ~8s | TLS configuration scan |
| CRAWLER | ~29 min | Multiple AI agents explore endpoints in parallel using browser and Python |
| PENTEST (attack tasks) | ~1h 37m | 13 attack categories in parallel + VALIDATOR TASK verification |
The CRAWLER behavior is notable: multiple AI agents run in parallel, using browser sessions (browse tool) and Python requests to autonomously discover and analyze endpoints (at least 4 distinct agent_ids were observed in the first run's CloudWatch Logs). They probe unknown paths (/admin, /api/v1, /graphql), parse form structures, and map the application — a fundamentally different approach from traditional DAST crawlers.
During the PENTEST phase, 13 OWASP Top 10-centered attack categories (XSS, SQLi, IDOR, Path Traversal, SSRF, SSTI, Code Injection, Command Injection, LFI, XXE, JWT, Privilege Escalation, Arbitrary File Upload) ran in parallel. After each attack task completed, a VALIDATOR TASK reproduced findings to confirm confidence levels.
The first run failed because Flask's development server (single-threaded) crashed under parallel attack load. The second run used gunicorn with 4 workers and completed successfully. A multi-worker server configuration is required for pentest targets.
Results
After test completion, retrieve findings with list-findings and batch-get-findings.
# List findings
aws securityagent list-findings \
--agent-space-id $AGENT_SPACE_ID \
--pentest-job-id <pentestJobId> \
--region $REGION
# Get finding details (includes reproduction steps, CVSS, impact analysis)
aws securityagent batch-get-findings \
--agent-space-id $AGENT_SPACE_ID \
--finding-ids <findingId1> <findingId2> ... \
--region $REGIONTotal test duration was approximately 2 hours 43 minutes (PREFLIGHT ~32 min + STATIC_ANALYSIS ~29 min + PENTEST ~1h 37m + FINALIZING ~6 min).
| Vulnerability | Detected | CVSS | Confidence | Notes |
|---|---|---|---|---|
| SQL Injection | Yes | 9.8 CRITICAL | HIGH | Auth bypass reproduced with ' OR '1'='1. Also discovered credentials admin:admin123 |
| Stored XSS | Yes | 6.4 MEDIUM | LOW | JS execution confirmed via <img onerror> payload. Also flagged missing CSP headers |
| IDOR | Yes | 6.5 MEDIUM | HIGH | Used alice's token to access GET /api/users/1 (admin) and retrieved profile data |
| JWT bypass | Yes | 10.0 CRITICAL | HIGH | Crafted unsigned token with arbitrary user_id/role, reproduced privilege escalation |
| Path Traversal | No | - | - | Not detected |
| Unexpected: Integer Overflow | Yes | 4.3 MEDIUM | HIGH | GET /api/users/99999999999999999 triggered 500 error. Not a planted vulnerability |
Detection rate: 4/5 (80%), false positives: 0
The reason Path Traversal went undetected cannot be confirmed definitively, but /api/files/download?filename= had no links on any HTML page and no API documentation or source code was provided, making it likely that the CRAWLER never discovered this endpoint. Registering source code as an artifact would expand the crawler's discovery scope, though whether that would lead to detection is unverified.
The unexpected Integer Overflow finding is worth noting — the agent discovered that extreme values for user_id cause a 500 Internal Server Error, an input validation issue that was not deliberately planted.
Finding quality is high. Each finding includes:
- Detailed reproduction steps — in curl command format, copy-paste ready
- CVSS scoring — 9.8 (SQL Injection), 10.0 (JWT) with appropriate severity ratings
- Impact analysis — concrete descriptions of session hijacking, data leakage, etc.
- Remediation guidance — specific fixes like parameterized queries and output escaping (within the finding description)
Cost
At $50/task-hour, the above duration (~2h 43m) comes to approximately $136 (per-second billing). The 2-month free trial for new customers (200 task-hours/month) easily covers tests of this scale.
Summary — Who Should Use This
In this verification, Security Agent detected 4 out of 5 known vulnerabilities in a small app (6 endpoints) with zero false positives. It is practical as continuous security testing that fills the gaps between periodic manual tests. The ability to detect business logic vulnerabilities (IDOR, JWT bypass) with HIGH confidence is a clear differentiator from traditional DAST tools.
| Aspect | Result | Rating |
|---|---|---|
| CLI setup | 18s (IAM role to pentest creation) | Excellent |
| PREFLIGHT (VPC env setup) | ~32 min | Good |
| Total test duration | ~2h 43m (small app with 6 endpoints) | Good |
| Detection rate | 4/5 (80%) + 1 unexpected finding | Excellent |
| False positives | 0 | Excellent |
| Finding quality | Reproduction steps, CVSS, impact analysis, remediation guidance | Excellent |
| Business logic vulns | IDOR and JWT bypass detected with HIGH confidence | Excellent |
| VPC testing | Supported via VPC Config | Excellent |
| Code remediation | Not available in Tokyo | Fair |
| Cost | ~$136 (2h 43m x $50/task-hour) | Good |
- Best suited for filling gaps between periodic manual pentests — Manual pentesting (thousands to tens of thousands of dollars) typically happens 1-2 times per year. At ~$136/run, Security Agent enables testing at development-cycle frequency, with a 2-month free trial for evaluation
- Register source code / API docs as artifacts for potential detection improvement — We achieved 4/5 detection with URL-only configuration. The missed Path Traversal was on an endpoint with no HTML links. Registering source code could expand the crawler's discovery scope (unverified)
- Factor in ~32 min PREFLIGHT overhead for CI/CD — VPC environment setup takes time, making nightly batches or weekly schedules more practical than pre-deploy gates
- Use multi-worker servers for test targets — 13 attack categories run in parallel; single-threaded servers can't handle the load. Production environments are typically fine, but staging/test environments may need gunicorn or equivalent
Production Considerations
- Domain verification with VPC Config — The CLI
verify-target-domainreturnsUNREACHABLEfor private DNS, but VPC Config pentests perform internal VPC verification during PREFLIGHT, so test execution is not blocked. Target Domain registration is still required - Don't forget CloudWatch Logs permissions — Without
logs:CreateLogGroupon the service role, PREFLIGHT fails immediately. Not documented, easy to miss when creating IAM policies - Code Remediation not available in Tokyo —
--code-remediation-strategy AUTOMATICis not available in Tokyo (supported regions unconfirmed). Choose region accordingly if code fix suggestions are needed
Cleanup
Resource deletion
# Delete pentest
aws securityagent batch-delete-pentests \
--agent-space-id $AGENT_SPACE_ID \
--pentest-ids $PENTEST_ID --region $REGION
# Delete target domain
aws securityagent delete-target-domain \
--target-domain-id $TARGET_DOMAIN_ID --region $REGION
# Delete agent space
aws securityagent delete-agent-space \
--agent-space-id $AGENT_SPACE_ID --region $REGION
# Terminate EC2
aws ec2 terminate-instances --instance-ids $INSTANCE_ID --region $REGION
# Delete IAM roles
aws iam detach-role-policy --role-name SecurityAgentPentestRole \
--policy-arn arn:aws:iam::aws:policy/AmazonVPCFullAccess
aws iam delete-role-policy --role-name SecurityAgentPentestRole \
--policy-name SecurityAgentLogsAccess 2>/dev/null
aws iam delete-role --role-name SecurityAgentPentestRole
aws iam detach-role-policy --role-name SecurityAgentPentestEC2Role \
--policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
aws iam delete-instance-profile \
--instance-profile-name SecurityAgentPentestEC2Profile
aws iam delete-role --role-name SecurityAgentPentestEC2Role