Skip to content

Cloud Account Configuration

Before running an inspection you need to configure credentials so VirtualSutra AI can read your cloud resources. All access is read-only - the platform never creates, modifies, or deletes any of your resources.


AWS

What you need

Field Description
Account ID 12-digit AWS account number (e.g. 123456789012)
Role ARN The IAM role in your account that VirtualSutra will assume
External ID On the hosted service, generated by the platform for your organization (see below). Self-hosted, a value you choose and enter on the connection
Regions One or more AWS regions to scan (e.g. us-east-1, eu-west-1)
Credentials Optional - access key / secret key, or omit to use the ambient role chain

Creating the inspection IAM role

Create a cross-account IAM role in your AWS account that trusts the VirtualSutra AI inspection account. Ask your administrator for the trusted account ID.

Step 1 - Find your External ID

The trust policy should include an sts:ExternalId condition. The External ID stops another organization from getting the platform to assume your role (the "confused deputy" problem).

  • Hosted service: the platform generates one External ID for your organization, a value starting with vsutra-. You cannot choose or change it. Find it under Settings > Connections > Cloud: click Add Cloud Connection and choose the AWS tab. Copy the value, or copy the whole trust-policy condition. You can also read it from the API: GET /orgs/{org_id}/aws-external-id returns {"external_id": "...", "enforced": true}. The platform presents this value every time it assumes a role for your organization: when it tests a connection and during every scan. Submitting a run, or creating or editing a schedule, whose configuration names any other external_id is refused, so leave the field out.
  • Self-hosted: choose your own value. Put it in the trust policy and in the External ID field of the connection. The platform presents it as entered, so this case is unchanged.

Step 2 - Create the role with a trust policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<VSUTRA_ACCOUNT_ID>:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<YOUR_EXTERNAL_ID>"
        }
      }
    }
  ]
}

Replace <YOUR_EXTERNAL_ID> with the value from Step 1. On the hosted service that is your organization's vsutra-... value, exactly as shown.

Step 3 - Attach managed policies

Policy ARN Purpose
arn:aws:iam::aws:policy/SecurityAudit IAM, CloudTrail, VPC, S3, certificates
arn:aws:iam::aws:policy/ReadOnlyAccess All remaining read APIs

Step 4 - Record the Role ARN

The Role ARN looks like: arn:aws:iam::123456789012:role/VirtualSutraInspection

Paste this into the Role ARN field in the inspection wizard.

Using the CLI

# Create the role (replace placeholders)
aws iam create-role \
  --role-name VirtualSutraInspection \
  --assume-role-policy-document file://trust-policy.json
# Attach policies
aws iam attach-role-policy \
  --role-name VirtualSutraInspection \
  --policy-arn arn:aws:iam::aws:policy/SecurityAudit
aws iam attach-role-policy \
  --role-name VirtualSutraInspection \
  --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess

Upgrading an existing role on the hosted service

The hosted service used to present the External ID set on each connection or run. It now always presents your organization's own value. If your role's trust policy names a different External ID, the role can no longer be assumed: connection tests and AWS scans fail with an access-denied error. A trust policy with no sts:ExternalId condition keeps working, but any organization could get the platform to assume that role, so add the condition. To fix either case:

  1. Copy your organization's External ID from Settings > Connections > Cloud (AWS tab).
  2. Set (or add) the role's sts:ExternalId condition with that value: aws iam update-assume-role-policy --role-name VirtualSutraInspection --policy-document file://trust-policy.json.
  3. Remove external_id from saved run configurations and schedules. Existing schedules keep running with the platform's value, but editing a schedule that still names a different external_id is refused until you remove the field.

Saving an AWS connection from the console removes any External ID stored on it. The platform ignores a stored value either way.

Self-hosted deployments are not affected.


Scanning an AWS Organization: the member role

