Kumo.io

Docs Connect your cloud

Connect your cloud

Connect AWS

Give Kumo.io read-only access to one AWS account. On kumo.io you deploy a small CloudFormation stack that creates a role Kumo.io can assume. Self-hosted Kumo.io can also crawl as the identity it already runs with.

Applies to: BothPlan: Allread-onlyno long-lived keys with a roleone account per source

Choose how Kumo.io authenticates

Kumo.io connects to one AWS account per source. When you add an AWS source you pick one of three methods:

Method What you give Kumo.io Hosted (kumo.io) Self-hosted
Cross-account role (recommended)The ARN of a read-only IAM role in your account that trusts Kumo.io, guarded by an external IDYesYes, when the deployment knows its own AWS account
Access keyAn IAM user's access key ID and secret access keyYesYes
Runtime identity (advanced)Nothing. Kumo.io crawls as the identity its deployment already runs withNoYes

A role is the safest choice: no long-lived keys leave your account, and you can revoke access by deleting the role.

Connect with a cross-account role

You need permission to create an IAM role and a CloudFormation stack in the AWS account you want to connect.

  1. In Kumo.io, open Cloud Connections and select Add a Source. (From a workspace you can also use Add Source on the Connections tab.)
  2. Under 1 · Choose a Provider, pick Amazon Web Services.
  3. Under 2 · How Should Kumo.io Authenticate?, pick Cross-account role (recommended). (If your account already has an AWS credential, a Choose a Credential step comes first: pick New Credential, and the steps below are numbered one higher. To reuse the credential instead, see Reuse a credential.)
  4. In the Stand Up the Read-Only Role panel, note the external ID Kumo.io generated for this connection. Choose a Region for the console link, select Copy Template, then Open CloudFormation console.
  5. In the AWS console, create a stack from the copied template and deploy it.
  6. When the stack completes, copy the RoleArn value from the stack's Outputs.
  7. Back in Kumo.io, under 3 · Paste the Role ARN, paste it into Role ARN. Kumo.io checks its shape before sending anything: arn:aws:iam::<12-digit account>:role/<name> (or the aws-cn / aws-us-gov partition). A user ARN or the stack's own ARN is flagged on the field.
  8. Pick the Workspace it syncs into. To give the source a Name, open Advanced Options.
  9. Select Test Connection to check the role before saving, then Connect Source.

Kumo.io stores the credential, discovers the account, creates the source and starts the first sync. You land on that sync's progress page; resources and a diagram appear when it finishes.

Kumo.io generates the external ID once for the connection you are setting up, and keeps it with your account until that connection succeeds. Reloading the page, moving between Connect AWS With a Cross-Account Role and Add a Source, or continuing in another browser or on another device shows the same ID, so the role you deployed still matches. After a successful connection the next one gets a new ID. It is also the only external ID Kumo.io accepts for a cross-account role. If the role's trust policy uses any other external ID (one you chose yourself, or one from an earlier connection), connecting and Test Connection both fail with "the external ID doesn't match the one Kumo.io issued for this account". Re-apply the template with the ID Kumo.io shows, then connect again. The external ID is required, too. Kumo.io assumes the first role, the one in your account, only with your external ID, so a role sent without one fails with "the role has no external ID". Add a Source always sends it. If you use Then Assume, the second role is assumed with your first role's session, so Kumo.io doesn't require an external ID for it (if its trust policy asks for one, use the same ID).

The region you choose only decides which CloudFormation console opens. IAM roles are global, and Kumo.io discovers which regions to read on its own (see Regions).

What the template creates

The template creates one IAM role and nothing else:

  • Role name: KumoReadOnlyRole.
  • Trust: Kumo.io's AWS account may call sts:AssumeRole, but only when it presents your external ID (an sts:ExternalId condition). The external ID protects you from another Kumo.io customer pointing Kumo.io at your role.
  • Permissions: the AWS-managed ReadOnlyAccess policy.
  • Output: RoleArn, the value you paste back into Kumo.io.

Optional: a second role hop

The role method has two optional fields under Advanced Options, Then Assume and External ID for the second hop. Use them when the role Kumo.io should read with is only reachable through the first role, for example an audit role. Kumo.io assumes the first role, then the second, and crawls as the second. If the second role's trust policy requires an external ID, it must be the same one Kumo.io issued for your account; any other is refused. On kumo.io, neither role may be in Kumo.io's own AWS account.

Use the least-privilege policy instead

