Configure AWS CloudTrail for audit logging. Set up organization trails and event analysis. Use when auditing AWS activity.
npx skills add https://github.com/BagelHole/DevOps-Security-Agent-Skills --skill aws-cloudtrail
Audit AWS account activity with CloudTrail for compliance, security investigation, and operational troubleshooting.
# Create the S3 bucket for log storage
aws s3api create-bucket \
--bucket org-cloudtrail-audit-logs \
--region us-east-1
# Apply bucket policy allowing CloudTrail to write
aws s3api put-bucket-policy \
--bucket org-cloudtrail-audit-logs \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {"Service": "cloudtrail.amazonaws.com"},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::org-cloudtrail-audit-logs"
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": {"Service": "cloudtrail.amazonaws.com"},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::org-cloudtrail-audit-logs/AWSLogs/*",
"Condition": {
"StringEquals": {"s3:x-amz-acl": "bucket-owner-full-control"}
}
}
]
}'
# Block public access on the audit bucket
aws s3api put-public-access-block \
--bucket org-cloudtrail-audit-logs \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
# Enable versioning for tamper protection
aws s3api put-bucket-versioning \
--bucket org-cloudtrail-audit-logs \
--versioning-configuration Status=Enabled
# Enable server-side encryption
aws s3api put-bucket-encryption \
--bucket org-cloudtrail-audit-logs \
--server-side-encryption-configuration '{
"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "aws:kms", "KMSMasterKeyID": "alias/cloudtrail-key"}}]
}'
# Set lifecycle policy for log retention
aws s3api put-bucket-lifecycle-configuration \
--bucket org-cloudtrail-audit-logs \
--lifecycle-configuration '{
"Rules": [
{
"ID": "TransitionToGlacier",
"Status": "Enabled",
"Filter": {"Prefix": "AWSLogs/"},
"Transitions": [
{"Days": 90, "StorageClass": "GLACIER"}
]
},
{
"ID": "ExpireOldLogs",
"Status": "Enabled",
"Filter": {"Prefix": "AWSLogs/"},
"Expiration": {"Days": 2555}
}
]
}'
# Create the organization trail
aws cloudtrail create-trail \
--name org-audit-trail \
--s3-bucket-name org-cloudtrail-audit-logs \
--is-organization-trail \
--is-multi-region-trail \
--enable-log-file-validation \
--kms-key-id arn:aws:kms:us-east-1:123456789012:alias/cloudtrail-key \
--cloud-watch-logs-log-group-arn arn:aws:logs:us-east-1:123456789012:log-group:CloudTrail:* \
--cloud-watch-logs-role-arn arn:aws:iam::123456789012:role/CloudTrail-CWLogs-Role
# Start logging
aws cloudtrail start-logging --name org-audit-trail
# Configure advanced event selectors for granular control
aws cloudtrail put-event-selectors \
--trail-name org-audit-trail \
--advanced-event-selectors '[
{
"Name": "AllManagementEvents",
"FieldSelectors": [
{"Field": "eventCategory", "Equals": ["Management"]}
]
},
{
"Name": "S3DataEventsForSensitiveBuckets",
"FieldSelectors": [
{"Field": "eventCategory", "Equals": ["Data"]},
{"Field": "resources.type", "Equals": ["AWS::S3::Object"]},
{"Field": "resources.ARN", "StartsWith": [
"arn:aws:s3:::sensitive-data-bucket/",
"arn:aws:s3:::pii-bucket/",
"arn:aws:s3:::financial-data/"
]}
]
},
{
"Name": "LambdaInvocations",
"FieldSelectors": [
{"Field": "eventCategory", "Equals": ["Data"]},
{"Field": "resources.type", "Equals": ["AWS::Lambda::Function"]}
]
},
{
"Name": "DynamoDBDataEvents",
"FieldSelectors": [
{"Field": "eventCategory", "Equals": ["Data"]},
{"Field": "resources.type", "Equals": ["AWS::DynamoDB::Table"]}
]
}
]'
# Create metric filter for unauthorized API calls
aws logs put-metric-filter \
--log-group-name CloudTrail \
--filter-name UnauthorizedAPICalls \
--filter-pattern '{ ($.errorCode = "*UnauthorizedAccess*") || ($.errorCode = "AccessDenied*") }' \
--metric-transformations \
metricName=UnauthorizedAPICalls,metricNamespace=CloudTrailMetrics,metricValue=1
# Create alarm for unauthorized calls
aws cloudwatch put-metric-alarm \
--alarm-name UnauthorizedAPICallsAlarm \
--metric-name UnauthorizedAPICalls \
--namespace CloudTrailMetrics \
--statistic Sum \
--period 300 \
--threshold 5 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts
# Root account usage alarm
aws logs put-metric-filter \
--log-group-name CloudTrail \
--filter-name RootAccountUsage \
--filter-pattern '{ ($.userIdentity.type = "Root") && ($.userIdentity.invokedBy NOT EXISTS) && ($.eventType != "AwsServiceEvent") }' \
--metric-transformations \
metricName=RootAccountUsage,metricNamespace=CloudTrailMetrics,metricValue=1
aws cloudwatch put-metric-alarm \
--alarm-name RootAccountUsageAlarm \
--metric-name RootAccountUsage \
--namespace CloudTrailMetrics \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts
# Console login without MFA
aws logs put-metric-filter \
--log-group-name CloudTrail \
--filter-name ConsoleLoginWithoutMFA \
--filter-pattern '{ ($.eventName = "ConsoleLogin") && ($.additionalEventData.MFAUsed != "Yes") && ($.userIdentity.type = "IAMUser") }' \
--metric-transformations \
metricName=ConsoleLoginWithoutMFA,metricNamespace=CloudTrailMetrics,metricValue=1
# IAM policy changes
aws logs put-metric-filter \
--log-group-name CloudTrail \
--filter-name IAMPolicyChanges \
--filter-pattern '{ ($.eventName=CreatePolicy) || ($.eventName=DeletePolicy) || ($.eventName=AttachRolePolicy) || ($.eventName=DetachRolePolicy) || ($.eventName=AttachUserPolicy) || ($.eventName=PutUserPolicy) }' \
--metric-transformations \
metricName=IAMPolicyChanges,metricNamespace=CloudTrailMetrics,metricValue=1
# Security group changes
aws logs put-metric-filter \
--log-group-name CloudTrail \
--filter-name SecurityGroupChanges \
--filter-pattern '{ ($.eventName=AuthorizeSecurityGroupIngress) || ($.eventName=RevokeSecurityGroupIngress) || ($.eventName=CreateSecurityGroup) || ($.eventName=DeleteSecurityGroup) }' \
--metric-transformations \
metricName=SecurityGroupChanges,metricNamespace=CloudTrailMetrics,metricValue=1
-- Create Athena table for CloudTrail logs
CREATE EXTERNAL TABLE IF NOT EXISTS cloudtrail_logs (
eventVersion STRING,
userIdentity STRUCT<
type: STRING,
principalId: STRING,
arn: STRING,
accountId: STRING,
invokedBy: STRING,
accessKeyId: STRING,
userName: STRING,
sessionContext: STRUCT<
attributes: STRUCT<mfaAuthenticated: STRING, creationDate: STRING>,
sessionIssuer: STRUCT<type: STRING, principalId: STRING, arn: STRING, accountId: STRING, userName: STRING>
>
>,
eventTime STRING,
eventSource STRING,
eventName STRING,
awsRegion STRING,
sourceIPAddress STRING,
userAgent STRING,
errorCode STRING,
errorMessage STRING,
requestParameters STRING,
responseElements STRING,
additionalEventData STRING,
requestId STRING,
eventId STRING,
readOnly STRING,
resources ARRAY<STRUCT<arn: STRING, accountId: STRING, type: STRING>>,
eventType STRING,
recipientAccountId STRING
)
PARTITIONED BY (region STRING, year STRING, month STRING, day STRING)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://org-cloudtrail-audit-logs/AWSLogs/123456789012/CloudTrail/';
-- Find all delete operations in the last 7 days
SELECT eventTime, userIdentity.arn, eventName, sourceIPAddress,
requestParameters
FROM cloudtrail_logs
WHERE eventName LIKE '%Delete%'
AND eventTime > date_format(date_add('day', -7, current_date), '%Y-%m-%dT%H:%i:%sZ')
ORDER BY eventTime DESC
LIMIT 100;
-- Identify console logins from unusual IP addresses
SELECT eventTime, userIdentity.userName, sourceIPAddress,
additionalEventData
FROM cloudtrail_logs
WHERE eventName = 'ConsoleLogin'
AND sourceIPAddress NOT IN ('198.51.100.0/24', '203.0.113.0/24')
AND eventTime > date_format(date_add('day', -30, current_date), '%Y-%m-%dT%H:%i:%sZ')
ORDER BY eventTime DESC;
-- Access key usage patterns per principal
SELECT userIdentity.arn,
count(*) AS api_call_count,
count(DISTINCT eventName) AS unique_actions,
count(DISTINCT sourceIPAddress) AS unique_ips,
min(eventTime) AS first_seen,
max(eventTime) AS last_seen
FROM cloudtrail_logs
WHERE eventTime > date_format(date_add('day', -30, current_date), '%Y-%m-%dT%H:%i:%sZ')
GROUP BY userIdentity.arn
ORDER BY api_call_count DESC
LIMIT 50;
-- Failed API calls indicating permission issues or reconnaissance
SELECT eventTime, userIdentity.arn, eventName, errorCode, errorMessage,
sourceIPAddress
FROM cloudtrail_logs
WHERE errorCode IN ('AccessDenied', 'UnauthorizedAccess', 'Client.UnauthorizedAccess')
AND eventTime > date_format(date_add('day', -7, current_date), '%Y-%m-%dT%H:%i:%sZ')
ORDER BY eventTime DESC
LIMIT 200;
-- Track KMS key usage
SELECT eventTime, userIdentity.arn, eventName, requestParameters,
resources[1].arn AS key_arn
FROM cloudtrail_logs
WHERE eventSource = 'kms.amazonaws.com'
AND eventName IN ('Decrypt', 'Encrypt', 'GenerateDataKey', 'DisableKey', 'ScheduleKeyDeletion')
AND eventTime > date_format(date_add('day', -7, current_date), '%Y-%m-%dT%H:%i:%sZ')
ORDER BY eventTime DESC;
# Create an event data store for long-term queryable storage
aws cloudtrail create-event-data-store \
--name org-audit-event-store \
--multi-region-enabled \
--organization-enabled \
--retention-period 2555 \
--advanced-event-selectors '[
{
"Name": "AllManagementEvents",
"FieldSelectors": [
{"Field": "eventCategory", "Equals": ["Management"]}
]
}
]'
-- CloudTrail Lake SQL queries (run in console or via StartQuery API)
-- Investigate a specific user's activity
SELECT eventTime, eventName, eventSource, sourceIPAddress,
errorCode, requestParameters
FROM EVENT_DATA_STORE_ID
WHERE userIdentity.arn = 'arn:aws:iam::123456789012:user/suspicious-user'
AND eventTime > '2024-01-01 00:00:00'
ORDER BY eventTime DESC;
-- Cross-account activity summary
SELECT recipientAccountId, userIdentity.arn,
count(*) AS event_count
FROM EVENT_DATA_STORE_ID
WHERE eventTime > '2024-01-01 00:00:00'
GROUP BY recipientAccountId, userIdentity.arn
ORDER BY event_count DESC;
# Validate log file integrity for a date range
aws cloudtrail validate-logs \
--trail-arn arn:aws:cloudtrail:us-east-1:123456789012:trail/org-audit-trail \
--start-time "2024-01-01T00:00:00Z" \
--end-time "2024-01-31T23:59:59Z"
# Check trail status
aws cloudtrail get-trail-status --name org-audit-trail
# Describe the trail configuration
aws cloudtrail describe-trails --trail-name-list org-audit-trail
resource "aws_cloudtrail" "org_trail" {
name = "org-audit-trail"
s3_bucket_name = aws_s3_bucket.cloudtrail.id
is_organization_trail = true
is_multi_region_trail = true
enable_log_file_validation = true
kms_key_id = aws_kms_key.cloudtrail.arn
cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.cloudtrail.arn}:*"
cloud_watch_logs_role_arn = aws_iam_role.cloudtrail_cw.arn
include_global_service_events = true
advanced_event_selector {
name = "AllManagementEvents"
field_selector {
field = "eventCategory"
equals = ["Management"]
}
}
advanced_event_selector {
name = "SensitiveS3DataEvents"
field_selector {
field = "eventCategory"
equals = ["Data"]
}
field_selector {
field = "resources.type"
equals = ["AWS::S3::Object"]
}
field_selector {
field = "resources.ARN"
starts_with = ["arn:aws:s3:::sensitive-data-bucket/"]
}
}
tags = {
Environment = "production"
Compliance = "soc2,hipaa"
}
}
cloudtrail_checklist:
trail_configuration:
- [ ] Organization trail enabled across all accounts
- [ ] Multi-region trail enabled
- [ ] Log file validation enabled
- [ ] KMS encryption configured with dedicated key
- [ ] CloudWatch Logs integration active
- [ ] S3 bucket policy restricts access to CloudTrail service only
s3_bucket_hardening:
- [ ] Public access blocked
- [ ] Versioning enabled
- [ ] Server-side encryption enabled
- [ ] Lifecycle policy set for retention and archival
- [ ] Access logging enabled on the bucket itself
- [ ] Object Lock enabled for WORM compliance (if required)
monitoring_and_alerting:
- [ ] Metric filters for unauthorized API calls
- [ ] Alarm on root account usage
- [ ] Alarm on console login without MFA
- [ ] Alarm on IAM policy changes
- [ ] Alarm on security group and NACL changes
- [ ] Alarm on CloudTrail configuration changes
- [ ] Alarm on S3 bucket policy changes
analysis:
- [ ] Athena table created for ad-hoc queries
- [ ] CloudTrail Lake event data store for long-term queries
- [ ] Regular review of high-risk API patterns
- [ ] Automated reports for compliance evidence
operational:
- [ ] Trail status health check automated
- [ ] Log delivery latency monitored
- [ ] Log file validation run periodically
- [ ] SNS notification for trail configuration changes
Assess Kubernetes workloads and cluster configuration for AKS Automatic compatibility. Identifies incompatibilities, generates fixes, and guides migration from AKS Standard to AKS Automatic. WHEN: migrate to AKS Automatic, check AKS Automatic readiness, validate manifests for Automatic, assess cluster for Automatic compatibility, fix deployment for Automatic compatibility, identify AKS Automatic migration blockers, is my cluster ready for AKS Automatic.
Discovers available Azure OpenAI model capacity across regions and projects. Analyzes quota limits, compares availability, and recommends optimal deployment locations based on capacity requirements. USE FOR: find capacity, check quota, where can I deploy, capacity discovery, best region for capacity, multi-project capacity search, quota analysis, model availability, region comparison, check TPM availability. DO NOT USE FOR: actual deployment (hand off to preset or customize after discovery), quota increase requests (direct user to Azure Portal), listing existing deployments.
Interactive guided deployment flow for Azure OpenAI models with full customization control. Step-by-step selection of model version, SKU (GlobalStandard/Standard/ProvisionedManaged), capacity, RAI policy (content filter), and advanced options (dynamic quota, priority processing, spillover). USE FOR: custom deployment, customize model deployment, choose version, select SKU, set capacity, configure content filter, RAI policy, deployment options, detailed deployment, advanced deployment, PTU deployment, provisioned throughput. DO NOT USE FOR: quick deployment to optimal region (use preset).
Unified Azure OpenAI model deployment skill with intelligent intent-based routing. Handles quick preset deployments, fully customized deployments (version/SKU/capacity/RAI policy), and capacity discovery across regions and projects. USE FOR: deploy model, deploy gpt, create deployment, model deployment, deploy openai model, set up model, provision model, find capacity, check model availability, where can I deploy, best region for model, capacity analysis. DO NOT USE FOR: listing existing deployments (use foundry_models_deployments_list MCP tool), deleting deployments, agent creation (use agent/create), project creation (use project/create).
Intelligently deploys Azure OpenAI models to optimal regions by analyzing capacity across all available regions. Automatically checks current region first and shows alternatives if needed. USE FOR: quick deployment, optimal region, best region, automatic region selection, fast setup, multi-region capacity check, high availability deployment, deploy to best location. DO NOT USE FOR: custom SKU selection (use customize), specific version selection (use customize), custom capacity configuration (use customize), PTU deployments (use customize).
This skill should be used when working with LaminDB, an open-source data framework for biology that makes data queryable, traceable, reproducible, and FAIR. Use when managing biological datasets (scRNA-seq, spatial, flow cytometry, etc.), tracking computational workflows, curating and validating data with biological ontologies, building data lakehouses, or ensuring data lineage and reproducibility in biological research. Covers data management, annotation, ontologies (genes, cell types, diseases, tissues), schema validation, integrations with workflow managers (Nextflow, Snakemake) and MLOps platforms (W&B, MLflow), and deployment strategies.
Latch platform for bioinformatics workflows. Build pipelines with Latch SDK, @workflow/@task decorators, deploy serverless workflows, LatchFile/LatchDir, Nextflow/Snakemake integration.
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.
Take bagelhole/aws-cloudtrail from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.