To scan every account of an AWS Organization with one connection, VirtualSutra assumes your hub role and then a member role in each other account. The member role is a read-only role that a CloudFormation StackSet creates in all of them at once. It holds the AWS managed SecurityAudit policy and a handful of reads (the regions, EKS clusters and ECR repositories) and the three ECR actions a scan needs to pull an image; nothing that writes. Its trust policy lets only your hub role assume it, and only with your External ID.

  1. Download the template: virtualsutra-readonly-stackset.yaml.
  2. Create the StackSet in the management account, service-managed, with automatic deployment on so accounts added later get the role:
aws cloudformation create-stack-set --stack-set-name VirtualSutraReadOnly \
  --template-body file://virtualsutra-readonly-stackset.yaml --permission-model 'SERVICE_MANAGED' \
  --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \
  --capabilities 'CAPABILITY_NAMED_IAM' \
  --parameters ParameterKey=HubAccountId,ParameterValue=HUB_ACCOUNT_ID \
  ParameterKey=HubRoleName,ParameterValue=HUB_ROLE_NAME ParameterKey=ExternalId,ParameterValue=EXTERNAL_ID
aws cloudformation create-stack-instances --stack-set-name VirtualSutraReadOnly \
  --deployment-targets OrganizationalUnitIds=ORGANIZATION_ROOT_ID --regions us-east-1

The role is global, so one region is enough. The management account is skipped by a service-managed StackSet: it is scanned with the hub role itself when the hub role lives there. When the hub role is in a delegated account instead, deploy the same template once in the management account as an ordinary stack.

  1. The hub role needs to list the Organization and assume the member role. The Check result prints the exact policy and the command that attaches it (aws iam put-role-policy ... VirtualSutraDiscovery), together with SecurityAudit and the scan's own actions.

The commands the console shows when a member account lacks the role are these same commands with your names filled in. If the connection's Member role name is not VirtualSutraReadOnly, they add ParameterKey=RoleName,ParameterValue=<your name> so the template creates the role under that name; add it yourself when you run the commands above.

Scanning an EKS or GKE cluster: the read-only ClusterRole

A scan needs permission to list a few kinds of object inside a cluster, and nothing else. vsutra-readonly-clusterrole.yaml holds a ClusterRole that grants exactly those (no Secrets, no ConfigMaps, nothing that writes) and a binding.

  • EKS: create an access entry that maps the scan's IAM role to the group vsutra-readonly, then apply the file:
aws eks create-access-entry --cluster-name <CLUSTER_NAME> --principal-arn <ROLE_ARN> --kubernetes-groups vsutra-readonly
kubectl apply -f vsutra-readonly-clusterrole.yaml

The cluster's authentication mode must include the API (API or API_AND_CONFIG_MAP). If the cluster's public endpoint is restricted to some networks, add the scanner's address to them; a private-only endpoint cannot be scanned yet.

  • GKE: bind the same ClusterRole to the scanning service account instead: edit the binding's subject to the commented User entry in the file, with the service account's e-mail address.

Azure

What you need

Field Description
Subscription ID GUID of the Azure subscription to scan
Tenant ID Your Azure AD / Entra ID tenant GUID
Client ID The application (client) ID of the service principal
Client Secret A client secret created for the service principal
Regions Azure regions to include in the scan

Creating the service principal

Step 1 - Create a service principal

az ad sp create-for-rbac --name "vsutra-ai-inspector" --skip-assignment
# Save the appId (Client ID) and password (Client Secret) from the output

Step 2 - Assign required roles

SUBSCRIPTION="/subscriptions/<subscription-id>"
CLIENT_ID="<appId from step 1>"

az role assignment create --assignee "$CLIENT_ID" \
  --role "Reader" --scope "$SUBSCRIPTION"

az role assignment create --assignee "$CLIENT_ID" \
  --role "Security Reader" --scope "$SUBSCRIPTION"

az role assignment create --assignee "$CLIENT_ID" \
  --role "Billing Reader" --scope "$SUBSCRIPTION"