ReadOnlyAccess is convenient, but it allows far more than Kumo.io reads. Kumo.io also publishes its own read-only policy, which lists exactly the actions Kumo.io's crawlers call: 144 Get, List and Describe actions. Kumo.io's build checks this policy against the crawlers in both directions: it fails if the policy grants an action no crawler uses, or misses one a crawler calls.

The policy has five statements:

Statement Grants Keep it if
KumoInventoryReadOnly134 read actions across the services Kumo.io inventoriesAlways
KumoOrganizationsDiscovery4 AWS Organizations read actionsYou use organization discovery
KumoCostReportDiscovery3 actions that find your cost and usage report exportsYou want cost data
KumoUsageMetricsReadcloudwatch:GetMetricData, to read Lambda invocation and duration metricsYou want cost estimates for Lambda functions. Without it they show no estimate, and the cost view says the permission is missing
KumoCostAndUsageReportReads3:GetObject and s3:ListBucket on your report bucketYou want cost data. Replace KUMO_CUR_BUCKET with your bucket name

To use it, create the role yourself instead of deploying Kumo.io's template:

  1. Start the cross-account role flow in Kumo.io as above, and copy the template to find Kumo.io's AWS account ID (the trust principal) and your external ID.
  2. In IAM, create a role with this trust policy, replacing KUMO_ACCOUNT_ID and KUMO_EXTERNAL_ID with those values:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "KumoAssumeRole",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::KUMO_ACCOUNT_ID:root" },
      "Action": "sts:AssumeRole",
      "Condition": { "StringEquals": { "sts:ExternalId": "KUMO_EXTERNAL_ID" } }
    }
  ]
}
  1. Attach Kumo.io's read-only policy to the role as a customer-managed policy, without any statements you don't need.
  2. Paste the role's ARN into Role ARN and continue from step 9.

The full policy is at the end of this page. The AWS coverage page lists the resource types it lets Kumo.io read.

Connect with an access key

If you can't create a role, pick Access key and enter an Access key ID and Secret access key. Use a dedicated IAM user with a read-only policy, such as the one above.

Kumo.io checks the key's shape before sending it: an Access key ID is AKIA (long-term) or ASIA (temporary) plus 16 characters, and a Secret access key is 40 characters.

  • Session token is optional and only for temporary credentials (STS or SSO sessions); it is under Advanced Options. An ASIA… key needs it. Temporary credentials expire, so a source that uses them stops syncing when they do. Prefer a long-lived key for a standing source.
  • Then Assume Role and External ID are optional, also under Advanced Options. Fill them in to have Kumo.io use the key only to assume a role, and crawl as that role.

Connect with the runtime identity (self-hosted)

A self-hosted Kumo.io running in AWS can crawl as the identity it already has: environment credentials, an AWS profile, a web-identity token, an ECS task role, or an EC2 instance profile. Nothing is pasted or stored.

When Kumo.io detects such an identity, Add a Source shows a banner, This deployment already runs as an AWS identity, with the identity's ARN, account and source, and a Use Runtime Identity — Nothing to Paste button. You can also pick Runtime identity (advanced) as the method. Its optional Then Assume Role field, under Advanced Options, makes Kumo.io assume a role from the runtime identity and crawl as that role, which is how one deployment reads other accounts.

From the command line, kumo init does the same during first-time setup:

kumo init --from-ambient

It creates a runtime-identity credential, discovers the account through STS, and reports the identity it found. If no identity is found, or AWS rejects the one found, it stops and says why.

The runtime identity is not available on kumo.io. Hosted Kumo.io never crawls as its own AWS identity, so you always connect with a role or a key in your own account.

Cross-account roles on self-hosted

The cross-account role method needs to know which AWS account your role must trust. A self-hosted deployment works this out from its own AWS identity. If its web interface runs outside the AWS account that crawls, set KUMO_AWS_TRUST_ACCOUNT_ID to the crawling account's 12-digit ID. If Kumo.io can't determine the account, the role panel says connecting with a cross-account role isn't available on this deployment, and you can choose another method.

Regions

Kumo.io reads every region that is enabled in your account. It discovers them on each sync, so a region you enable later is picked up automatically. Regions you haven't enabled are skipped.

From the command line, kumo source sync --regions limits a sync to the regions you list.

Connect from the CLI

kumo source connect runs the same flow as Add a Source: it stores the credential, discovers the account, creates the source and starts the first sync. Pass the credential as one line of JSON on standard input so it never lands in your shell history.

