Amazon Web Services (AWS)
Set up with an AI agent
Using Claude Code, Cursor, Codex CLI, or another coding agent? The subimage-setup:connect-aws skill walks the agent through every choice on this page. Paste this into your agent:
Use curl to download, read and follow: https://skills.subimage.io/plugins/subimage-setup/skills/connect-aws/SKILL.mdOr install the marketplace once with claude plugin marketplace add subimagesec/skills. Full instructions on Agent Skills.
Purpose
Inventories AWS resources and IAM relationships across one or more accounts. AWS is the foundation most other security analyses build on: connect every account you want to cover.
How It Works
Each account needs a read-only IAM role named that trusts the SubImage principal for your tenant. Every sync calls SubImageScanRolests:AssumeRole on that role and reads the account. If one configured account is the AWS Organizations management account or a delegated administrator account, SubImage also uses it once per sync to map the organization, roots, OUs, and account placement before scanning per-account resources.
Recommended: Organization-wide deployment
Deploy the role as an AWS Organizations StackSet so that:
- every existing account receives the role automatically
- new accounts created later pick it up with no extra work
Service-managed StackSets do not deploy to the organization's management account, even when you target the org root. To scan the management account, deploy the same template there as a standalone stack.
One-time steps
Confirm the SubImage principal ARN. When you view these docs inside your authenticated tenant, SubImage auto-fills your tenant AWS account ID in the snippets below. The principal looks like:
arn:aws:iam::<ACCOUNT_ID>:role/<TENANT_ID>-subimage-readonlyDeploy the scanning role using CloudFormation (service-managed StackSet) or Terraform below.
Target All AWS accounts and All current and future Regions.
If you want to scan the management account, deploy the same template there as a standalone stack.
Use the template below unchanged. It already includes the EKS identity/OIDC read permissions needed by EKS rows in the Kubernetes module and read access to AWS-managed public SSM parameters used for Bottlerocket and EKS optimized AMI recommendations. If you deployed this role from an older copy of the template, update the existing StackSet or standalone stack so every scanned account receives the current inline policies.SubImageScanRole
Option A – CloudFormation
AWSTemplateFormatVersion: "2010-09-09"
Description: >
IAM role used by SubImage to inventory this AWS account.
Resources:
SubImageScanRole:
Type: AWS::IAM::Role
Properties:
RoleName: "SubImageScanRole"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS:
- "arn:aws:iam::<ACCOUNT_ID>:role/<TENANT_ID>-subimage-readonly"
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/SecurityAudit
Policies:
- PolicyName: AllowSSOResourceSpecificActions
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "sso:DescribeAccountAssignmentCreationStatus"
- "sso:DescribeAccountAssignmentDeletionStatus"
- "sso:DescribeApplication"
- "sso:DescribeApplicationAssignment"
- "sso:DescribeApplicationProvider"
- "sso:DescribeInstance"
- "sso:DescribeInstanceAccessControlAttributeConfiguration"
- "sso:DescribePermissionSet"
- "sso:DescribePermissionSetProvisioningStatus"
- "sso:DescribeTrustedTokenIssuer"
- "sso:GetApplicationAccessScope"
- "sso:GetApplicationAssignmentConfiguration"
- "sso:GetApplicationAuthenticationMethod"
- "sso:GetApplicationGrant"
- "sso:GetInlinePolicyForPermissionSet"
- "sso:GetPermissionsBoundaryForPermissionSet"
- "sso:ListAccountAssignmentCreationStatus"
- "sso:ListAccountAssignmentDeletionStatus"
- "sso:ListAccountAssignments"
- "sso:ListAccountAssignmentsForPrincipal"
- "sso:ListAccountsForProvisionedPermissionSet"
- "sso:ListApplicationAccessScopes"
- "sso:ListApplicationAssignments"
- "sso:ListApplicationAssignmentsForPrincipal"
- "sso:ListApplicationAuthenticationMethods"
- "sso:ListApplicationGrants"
- "sso:ListApplicationProviders"
- "sso:ListCustomerManagedPolicyReferencesInPermissionSet"
- "sso:ListManagedPoliciesInPermissionSet"
- "sso:ListPermissionSetProvisioningStatus"
- "sso:ListPermissionSets"
- "sso:ListPermissionSetsProvisionedToAccount"
- "sso:ListTagsForResource"
- "sso:ListTrustedTokenIssuers"
Resource:
- "arn:aws:sso::*:application/*/*"
- "arn:aws:sso::*:trustedTokenIssuer/*/*"
- "arn:aws:sso:::account/*"
- "arn:aws:sso:::instance/*"
- "arn:aws:sso:::permissionSet/*/*"
- "arn:aws:sso::aws:applicationProvider/*"
- PolicyName: AllowSSOGlobalActions
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "sso:DescribeDirectories"
- "sso:DescribeRegisteredRegions"
- "sso:DescribeTrusts"
- "sso:GetApplicationInstance"
- "sso:GetApplicationTemplate"
- "sso:GetManagedApplicationInstance"
- "sso:GetMfaDeviceManagementForDirectory"
- "sso:GetPermissionSet"
- "sso:GetProfile"
- "sso:GetSharedSsoConfiguration"
- "sso:GetSsoConfiguration"
- "sso:GetSSOStatus"
- "sso:GetTrust"
- "sso:ListApplicationInstanceCertificates"
- "sso:ListApplicationInstances"
- "sso:ListApplications"
- "sso:ListApplicationTemplates"
- "sso:ListDirectoryAssociations"
- "sso:ListInstances"
- "sso:ListProfileAssociations"
- "sso:ListProfiles"
Resource: "arn:aws:sso::*"
- PolicyName: AllowEKSIdentityRead
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
# SecurityAudit already covers eks:DescribeCluster and
# eks:ListAccessEntries. These are the EKS-specific additions
# SubImage needs for identity metadata enrichment.
- "eks:DescribeAccessEntry"
- "eks:ListIdentityProviderConfigs"
- "eks:DescribeIdentityProviderConfig"
Resource: "*"
- PolicyName: AllowPublicSSMParameterRead
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "ssm:GetParametersByPath"
Resource: !Sub "arn:${AWS::Partition}:ssm:*::parameter/aws/service/*"
- PolicyName: AllowECRRead
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
Resource: "*"
- PolicyName: AllowGlueInspectorRead
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
# Not covered by SecurityAudit; needed by the Cartography
# AWS glue and inspector sync stages.
- "glue:GetConnections"
- "inspector2:ListMembers"
Resource: "*"
Outputs:
SubImageScanRoleArn:
Description: ARN of the created SubImage scanning IAM Role.
Value: !GetAtt SubImageScanRole.ArnOption B – Terraform
resource "aws_iam_role" "subimage_scan_role" {
name = "SubImageScanRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
AWS = [
"arn:aws:iam::<ACCOUNT_ID>:role/<TENANT_ID>-subimage-readonly",
]
}
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "attach_security_audit" {
role = aws_iam_role.subimage_scan_role.name
policy_arn = "arn:aws:iam::aws:policy/SecurityAudit"
}
resource "aws_iam_role_policy" "allow_sso_resource_specific_actions" {
name = "AllowSSOResourceSpecificActions"
role = aws_iam_role.subimage_scan_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"sso:DescribeAccountAssignmentCreationStatus",
"sso:DescribeAccountAssignmentDeletionStatus",
"sso:DescribeApplication",
"sso:DescribeApplicationAssignment",
"sso:DescribeApplicationProvider",
"sso:DescribeInstance",
"sso:DescribeInstanceAccessControlAttributeConfiguration",
"sso:DescribePermissionSet",
"sso:DescribePermissionSetProvisioningStatus",
"sso:DescribeTrustedTokenIssuer",
"sso:GetApplicationAccessScope",
"sso:GetApplicationAssignmentConfiguration",
"sso:GetApplicationAuthenticationMethod",
"sso:GetApplicationGrant",
"sso:GetInlinePolicyForPermissionSet",
"sso:GetPermissionsBoundaryForPermissionSet",
"sso:ListAccountAssignmentCreationStatus",
"sso:ListAccountAssignmentDeletionStatus",
"sso:ListAccountAssignments",
"sso:ListAccountAssignmentsForPrincipal",
"sso:ListAccountsForProvisionedPermissionSet",
"sso:ListApplicationAccessScopes",
"sso:ListApplicationAssignments",
"sso:ListApplicationAssignmentsForPrincipal",
"sso:ListApplicationAuthenticationMethods",
"sso:ListApplicationGrants",
"sso:ListApplicationProviders",
"sso:ListCustomerManagedPolicyReferencesInPermissionSet",
"sso:ListManagedPoliciesInPermissionSet",
"sso:ListPermissionSetProvisioningStatus",
"sso:ListPermissionSets",
"sso:ListPermissionSetsProvisionedToAccount",
"sso:ListTagsForResource",
"sso:ListTrustedTokenIssuers"
]
Resource = [
"arn:aws:sso::*:application/*/*",
"arn:aws:sso::*:trustedTokenIssuer/*/*",
"arn:aws:sso:::account/*",
"arn:aws:sso:::instance/*",
"arn:aws:sso:::permissionSet/*/*",
"arn:aws:sso::aws:applicationProvider/*"
]
}]
})
}
resource "aws_iam_role_policy" "allow_sso_global_actions" {
name = "AllowSSOGlobalActions"
role = aws_iam_role.subimage_scan_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"sso:DescribeDirectories",
"sso:DescribeRegisteredRegions",
"sso:DescribeTrusts",
"sso:GetApplicationInstance",
"sso:GetApplicationTemplate",
"sso:GetManagedApplicationInstance",
"sso:GetMfaDeviceManagementForDirectory",
"sso:GetPermissionSet",
"sso:GetProfile",
"sso:GetSharedSsoConfiguration",
"sso:GetSsoConfiguration",
"sso:GetSSOStatus",
"sso:GetTrust",
"sso:ListApplicationInstanceCertificates",
"sso:ListApplicationInstances",
"sso:ListApplications",
"sso:ListApplicationTemplates",
"sso:ListDirectoryAssociations",
"sso:ListInstances",
"sso:ListProfileAssociations",
"sso:ListProfiles"
]
Resource = "arn:aws:sso::*"
}]
})
}
resource "aws_iam_role_policy" "allow_eks_identity_read" {
name = "AllowEKSIdentityRead"
role = aws_iam_role.subimage_scan_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"eks:DescribeAccessEntry",
"eks:ListIdentityProviderConfigs",
"eks:DescribeIdentityProviderConfig"
]
Resource = "*"
}]
})
}
data "aws_partition" "current" {}
resource "aws_iam_role_policy" "allow_public_ssm_parameter_read" {
name = "AllowPublicSSMParameterRead"
role = aws_iam_role.subimage_scan_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "ssm:GetParametersByPath"
Resource = "arn:${data.aws_partition.current.partition}:ssm:*::parameter/aws/service/*"
}]
})
}
resource "aws_iam_role_policy" "allow_ecr_read" {
name = "AllowECRRead"
role = aws_iam_role.subimage_scan_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
]
Resource = "*"
}]
})
}
resource "aws_iam_role_policy" "allow_glue_inspector_read" {
name = "AllowGlueInspectorRead"
role = aws_iam_role.subimage_scan_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"glue:GetConnections",
"inspector2:ListMembers"
]
Resource = "*"
}]
})
}
output "subimage_scan_role_arn" {
description = "ARN of the created SubImage scanning IAM Role."
value = aws_iam_role.subimage_scan_role.arn
}Register AWS in SubImage
Go to Modules → Add → aws and fill in:
| Field | Example | Notes |
|---|---|---|
aws_account_ids |
111111111111,222222222222 |
Selected-account mode: every AWS account ID you want SubImage to scan. Include the management or delegated administrator account ID too if you want hierarchy mapping. |
aws_organization_account_ids (optional) |
111111111111 |
AWS management or delegated administrator account IDs that can enumerate AWS Organizations. |
aws_scan_all_active_organization_accounts |
false |
Org-wide mode: set to true to scan all ACTIVE accounts discovered from aws_organization_account_ids instead of listing selected accounts. |
aws_resource_functions (optional) |
s3,iam,ssm |
A subset of AWS resource collectors for faster, scoped scans. Select "Sync all resources" for all collectors. |
Save the module, then use Run Sync to trigger an AWS sync immediately or wait for the hourly scheduled run.
For selected-account scans, keep aws_scan_all_active_organization_accounts disabled and list the exact accounts in aws_account_ids. If you set aws_organization_account_ids in this mode, each value must also be listed in aws_account_ids; omit it only as a fallback, in which case SubImage probes the configured scan accounts and uses the first account that can enumerate the hierarchy.
For org-wide scans, set aws_scan_all_active_organization_accounts to true, leave aws_account_ids empty, and provide one or more management or delegated administrator account IDs in aws_organization_account_ids. SubImage enumerates the organization first, scans only ACTIVE accounts, and automatically includes newly active accounts on later syncs. Use selected-account mode instead if you do not want every active account in the organization scanned.
The SecurityAudit managed policy includes AWS Organizations read actions, but AWS still restricts hierarchy APIs such as ListRoots, ListAccountsForParent, and ListOrganizationalUnitsForParent to the management account or a delegated administrator account. In selected-account mode, if no configured account can enumerate the full hierarchy, SubImage skips organization hierarchy cleanup and continues normal account-scoped resource sync. In org-wide mode, SubImage must enumerate the organization to resolve accounts to scan, so the sync fails as a configuration error if the listed org account IDs cannot enumerate the hierarchy.
Org-wide mode scans every ACTIVE account returned by AWS Organizations, including the management account when AWS reports it as active. Service-managed StackSets do not deploy to the management account automatically, so deploy the template there as a standalone stack before enabling org-wide mode.
Single-account fallback
If you prefer not to use StackSets, deploy the same template as a standalone CloudFormation stack in each account. Repeat for every account you want SubImage to scan.
Troubleshooting
AccessDeniedon a specific service: the role is missing required read-only permissions. Verify the managed policy and inline policies.AccessDeniedfromssm:GetParametersByPath: update the StackSet, standalone CloudFormation stack, or Terraform role so it includesAllowPublicSSMParameterRead. If you configured prefixes outside/aws/service/..., expand the policy resource to those exact public parameter paths.AccessDeniedfromsts:AssumeRole: the trust policy does not list the SubImage principal ARN. Re-deploy the stack or add the principal manually.- AWS Organizations hierarchy is missing: in selected-account mode, verify that
aws_account_idsincludes the management account or a delegated administrator account and thataws_organization_account_idspoints at that account for predictable syncs. In org-wide mode, verify thataws_organization_account_idscontains a management or delegated administrator account ID. - Management account fails in org-wide mode, StackSet otherwise healthy: expected if
was only deployed through a service-managed StackSet. Deploy the template in the management account as a standalone stack, or use selected-account mode if you do not want to scan it.SubImageScanRole