Step 3 - Find your Tenant ID

az account show --query tenantId -o tsv

Roles for connection discovery

To let Check list your subscriptions, AKS clusters and ACR repositories (see Connections), assign the principal these roles at each subscription (or at a management group above them):

SUBSCRIPTION="/subscriptions/<subscription-id>"
CLIENT_ID="<appId>"

# Subscriptions, AKS clusters and ACR registries
az role assignment create --assignee "$CLIENT_ID" --role "Reader" --scope "$SUBSCRIPTION"
# The cluster's certificate authority (listClusterUserCredential)
az role assignment create --assignee "$CLIENT_ID" --role "Azure Kubernetes Service Cluster User Role" --scope "$SUBSCRIPTION"
# Repositories and images of each registry
az role assignment create --assignee "$CLIENT_ID" --role "Container Registry Repository Catalog Lister" --scope "$SUBSCRIPTION"
az role assignment create --assignee "$CLIENT_ID" --role "Container Registry Repository Reader" --scope "$SUBSCRIPTION"

The AKS cluster must be integrated with Microsoft Entra. Inside the cluster, bind the read-only ClusterRole to the principal's object ID:

az ad sp show --id "$CLIENT_ID" --query id -o tsv   # the object ID

Check prints these commands with your values filled in, for each role it found missing, at subscription scope: for the subscription listing it prints Reader and Security Reader, then the AKS and registry roles above as they are found missing. When the principal sees no subscription at all, Check also prints a Reader assignment at a management group (with a placeholder for the management group id); in every other case it prints no management-group command. To have subscriptions added later followed, assign Reader at a management group (or the tenant root group) above them yourself:

az role assignment create --assignee "$CLIENT_ID" --role "Reader" \
  --scope /providers/Microsoft.Management/managementGroups/<management-group-id>

Registry roles. The two registry roles above, Container Registry Repository Catalog Lister and Repository Reader, are those of a registry with repository-scoped permissions (ABAC) enabled. A registry in the default mode needs AcrPull for the pull (see Azure ACR); we could not verify which role lets Check list the repositories of such a registry. If Check still reports it as refused, add the two roles anyway, or save the repository by hand.

What to enter in the wizard

  • Subscription ID - from az account show --query id -o tsv
  • Tenant ID - from step 3
  • Client ID - appId from the service principal
  • Client Secret - password from the service principal

GCP

What you need

Field Description
Project ID The GCP project ID to scan
Service Account Key The JSON key file for a service account in that project

The steps below set up one service account for one project. To scan many projects with one key, see One service account for many projects.

Creating the service account

Step 1 - Create a service account

PROJECT="my-gcp-project"
SA_NAME="vsutra-ai-inspector"

gcloud iam service-accounts create "$SA_NAME" \
  --project="$PROJECT" \
  --display-name="VirtualSutra AI Inspector"

SA_EMAIL="${SA_NAME}@${PROJECT}.iam.gserviceaccount.com"

Step 2 - Grant required roles

for ROLE in \
  roles/viewer \
  roles/iam.securityReviewer \
  roles/cloudasset.viewer \
  roles/logging.viewer \
  roles/monitoring.viewer; do
  gcloud projects add-iam-policy-binding "$PROJECT" \
    --member="serviceAccount:$SA_EMAIL" \
    --role="$ROLE" --condition=None
done

These roles cover the base checks. Some checks need an API enabled or another role as well, for example Organization Policy, Security Command Center and the privilege-escalation check; see The APIs and roles each GCP check needs.

Step 3 - Create and download the key

gcloud iam service-accounts keys create key.json \
  --iam-account="$SA_EMAIL"

What to enter in the console

Open Settings > Connections > Cloud, add a GCP connection and choose key.json as the Service account key (JSON). The same form opens from Add in the Run wizard's Cloud accounts section. Check tests the key and lists what it can read before anything is saved. See Connections Registry for the form and what follows it.