With a role (the external ID is required, and is the one Kumo.io issued for your account, shown in Add a Source; Kumo.io refuses any other):

echo '{"roleArn":"arn:aws:iam::123456789012:role/KumoReadOnlyRole","externalId":"<your-external-id>"}' | \
  kumo source connect --provider aws --credential-kind aws.assume_role \
    --display-name "prod account" --secret-material-stdin

With an access key:

echo '{"accessKeyId":"AKIA…","secretAccessKey":"…"}' | \
  kumo source connect --provider aws --credential-kind aws.static_credentials \
    --display-name "prod account" --secret-material-stdin

Add --no-sync to skip the first sync, and --workspace to choose the workspace. Against kumo.io, --workspace must be a workspace ID. See the CLI reference for every flag.

If the connection fails

  • "The credential could not be verified." Test Connection lists the reasons below this message. The most common is a trust policy whose external ID doesn't match the one Kumo.io sent. Check the role's trust policy in IAM.
  • "The external ID doesn't match the one Kumo.io issued for this account." The role you entered uses an external ID Kumo.io didn't issue for this connection, so Kumo.io refuses it before calling AWS. Re-apply the template (or update the trust policy) with the external ID shown in Add a Source, then connect again.
  • "The role has no external ID." The role was sent without an external ID, for example from the CLI or the API, so Kumo.io refuses it before calling AWS. Deploy the Kumo.io read-only role template (its trust policy requires the external ID shown in Add a Source), and send that external ID with the role ARN: {"roleArn":"…","externalId":"<your-external-id>"}.
  • Discovery found no accounts. Kumo.io could authenticate but couldn't identify the account. Check that the role or key can call sts:GetCallerIdentity, which AWS allows by default.
  • Some resource types are missing after the sync. The credential works but can't read everything. The sync's diagnostics list what Kumo.io couldn't read. Compare your role's permissions with the read-only policy.

Next: connect every account in your organization, or read how Kumo.io stores your credentials.

The read-only policy

