The AI/ML Security Assessment Framework is a serverless, multi-account security assessment solution for AWS AI/ML workloads. It performs 52 security checks across Amazon Bedrock, Amazon SageMaker AI, and Amazon Bedrock AgentCore, generating interactive HTML reports with findings and remediation guidance.



1-aiml-security-member-roles.yaml)AIMLSecurityMemberRole to all target accounts2-aiml-security-codebuild.yaml)MultiAccountCodeBuildRole with cross-account access permissionsAIMLSecurityMemberRole in each target accountsample-aiml-security-assessment/
├── aiml-security-assessment/
│ ├── functions/security/
│ │ ├── bedrock_assessments/ # Bedrock security checks (14)
│ │ ├── sagemaker_assessments/ # SageMaker security checks (25)
│ │ ├── agentcore_assessments/ # AgentCore security checks (13)
│ │ ├── iam_permission_caching/ # AWS IAM permissions cache
│ │ ├── cleanup_bucket/ # Amazon S3 cleanup
│ │ └── generate_consolidated_report/ # HTML/CSV report generation
│ ├── statemachine/ # AWS Step Functions definition
│ ├── images/ # SAM application images
│ ├── template.yaml # AWS SAM template (single-account)
│ ├── template-multi-account.yaml # AWS SAM template (multi-account)
│ ├── samconfig.toml # SAM deployment configuration
│ ├── envvars.json # Environment variables for local testing
│ └── testfile.json # Test event file for local invocation
├── deployment/ # AWS CloudFormation templates
├── docs/ # Documentation
│ ├── DEVELOPER_GUIDE.md # This guide
│ ├── SECURITY_CHECKS.md # Security checks reference
│ ├── TROUBLESHOOTING.md # Troubleshooting guide
│ ├── diagrams/ # Architecture diagrams
│ └── icons/ # AWS service icons
├── sample-reports/ # Sample assessment reports
│ ├── scripts/ # Screenshot capture scripts
│ ├── *.html # Sample HTML reports
│ └── *.png # Report screenshots
├── buildspec.yml # AWS CodeBuild orchestration
├── buildspec-modular-example.yml # Modular buildspec example
└── consolidate_html_reports.py # Multi-account report consolidation
# buildspec.yml execution flow
1. Get active accounts from AWS Organizations
2. For each account:
- Assume AIMLSecurityMemberRole
- Deploy AI/ML assessment stack through AWS SAM
- Start AWS Step Functions execution
3. Wait for completion and consolidate results
{
"Comment": "AI/ML Assessment Module",
"StartAt": "Cleanup Amazon S3 Bucket",
"States": {
"Cleanup Amazon S3 Bucket": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Next": "AWS IAM Permission Caching"
},
"AWS IAM Permission Caching": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Next": "Parallel Service Assessments"
},
"Parallel Service Assessments": {
"Type": "Parallel",
"Branches": [
{"StartAt": "Amazon Bedrock Assessment", "States": {...}},
{"StartAt": "Amazon SageMaker AI Assessment", "States": {...}},
{"StartAt": "Amazon Bedrock AgentCore Assessment", "States": {...}}
],
"Next": "Generate Consolidated Report"
},
"Generate Consolidated Report": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"End": true
}
}
}
The framework includes 52 security checks across three AI/ML services. For the complete list of checks with descriptions, see the Security Checks Reference.
Each assessment AWS Lambda function:
Additional Functions:
To add a new AI/ML service (for example, Amazon Comprehend, Amazon Textract):
# Example: Adding Comprehend security assessment
mkdir -p aiml-security-assessment/functions/security/comprehend_assessments
cd aiml-security-assessment/functions/security/comprehend_assessments
import boto3 import json from schema import create_finding
def lambda_handler(event, context): “"”Main assessment handler for new service””” all_findings = []
# Get cached permissions
execution_id = event["Execution"]["Name"]
permission_cache = get_permissions_cache(execution_id)
# Run assessment checks
findings = check_new_service_security(permission_cache)
all_findings.append(findings)
# Generate and upload report
csv_content = generate_csv_report(all_findings)
bucket_name = os.environ.get("AIML_ASSESSMENT_BUCKET_NAME")
s3_url = write_to_s3(execution_id, csv_content, bucket_name)
return {
"statusCode": 200,
"body": {
"message": "New service assessment completed",
"findings": all_findings,
"report_url": s3_url,
},
}
def check_new_service_security(permission_cache): “"”Implement your security checks here””” findings = { “check_name”: “New Service Security Check”, “status”: “PASS”, “details”: “”, “csv_data”: [], }
# Your assessment logic here
# Use permission_cache to check IAM permissions
# Use AWS SDK to check service configurations
return findings ```
# requirements.txt
boto3>=1.26.0
botocore>=1.29.0
from enum import Enum
class SeverityEnum(str, Enum): HIGH = “High” MEDIUM = “Medium” LOW = “Low” INFORMATIONAL = “Informational” NA = “N/A”
class StatusEnum(str, Enum): FAILED = “Failed” PASSED = “Passed” NA = “N/A”
def create_finding( check_id, finding_name, finding_details, resolution, reference, severity, status ): “"”Create standardized finding format
Args:
check_id: Unique check identifier (for example, SM-01, BR-01, AC-01)
finding_name: Name of the finding
finding_details: Detailed description
resolution: Steps to resolve (empty string for N/A status)
reference: Documentation URL
severity: SeverityEnum value
status: StatusEnum value (Failed, Passed, or N/A)
"""
return {
"Check_ID": check_id,
"Finding": finding_name,
"Finding_Details": finding_details,
"Resolution": resolution,
"Reference": reference,
"Severity": severity,
"Status": status,
} ```
Add your new function to aiml-security-assessment/template.yaml:
ComprehendSecurityAssessmentFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub 'ComprehendSecurityAssessment-${AWS::AccountId}'
CodeUri: functions/security/comprehend_assessments/
Handler: app.lambda_handler
Runtime: python3.12
Timeout: 600
MemorySize: 1024
Environment:
Variables:
AIML_ASSESSMENT_BUCKET_NAME: !Ref AIMLAssessmentBucket
Policies:
- S3CrudPolicy:
BucketName: !Ref AIMLAssessmentBucket
- Statement:
- Sid: ComprehendReadPermissions
Effect: Allow
Action:
- comprehend:List*
- comprehend:Describe*
- comprehend:Get*
Resource: '*'
Add new service to the parallel execution in aiml-security-assessment/statemachine/assessments.asl.json:
{
"Parallel Service Assessments": {
"Type": "Parallel",
"Branches": [
{
"StartAt": "Bedrock Security Assessment",
"States": {"Bedrock Security Assessment": {"Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "End": true}}
},
{
"StartAt": "SageMaker Security Assessment",
"States": {"SageMaker Security Assessment": {"Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "End": true}}
},
{
"StartAt": "AgentCore Security Assessment",
"States": {"AgentCore Security Assessment": {"Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "End": true}}
},
{
"StartAt": "Comprehend Security Assessment",
"States": {"Comprehend Security Assessment": {"Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "End": true}}
}
]
}
}
Add required permissions to member role template:
In deployment/1-aiml-security-member-roles.yaml:
- Effect: Allow
Action:
- comprehend:List*
- comprehend:Describe*
- comprehend:Get*
Resource: '*'
In deployment/aiml-security-single-account.yaml (for single account mode):
- comprehend:List*
- comprehend:Describe*
- comprehend:Get*
Test your new assessment function locally:
cd aiml-security-assessment
sam build
sam local invoke ComprehendSecurityAssessmentFunction --event testfile.json
create_finding() function for consistent outputPassed: Resources were checked and met the assessed best practiceFailed: Resources were checked and found non-compliantN/A: No resources exist to check (for example, “No notebooks found”, “No guardrails configured”)High: Critical security issues requiring immediate attentionMedium: Important security improvements recommendedLow: Minor optimizations suggestedInformational: Advisory information, no action requiredN/A: Check not applicable (typically paired with N/A status)try:
# Assessment logic
result = aws_client.describe_service()
except ClientError as e:
if e.response["Error"]["Code"] == "AccessDenied":
# Handle permission issues
logger.warning(f"Access denied for service check: {str(e)}")
return create_finding(
finding_name="Permission Check",
finding_details="Insufficient permissions to assess service",
resolution="Grant required permissions to assessment role",
reference="https://docs.aws.amazon.com/service/permissions",
severity="High",
status="Failed",
)
else:
# Handle other AWS errors
logger.error(f"AWS API error: {str(e)}")
raise
except Exception as e:
# Handle unexpected errors
logger.error(f"Unexpected error: {str(e)}", exc_info=True)
raise
# Test individual function
cd aiml-security-assessment
sam build
sam local invoke NewServiceSecurityAssessmentFunction --event test-event.json
# Deploy to test account
sam deploy --stack-name aiml-security-test --capabilities CAPABILITY_IAM
# Execute AWS Step Functions
aws stepfunctions start-execution \
--state-machine-arn arn:aws:states:region:account:stateMachine:TestStateMachine \
--input '{"accountId":"123456789012"}'
For detailed troubleshooting guidance, common issues, and debugging tips, see the Troubleshooting Guide.
Report generation uses a single shared template (report_template.py) for both deployment modes:
aiml-security-assessment/functions/security/generate_consolidated_report/
├── app.py # Lambda handler (single-account)
├── report_template.py # Shared HTML/CSS/JS template
└── ...
consolidate_html_reports.py # CodeBuild script (multi-account)
| Component | Mode | Description |
|---|---|---|
app.py (AWS Lambda) |
mode='single' |
Generates per-account HTML reports during AWS Step Functions execution |
consolidate_html_reports.py |
mode='multi' |
Consolidates all account reports in AWS CodeBuild post-build phase |
Both call generate_html_report() from report_template.py with different parameters.
To update report styling, layout, or features:
report_template.py only - changes apply to both single and multi-account reportspython test_generate_report.pyget_html_template() - HTML/CSS/JS structuregenerate_table_rows() - Finding row generationgenerate_html_report() - Main entry point with mode parameter (‘single’ or ‘multi’)When you modify the report template or add new features, update the sample reports and screenshots:
After making changes to report_template.py, regenerate sample reports:
# Single-account mode
python test_generate_report.py --mode single --output sample-reports/security_assessment_single_account.html
# Multi-account mode
python test_generate_report.py --mode multi --output sample-reports/security_assessment_multi_account.html
The repository includes an automated screenshot capture tool:
# Activate virtual environment
source .venv/bin/activate
# Install dependencies (first time only)
pip install -r sample-reports/dev-requirements.txt
playwright install chromium
# Capture and optimize screenshots
python sample-reports/scripts/capture_screenshots.py
What the script does:
sample-reports/ folderWhat gets generated:
The script captures 4 screenshots:
dashboard-overview-light.png - Executive dashboard in light modedashboard-overview-dark.png - Executive dashboard in dark modefindings-table.png - Detailed findings table with filtersmulti-account-summary.png - Multi-account consolidated viewAll screenshots are automatically optimized (target: 200-300KB each, ~600KB total).
Customization:
Edit sample-reports/scripts/capture_screenshots.py to customize:
# Viewport size
VIEWPORT_WIDTH = 1440
VIEWPORT_HEIGHT = 900
# Image quality
JPEG_QUALITY = 85 # Range: 1-100
PNG_OPTIMIZE = True
# Add new screenshots to SCREENSHOTS list
SCREENSHOTS = [
{
"name": "my-screenshot",
"file": "security_assessment_single_account.html",
"description": "My Custom View",
"actions": [
{"type": "wait", "selector": ".element", "timeout": 2000},
{"type": "click", "selector": ".button"},
{"type": "scroll", "position": 500},
],
"clip": {"x": 0, "y": 0, "width": 1440, "height": 800},
}
]
Available action types:
wait - Wait for selector (for example, {"type": "wait", "selector": ".metrics", "timeout": 2000})click - Click element (for example, {"type": "click", "selector": ".theme-toggle"})scroll - Scroll to position (for example, {"type": "scroll", "position": 500})wait_time - Wait milliseconds (for example, {"type": "wait_time", "ms": 300})Troubleshooting:
| Issue | Solution |
|---|---|
playwright not installed |
pip install playwright && playwright install chromium |
| Sample reports not found | Run from repository root |
| Screenshots too large | Lower JPEG_QUALITY or reduce viewport size |
| Browser launch fails | Run playwright install-deps (Linux only) |
After generating new screenshots, update the README to reference them:
### Sample Assessment Reports
**Preview:**

*Executive summary with severity counts and service breakdown*

*Interactive findings table with filtering capabilities*
dashboard-overview-light.png, not screenshot1.pngsample-reports/ for easy organizationGitHub Actions workflows run automatically to validate code quality and security on every pull request.
| Workflow | File | What It Checks |
|---|---|---|
| Python Code Quality | .github/workflows/python-lint.yml |
ruff check (lint) and ruff format --check (formatting) on changed .py files |
| CloudFormation Lint | .github/workflows/cfn-lint.yml |
Validates deployment and SAM templates with cfn-lint |
| SAM Validate & Build | .github/workflows/sam-validate.yml |
Runs sam validate --lint and sam build on SAM templates |
| ASH Security Scan | .github/workflows/ash-security-scan.yml |
Scans changed files for secrets, dependency vulnerabilities, and IaC misconfigurations |
Additional workflows run post-merge or on schedule:
| Workflow | File | Trigger |
|---|---|---|
| ASH Full Repository Scan | .github/workflows/ash-full-repository-scan.yml |
Push to main, monthly schedule, manual |
| Labeler | .github/workflows/label.yml |
Auto-labels PRs by changed paths (bedrock, sagemaker, agentcore, deployment, docs) |
cfn-lint suppressions are configured in .cfnlintrc at the repository root for IAM actions not yet in cfn-lint’s database (for example, bedrock-agentcore actions).
Before pushing, run these checks locally to catch issues early:
# Install tools (first time only)
pip install ruff cfn-lint pytest boto3 pydantic
# Python lint and format
ruff check aiml-security-assessment/functions/security/
ruff format --check aiml-security-assessment/functions/security/
# Unit tests (213 tests, ~5 seconds, no AWS credentials needed)
python -m pytest tests/ -v
# CloudFormation lint
cfn-lint deployment/*.yaml
cfn-lint aiml-security-assessment/template.yaml
cfn-lint aiml-security-assessment/template-multi-account.yaml
# SAM validate and build
cd aiml-security-assessment
sam validate --template template.yaml --lint
sam build --template template.yaml
This developer guide provides the foundation for extending the AI/ML Security Assessment Framework. As you add new AI/ML services and security checks, please update this documentation to help future contributors understand and build upon your work.