Keep the key secure

The JSON key is a long-lived credential. Store it in a secrets manager and rotate it at least every 90 days. Delete it from disk after uploading it to the console.

One service account for many projects

One service account can scan every project of an organisation (or of a folder). Create it once, in one project, and grant its roles above the projects instead of in each one. Discovery then lists the projects, GKE clusters and Artifact Registry repositories the account can read, and you choose what to scan from that list (see Connections Registry).

Step 1 - Create the service account in a home project

Pick one project to own the service account. It does not have to be a project you scan.

HOME_PROJECT="my-security-project"
SA_NAME="vsutra-ai-inspector"

gcloud iam service-accounts create "$SA_NAME" \
  --project="$HOME_PROJECT" \
  --display-name="VirtualSutra AI Inspector"

SA_EMAIL="${SA_NAME}@${HOME_PROJECT}.iam.gserviceaccount.com"

Step 2 - Grant the roles at organisation level

A role granted on the organisation applies to every project in it, including projects created later. Grant the five scan roles from the single-project steps above, plus the three roles discovery needs:

Role Permission discovery uses What it is for
roles/browser resourcemanager.projects.get Listing the projects the account can read
roles/container.clusterViewer container.clusters.list Listing GKE clusters in each project
roles/artifactregistry.reader artifactregistry.repositories.list, artifactregistry.dockerimages.list Listing Artifact Registry repositories and the images in each Docker repository. The same role lets a scan pull those images

These four permissions are the ones the connection test checks, and the three roles are the ones the console names when one is missing.

ORG_ID="123456789012"   # gcloud organizations list

for ROLE in \
  roles/viewer \
  roles/iam.securityReviewer \
  roles/cloudasset.viewer \
  roles/logging.viewer \
  roles/monitoring.viewer \
  roles/browser \
  roles/container.clusterViewer \
  roles/artifactregistry.reader; do
  gcloud organizations add-iam-policy-binding "$ORG_ID" \
    --member="serviceAccount:$SA_EMAIL" \
    --role="$ROLE" --condition=None
done

To cover one folder instead of the whole organisation, grant the same roles on the folder:

gcloud resource-manager folders add-iam-policy-binding "$FOLDER_ID" \
  --member="serviceAccount:$SA_EMAIL" \
  --role="$ROLE" --condition=None

Enter the organisation's numeric ID in the connection's Organisation ID (optional) field. With it, the grant command the console shows for a missing role is the organisation-level one. Without it, the command is per project. The console shows no folder-level command.

Step 3 - Enable the APIs

Discovery calls three Google APIs. A listing whose API is not enabled is reported under Could not list, with the command that enables it.

API Where to enable it
Cloud Resource Manager (cloudresourcemanager.googleapis.com) The home project, which is the project that owns the service account
Kubernetes Engine (container.googleapis.com) Each project whose GKE clusters you want listed
Artifact Registry (artifactregistry.googleapis.com) Each project whose repositories you want listed
gcloud services enable cloudresourcemanager.googleapis.com --project="$HOME_PROJECT"

# In each project that has clusters or repositories to list:
gcloud services enable container.googleapis.com artifactregistry.googleapis.com \
  --project="<project-id>"

A project where the Kubernetes Engine or Artifact Registry API is off is still listed as a project. Only its clusters or repositories are missing.

Step 4 - Create the key

gcloud iam service-accounts keys create key.json \
  --iam-account="$SA_EMAIL"

Upload key.json when you add the GCP connection. The warning above applies to this key too: it reaches every project the roles cover.

Alternative - grant per project

If you cannot grant at organisation or folder level, keep the single service account and grant the same eight roles in each project you want scanned:

for PROJECT in project-a project-b; do
  for ROLE in \
    roles/viewer \
    roles/iam.securityReviewer \
    roles/cloudasset.viewer \
    roles/logging.viewer \
    roles/monitoring.viewer \
    roles/browser \
    roles/container.clusterViewer \
    roles/artifactregistry.reader; do
    gcloud projects add-iam-policy-binding "$PROJECT" \
      --member="serviceAccount:$SA_EMAIL" \
      --role="$ROLE" --condition=None
  done
done

Discovery lists only the projects the account has been granted on. A project created later is not covered until you grant the roles there.

The APIs and roles each GCP check needs

The roles and APIs above are what most checks read with. A scan also makes calls that need more: an API enabled in the project, a role the five base roles do not name, or a role granted somewhere other than the project. Without them the check is not evaluated, and the scan says so.

A check that cannot be evaluated leaves a gap in the scan

Every check of a GCP scan runs, whatever the enable_scc, enable_asset_inventory, enable_gke_audit, enable_artifact_registry_scan, enable_cost_analysis and enable_policy_analyzer fields on the connection say: the scan does not read them. Only the Well-Architected pillars and AI workload security (below) are switched by their own fields. The scan's coverage record (below) lists the other five of those groups as run, and the asset-inventory label check (enable_asset_inventory) with its Well-Architected operational-excellence pillar: switched_off while that pillar's own field is off. It lists any Well-Architected pillar or AI workload security group as switched_off when its own field is off, so a switched_off line there is expected.

When a check cannot be evaluated because its API is not enabled or a role is missing, the findings the scan did collect are kept, one Cloud scan coverage record finding per account lists each check that did not run, what it needs and the command that grants it, and the account's task completes with a message of this shape (INCOMPLETE COVERAGE: ...). If no check at all could run, the task fails instead, and so does a scan whose live compliance enrichment gave no usable reply. This holds for AWS, Azure and GCP alike.

INCOMPLETE COVERAGE: gcp account "Production": 3 check(s) could not be evaluated — the task completed and the scan-coverage record says what each needs: permission: GCP_IAM_EFFECTIVE_PRIVILEGE_ESCALATION on 4 scope(s) (needs roles/iam.serviceAccountTokenCreator); service not enabled: GCP_ASSET_NO_ORGPOLICY_vmExternalIpAccess on 1 scope(s) (needs orgpolicy.googleapis.com), GCP_ASSET_NO_ORGPOLICY_requireShieldedVm on 1 scope(s) (needs orgpolicy.googleapis.com)

The account is recorded as a coverage gap and the run ends partial. The compliance controls that depend on the account read "Not assessed - coverage incomplete" (see Compliance). The message names each check once, with how many scopes it affected, what it needs, and carries no commands; it names at most 20 checks (the rest are counted as "and N more") and is cut at 600 characters (ending in …). The commands are in the Cloud scan coverage record finding (title Cloud scan coverage record, check id CKV_VSUTRA_CLOUD_SCAN_COVERAGE, informational): one step per distinct API or role with its command, at most 25 steps, and at most 100 check entries (the rest are counted). Grant what it names, then resume the run or start a new scan. A plain resume runs the cloud scan again for every cloud account of the run, not only the one with the gap.

How a disabled API is read. A check of a service that lists your resources (Compute Engine, Cloud Storage, Kubernetes Engine, Artifact Registry, Cloud SQL and the like) treats a disabled API as "there are none": it finds nothing and records no gap. A check of an audit or policy service (Resource Manager, IAM, Security Command Center, Organization Policy, Cloud Logging, Cloud Billing budgets, Cloud Asset, Cloud Monitoring) cannot be evaluated when its API is off. A permission refusal is a gap.

Commands. These are the commands the product prints, with the same placeholders. Replace them and run them yourself: the product never runs them.