This is Kumo.io's published least-privilege policy, in full. Replace KUMO_CUR_BUCKET, or delete the KumoCostReportDiscovery and KumoCostAndUsageReportRead statements if you don't want cost data. Delete KumoUsageMetricsRead if you don't want Lambda cost estimates.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "KumoInventoryReadOnly",
      "Effect": "Allow",
      "Action": [
        "apigateway:GET",
        "autoscaling:DescribeAutoScalingGroups",
        "autoscaling:DescribeLaunchConfigurations",
        "cloudfront:ListCachePolicies",
        "cloudfront:ListCloudFrontOriginAccessIdentities",
        "cloudfront:ListDistributions",
        "cloudfront:ListFunctions",
        "cloudfront:ListOriginRequestPolicies",
        "dynamodb:DescribeContinuousBackups",
        "dynamodb:DescribeTable",
        "dynamodb:DescribeTimeToLive",
        "dynamodb:ListTables",
        "dynamodb:ListTagsOfResource",
        "ec2:DescribeAddresses",
        "ec2:DescribeAvailabilityZones",
        "ec2:DescribeCustomerGateways",
        "ec2:DescribeDhcpOptions",
        "ec2:DescribeInstances",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeLaunchTemplates",
        "ec2:DescribeNatGateways",
        "ec2:DescribeNetworkAcls",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DescribeRegions",
        "ec2:DescribeRouteTables",
        "ec2:DescribeSecurityGroupRules",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSnapshots",
        "ec2:DescribeSubnets",
        "ec2:DescribeTransitGateways",
        "ec2:DescribeVolumes",
        "ec2:DescribeVpcEndpoints",
        "ec2:DescribeVpcPeeringConnections",
        "ec2:DescribeVpcs",
        "ec2:DescribeVpnGateways",
        "ecr:DescribeImages",
        "ecr:DescribeRepositories",
        "ecr:GetLifecyclePolicy",
        "ecr:GetRepositoryPolicy",
        "ecr:ListTagsForResource",
        "ecs:DescribeClusters",
        "ecs:DescribeContainerInstances",
        "ecs:DescribeServices",
        "ecs:DescribeTaskDefinition",
        "ecs:DescribeTasks",
        "ecs:ListClusters",
        "ecs:ListContainerInstances",
        "ecs:ListServices",
        "ecs:ListTaskDefinitions",
        "ecs:ListTasks",
        "eks:DescribeCluster",
        "eks:DescribeFargateProfile",
        "eks:DescribeNodegroup",
        "eks:ListClusters",
        "eks:ListFargateProfiles",
        "eks:ListNodegroups",
        "elasticache:DescribeCacheClusters",
        "elasticache:DescribeCacheSubnetGroups",
        "elasticache:DescribeReplicationGroups",
        "elasticache:DescribeSnapshots",
        "elasticfilesystem:DescribeAccessPoints",
        "elasticfilesystem:DescribeBackupPolicy",
        "elasticfilesystem:DescribeFileSystems",
        "elasticfilesystem:DescribeMountTargetSecurityGroups",
        "elasticfilesystem:DescribeMountTargets",
        "elasticloadbalancing:DescribeListeners",
        "elasticloadbalancing:DescribeLoadBalancers",
        "elasticloadbalancing:DescribeRules",
        "elasticloadbalancing:DescribeTargetGroups",
        "elasticloadbalancing:DescribeTargetHealth",
        "events:DescribeEventBus",
        "events:ListApiDestinations",
        "events:ListArchives",
        "events:ListConnections",
        "events:ListEventBuses",
        "events:ListRules",
        "events:ListTargetsByRule",
        "firehose:DescribeDeliveryStream",
        "firehose:ListDeliveryStreams",
        "firehose:ListTagsForDeliveryStream",
        "iam:GetGroup",
        "iam:GetPolicyVersion",
        "iam:ListAttachedRolePolicies",
        "iam:ListEntitiesForPolicy",
        "iam:ListGroups",
        "iam:ListInstanceProfiles",
        "iam:ListPolicies",
        "iam:ListRoles",
        "iam:ListUsers",
        "kms:DescribeKey",
        "kms:GetKeyPolicy",
        "kms:ListAliases",
        "kms:ListGrants",
        "kms:ListKeys",
        "kms:ListResourceTags",
        "lambda:GetFunctionUrlConfig",
        "lambda:GetPolicy",
        "lambda:ListEventSourceMappings",
        "lambda:ListFunctions",
        "lambda:ListLayers",
        "lambda:ListTags",
        "logs:DescribeLogGroups",
        "rds:DescribeDBClusters",
        "rds:DescribeDBInstances",
        "rds:DescribeDBParameterGroups",
        "rds:DescribeDBSubnetGroups",
        "rds:DescribeOptionGroups",
        "route53:ListHostedZones",
        "route53:ListResourceRecordSets",
        "s3:GetBucketLocation",
        "s3:GetBucketLogging",
        "s3:GetBucketPolicy",
        "s3:GetBucketTagging",
        "s3:GetEncryptionConfiguration",
        "s3:ListAllMyBuckets",
        "sns:GetTopicAttributes",
        "sns:ListSubscriptionsByTopic",
        "sns:ListTagsForResource",
        "sns:ListTopics",
        "sqs:GetQueueAttributes",
        "sqs:ListQueueTags",
        "sqs:ListQueues",
        "states:DescribeActivity",
        "states:DescribeStateMachine",
        "states:ListActivities",
        "states:ListStateMachineAliases",
        "states:ListStateMachineVersions",
        "states:ListStateMachines",
        "states:ListTagsForResource",
        "wafv2:GetWebACL",
        "wafv2:ListIPSets",
        "wafv2:ListRegexPatternSets",
        "wafv2:ListRuleGroups",
        "wafv2:ListWebACLs"
      ],
      "Resource": "*"
    },
    {
      "Sid": "KumoOrganizationsDiscovery",
      "Effect": "Allow",
      "Action": [
        "organizations:DescribeOrganization",
        "organizations:ListAccountsForParent",
        "organizations:ListOrganizationalUnitsForParent",
        "organizations:ListRoots"
      ],
      "Resource": "*"
    },
    {
      "Sid": "KumoCostReportDiscovery",
      "Effect": "Allow",
      "Action": [
        "cur:DescribeReportDefinitions",
        "bcm-data-exports:ListExports",
        "bcm-data-exports:GetExport"
      ],
      "Resource": "*"
    },
    {
      "Sid": "KumoUsageMetricsRead",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:GetMetricData"
      ],
      "Resource": "*"
    },
    {
      "Sid": "KumoCostAndUsageReportRead",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::KUMO_CUR_BUCKET",
        "arn:aws:s3:::KUMO_CUR_BUCKET/*"
      ]
    }
  ]
}

See your own cloud in Kumo.io. Read-only access. Nothing in your account changes.

Free Trial