To Command
Enable an API gcloud services enable API --project=PROJECT_ID
Grant a role on a project gcloud projects add-iam-policy-binding PROJECT_ID --member=serviceAccount:SERVICE_ACCOUNT_EMAIL --role=ROLE
Grant a role on the organisation gcloud organizations add-iam-policy-binding ORGANIZATION_ID --member=serviceAccount:SERVICE_ACCOUNT_EMAIL --role=ROLE
Grant a role on a billing account gcloud billing accounts add-iam-policy-binding BILLING_ACCOUNT_ID --member=serviceAccount:SERVICE_ACCOUNT_EMAIL --role=ROLE
Grant a role on a service account gcloud iam service-accounts add-iam-policy-binding TARGET_SERVICE_ACCOUNT_EMAIL --member=serviceAccount:SERVICE_ACCOUNT_EMAIL --role=ROLE --project=PROJECT_ID

SERVICE_ACCOUNT_EMAIL is the account whose key the connection holds. A role granted on the organisation applies to every project in it.

Checks that run on every scan

Check group What it looks at API to enable Role to grant Granted on
IAM policy Public bindings, primitive roles, admin roles, Owner on service accounts, external accounts in bindings cloudresourcemanager.googleapis.com roles/iam.securityReviewer Project
IAM service-account keys Keys older than 90 days iam.googleapis.com roles/iam.securityReviewer Project
Effective permissions: listing service accounts Which service accounts the project has iam.googleapis.com roles/iam.securityReviewer Project
Effective permissions: impersonation What each service account can do, asked as that account iamcredentials.googleapis.com, enabled on the project that owns the scanning service account (gcloud services enable iamcredentials.googleapis.com --project=SCANNING_SERVICE_ACCOUNT_PROJECT_ID) roles/iam.serviceAccountTokenCreator Each service account being checked
Network Firewall rules open to the internet, default network, subnets without flow logs compute.googleapis.com roles/viewer Project
Cloud Storage Public ACLs, uniform bucket-level access, versioning storage.googleapis.com roles/viewer Project
Cloud Storage Public IAM bindings on a bucket storage.googleapis.com roles/iam.securityReviewer Project
Security Command Center High and critical findings securitycenter.googleapis.com roles/securitycenter.findingsViewer Project, or the organisation when the connection sets an organisation ID
Security Command Center Whether it is enabled (CKV_VSUTRA_GCP_LOG_7) securitycenter.googleapis.com roles/securitycenter.sourcesViewer Project
Organization Policy Five effective constraints: external IPs on VMs, Shielded VMs, service-account key creation, uniform bucket-level access, default network creation orgpolicy.googleapis.com roles/orgpolicy.policyViewer Project
Kubernetes Engine Cluster configuration container.googleapis.com roles/container.clusterViewer Project
Artifact Registry Repository configuration, CMEK artifactregistry.googleapis.com roles/artifactregistry.reader Project
Artifact Registry Public access to a repository artifactregistry.googleapis.com roles/iam.securityReviewer Project
Cloud Billing budgets A budget exists for the billing account. Runs only when the connection has a billing account ID billingbudgets.googleapis.com roles/billing.viewer The billing account
Logging: audit logs Data access audit logs on sensitive services cloudresourcemanager.googleapis.com roles/iam.securityReviewer Project
Logging: sinks A log sink to Cloud Storage or BigQuery logging.googleapis.com roles/logging.viewer Project
Logging: flow logs and DNS VPC flow logs; Cloud DNS logging compute.googleapis.com; dns.googleapis.com roles/viewer Project
Backup Cloud SQL backups and HA; disk snapshots; Cloud Spanner; Filestore; bucket versioning sqladmin.googleapis.com; compute.googleapis.com; spanner.googleapis.com; file.googleapis.com; storage.googleapis.com roles/viewer Project
Encryption CMEK on buckets, BigQuery datasets, Pub/Sub topics, Secret Manager secrets storage.googleapis.com; bigquery.googleapis.com; pubsub.googleapis.com; secretmanager.googleapis.com roles/viewer Project

Checks you switch on

Check group Switch What it looks at API to enable Role to grant Granted on
AI workload security enable_ai_spm Vertex AI endpoints: CMEK, request logging aiplatform.googleapis.com roles/viewer Project
AI workload security enable_ai_spm Vertex AI endpoints open to everyone aiplatform.googleapis.com roles/iam.securityReviewer Project
AI workload security enable_ai_spm Endpoint quota limit serviceusage.googleapis.com roles/viewer Project
AI workload security enable_ai_spm Service accounts of endpoints with excessive permissions cloudresourcemanager.googleapis.com roles/iam.securityReviewer Project
Well-Architected: operational excellence enable_waf_ops_excellence Resource labelling cloudasset.googleapis.com roles/cloudasset.viewer Project
Well-Architected: operational excellence enable_waf_ops_excellence Alert policies monitoring.googleapis.com roles/monitoring.viewer Project
Well-Architected: operational excellence enable_waf_ops_excellence Deployment Manager deploymentmanager.googleapis.com roles/viewer Project
Well-Architected: reliability enable_waf_reliability Single-zone Cloud SQL; load balancer health checks; instance-group autoscaling sqladmin.googleapis.com; compute.googleapis.com roles/viewer Project
Well-Architected: performance enable_waf_performance Oversized instances; Memorystore compute.googleapis.com; redis.googleapis.com roles/viewer Project
Well-Architected: performance enable_waf_performance Instance utilisation metrics monitoring.googleapis.com roles/monitoring.viewer Project
Well-Architected: cost enable_waf_cost Stopped instances, committed use discounts, bucket lifecycle rules compute.googleapis.com; storage.googleapis.com roles/viewer Project
Well-Architected: sustainability enable_waf_sustainability Spot ratio, N1 instances compute.googleapis.com roles/viewer Project

enable_waf_all switches on all five pillars. The sustainability pillar's carbon-footprint check is not supported in this release: it needs nothing from you and is never reported as a gap.

Scanning the GKE clusters discovery lists

roles/container.clusterViewer lets discovery list clusters. It reads nothing inside one. To scan a discovered cluster with the account's key, grant roles/container.viewer on the cluster's project (read-only, and it carries every read a cluster scan makes), or bind a read-only ClusterRole inside the cluster. See Access a scan needs.

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member=serviceAccount:SERVICE_ACCOUNT_EMAIL --role=roles/container.viewer

Privilege-escalation check: roles/iam.serviceAccountTokenCreator

The effective-permission check for GCP asks Google what each service account of the project can do, and the only way to get that answer is to call as that account. The scan therefore mints a short-lived token as each service account, which needs two things:

  • the IAM Service Account Credentials API (iamcredentials.googleapis.com) enabled on the project that owns the scanning service account, which is not always a project you scan; and
  • roles/iam.serviceAccountTokenCreator on each service account to be checked, granted with the command in the table above.

Without them the check does not run quietly. A service account the scanning account may not impersonate is not evaluated, the others are still checked, the task completes, the run ends partial, and GCP_IAM_EFFECTIVE_PRIVILEGE_ESCALATION is named in the message and in the Cloud scan coverage record, which carries the role and the command that grants it. If the Credentials API is off, no service account of the project is checked and the message names iamcredentials.googleapis.com instead.

This role lets the scanning account act as the service account

Token Creator allows minting tokens as the account it is granted on. The product prints a per-account command and no broader one. Grant it only for service accounts whose permissions you want the scan to judge, and do not grant it at all if you accept the check being reported as not evaluated (the run ends partial and the controls it feeds read Not assessed).


Effective permissions (CIEM): additional permissions

CIEM findings run automatically as part of a cloud inspection, but each provider needs one extra permission beyond the base read-only policies above. Without it the check is not evaluated: the findings the scan did collect are kept, the account's task completes with the check named in its message and in the Cloud scan coverage record (which carries the role and the command that grants it), and the run ends partial. This is the same for AWS, Azure and GCP.

Provider Additional permission Notes
AWS iam:SimulatePrincipalPolicy Read-only API; already included in the SecurityAudit managed policy above
Azure Microsoft.Authorization/roleAssignments/read and Microsoft.Authorization/roleDefinitions/read Included in the built-in Reader role assigned above
GCP iam.serviceAccounts.getAccessToken Not included in any of the roles granted above. Grant roles/iam.serviceAccountTokenCreator on each service account to be checked, and enable iamcredentials.googleapis.com on the project that owns the scanning service account. See Privilege-escalation check

AI workload security (AI-SPM): additional permissions

AI-SPM scanning reads configuration for AI/ML services in the account. It uses the same base read-only permissions granted above (SecurityAudit + ReadOnlyAccess for AWS, Reader for Azure, roles/viewer for GCP); no additional IAM grants are required for AWS and Azure beyond what's already listed. For GCP, enable aiplatform.googleapis.com and serviceusage.googleapis.com; the roles are the base ones (see the table). Enable it per-account with the enable_ai_spm field on the cloud account connection.


Multiple accounts

You can add multiple cloud accounts in a single run - for example, all your AWS accounts across regions, or a mix of AWS and Azure. Each account is scanned independently and findings are tagged with their source account.


Container Registry Access

If you want to scan private container images, grant the VirtualSutra scanner read access to your registry. Public images (Docker Hub, GHCR public repos) require no credentials.

AWS ECR

Add the AmazonEC2ContainerRegistryReadOnly managed policy to the VirtualSutra inspection role, or add this inline policy statement for least-privilege access:

# Attach the AWS managed read-only ECR policy
aws iam attach-role-policy \
  --role-name VirtualSutraInspection \
  --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly

The scanner uses the same IAM role as the cloud inspector - no separate credentials needed for ECR images in the same account. For an image in another account of your Organization the scanner assumes that account's member role (see Scanning an AWS Organization), which carries the three actions a pull needs: ecr:GetAuthorizationToken, ecr:BatchGetImage and ecr:GetDownloadUrlForLayer. A scan that is refused names all three.

Azure ACR

Grant the service principal the AcrPull role on the container registry:

ACR_ID=$(az acr show --name <acr-name> --resource-group <rg> --query id -o tsv)

az role assignment create \
  --assignee "<client-id>" \
  --role "AcrPull" \
  --scope "$ACR_ID"

GCP Artifact Registry

Grant the service account roles/artifactregistry.reader on the project:

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:vsutra-ai-inspector@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/artifactregistry.reader"

Private registries (Docker Hub, GHCR, self-hosted)

For other private registries, provide credentials directly in the container image config:

Config field Description
credentials_ref Secret reference for the registry password (e.g. env://REGISTRY_PASSWORD)
username_ref Secret reference for the registry username (e.g. env://REGISTRY_USERNAME)

Using the setup script

The setup_cloud_resources.sh script automates IAM role/policy setup for ECR, ACR, and Artifact Registry:

# AWS ECR permissions
bash setup_cloud_resources.sh --cloud aws --account-id 123456789012
# Print credential setup instructions for all registry types
bash setup_cloud_resources.sh container

What the setup script does for GCP

setup_cloud_resources.sh gcp --project-id PROJECT_ID creates the service account and a key and grants a set of project roles (the five base roles, roles/container.clusterViewer, roles/container.viewer, roles/billing.viewer, roles/securitycenter.findingsViewer, roles/artifactregistry.reader and three roles for the Well-Architected pillars). With --org-id it prints, and does not run, the organisation-level grants. It does not enable any API, does not grant roles/orgpolicy.policyViewer on the project, roles/securitycenter.sourcesViewer on the project, roles/browser or roles/iam.serviceAccountTokenCreator, and grants roles/billing.viewer on the project, where a budget check needs it on the billing account. Run the commands in The APIs and roles each GCP check needs for what it leaves out.


Next step

Once your cloud accounts are configured, go back to Running an Inspection and add them to the wizard.