AWS Cross Account Assume Role

A cross-account assume role lets Nirmata create and manage EKS clusters in your AWS account without long-lived access keys. You create an IAM role in your AWS account (the target account), trust the Nirmata principal to assume it, and attach the permissions Nirmata needs to provision the cluster. Nirmata then calls sts:AssumeRole on this role to get short-lived credentials whenever it acts on the cluster.

The same role can also be used as the EKS cluster service role, so a single role covers both the Nirmata control-plane operations and the EKS service.

How role-based authentication works

AWS role-based authentication has two parts, and both must allow an action for it to succeed:

  • Trust policy (on the role): defines who may assume the role. A principal that is not listed in the trust policy cannot get credentials for the role, even if it has broad permissions in its own account.
  • Permission policy (on the role): defines what the temporary credentials can do after the role is assumed.

When a principal assumes a role, AWS Security Token Service (STS) checks the trust policy and returns temporary credentials (an access key, secret key, and session token) that expire after the session duration, one hour by default. Nirmata uses these credentials for every AWS API call and requests new ones when they expire. No long-lived keys are stored in Nirmata.

Within a single target account

Nirmata service ──sts:AssumeRole + External ID──► Cross-account role (target account)
                                                    │
                                                    ├─ EC2 / EKS / IAM API calls in the target account
                                                    └─ iam:PassRole ──► EKS cluster role, node IAM role
  1. Nirmata calls sts:AssumeRole on the cross-account role and passes the External ID from your cloud credential.
  2. STS checks the role’s trust policy: the caller must be the Nirmata principal and the External ID must match.
  3. Nirmata uses the temporary credentials to create the EKS cluster, node groups, and add-ons in the target account.
  4. Nirmata passes (iam:PassRole) the EKS cluster role to EKS and the node IAM role to EC2. The EKS service and the worker nodes then assume those roles themselves. They do not use the Nirmata credentials.

Across accounts (hub and spoke)

If you manage clusters in several AWS accounts, you can register one cloud credential that points to a role in a central hub account. For each cluster, you then set the Target Account Role ARN in the cluster type to a role in a spoke account:

Nirmata service ──AssumeRole + External ID──► Hub role (hub account)
                                                │
                                                └─AssumeRole──► Spoke role (target account) ──► EKS cluster in the spoke account
  1. Nirmata assumes the hub role, as described above.
  2. Using the hub role’s credentials, Nirmata assumes the spoke role. This step is allowed only if the hub role’s permission policy grants sts:AssumeRole on the spoke role, and the spoke role’s trust policy lists the hub role as a principal.
  3. Nirmata creates the cluster in the spoke account with the spoke role’s credentials. The EKS cluster role and node IAM role must be in the spoke account, because EKS does not accept roles from another account.

See Configure a hub and spoke setup for the policies.

Roles used in this setup

Role Account Purpose Trusted principal Permissions
Cross-account role (this page) Target account Assumed by Nirmata to create and manage EKS clusters. Also used as the EKS cluster role, unless you create a separate one. Nirmata principal (with External ID), eks.amazonaws.com, pods.eks.amazonaws.com AmazonEKSClusterPolicy and the Nirmata permission policy in Step 2
Node IAM role Target account Assumed by the EC2 worker nodes so that the kubelet and VPC CNI can call AWS APIs and pull images from Amazon ECR. ec2.amazonaws.com AmazonEKSWorkerNodePolicy, AmazonEKS_CNI_Policy, AmazonEC2ContainerRegistryReadOnly
Hub role (optional) Hub account Assumed by Nirmata, then used to assume the spoke role. Nirmata principal (with External ID) sts:AssumeRole and sts:TagSession on the spoke roles

Prerequisites

  • Administrator access (or permission to create IAM roles and policies) in the target AWS account.
  • The Nirmata Account ID and External ID. These are displayed on the Settings tab when you add an AWS cloud credential in Nirmata (see AWS Cloud Credentials ).
  • The AWS CLI configured for the target account, if you use the CLI steps below.

In the examples below, replace the placeholders as follows:

Placeholder Value
<TARGET_ACCOUNT_ID> Your AWS account ID where the cluster is created
<NIRMATA_ACCOUNT_ID> Nirmata Account ID shown in the Nirmata cloud credential screen
<NIRMATA_PRINCIPAL> The Nirmata principal ARN, for example arn:aws:iam::<NIRMATA_ACCOUNT_ID>:root or the specific user/role ARN provided by Nirmata
<EXTERNAL_ID> External ID shown in the Nirmata cloud credential screen
<ROLE_NAME> Name of the cross-account role you create in the target account, for example AmazonEKSClusterRole. Nirmata assumes this role to create and manage EKS clusters, and EKS uses it as the cluster service role. It needs the trust policy from Step 1, plus AmazonEKSClusterPolicy and the permission policy from Step 2.

Step 1: Create the trust policy for the cross-account role

By default, no principal outside your AWS account can use a role in your account. The trust policy is what allows the Nirmata service, which runs in a different AWS account, to assume the cross-account role and get temporary credentials for your account. Without it, the sts:AssumeRole call from Nirmata is denied and the cloud credential fails validation.

The trust policy controls who can assume the role. Each statement has a specific purpose:

Statement Trusted principal Why it is needed
AllowNirmataCrossAccountAssume The Nirmata principal Lets Nirmata assume the role across accounts to create and manage your clusters. The sts:ExternalId condition ensures that only requests made for your Nirmata cloud credential are accepted.
AllowEKSService eks.amazonaws.com Lets the EKS service assume the role when it is used as the EKS cluster role. EKS uses it to manage the control plane’s network interfaces, load balancers, and other resources in your account. Remove this statement if you use a separate EKS cluster role.
AllowEKSPodIdentity pods.eks.amazonaws.com Lets EKS Pod Identity assume the role and tag sessions for workloads in the cluster. Remove this statement if you do not use EKS Pod Identity with this role.

Save the following as trust-policy.json:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowNirmataCrossAccountAssume",
            "Effect": "Allow",
            "Principal": {
                "AWS": "<NIRMATA_PRINCIPAL>"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:ExternalId": "<EXTERNAL_ID>"
                }
            }
        },
        {
            "Sid": "AllowEKSService",
            "Effect": "Allow",
            "Principal": {
                "Service": "eks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Sid": "AllowEKSPodIdentity",
            "Effect": "Allow",
            "Principal": {
                "Service": "pods.eks.amazonaws.com"
            },
            "Action": [
                "sts:AssumeRole",
                "sts:TagSession"
            ]
        }
    ]
}

NOTE:

  • Always keep the sts:ExternalId condition. It prevents another party who knows your role ARN from using Nirmata to access your account (the “confused deputy” problem).
  • The External ID changes each time you click +Add Cloud Credentials. Do not close the Nirmata session until the role is created and validated.

Step 2: Create the permission policy for the cross-account role

This permission policy is attached to the cross-account role (<ROLE_NAME>) from Step 1. It controls what Nirmata can do in the target account after it assumes the role. It is not used by the node IAM role. Save the following as nirmata-cluster-policy.json:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EC2Permissions",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeKeyPairs",
                "ec2:DescribeLaunchTemplates",
                "ec2:DescribeLaunchTemplateVersions",
                "ec2:DescribeVpcs",
                "ec2:DescribeSubnets",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeInstanceTypes",
                "ec2:DescribeImages",
                "ec2:DescribeAvailabilityZones",
                "ec2:DescribeAccountAttributes",
                "ec2:DescribeRegions",
                "ec2:DescribeInstances",
                "ec2:CreateTags",
                "ec2:DeleteTags",
                "ec2:CreateLaunchTemplate",
                "ec2:DeleteLaunchTemplate",
                "ec2:RunInstances"
            ],
            "Resource": "*"
        },
        {
            "Sid": "EKSPermissions",
            "Effect": "Allow",
            "Action": [
                "eks:CreateCluster",
                "eks:DeleteCluster",
                "eks:DescribeCluster",
                "eks:ListClusters",
                "eks:UpdateClusterConfig",
                "eks:UpdateClusterVersion",
                "eks:TagResource",
                "eks:UntagResource",
                "eks:CreateNodegroup",
                "eks:DeleteNodegroup",
                "eks:DescribeNodegroup",
                "eks:ListNodegroups",
                "eks:UpdateNodegroupConfig",
                "eks:UpdateNodegroupVersion",
                "eks:CreateFargateProfile",
                "eks:DeleteFargateProfile",
                "eks:DescribeFargateProfile",
                "eks:ListFargateProfiles",
                "eks:CreateAddon",
                "eks:DeleteAddon",
                "eks:DescribeAddon",
                "eks:ListAddons",
                "eks:UpdateAddon",
                "eks:DescribeAddonVersions"
            ],
            "Resource": "*"
        },
        {
            "Sid": "CloudFormationNodePools",
            "Effect": "Allow",
            "Action": [
                "cloudformation:CreateStack",
                "cloudformation:DeleteStack",
                "cloudformation:CreateChangeSet",
                "cloudformation:UpdateStack",
                "cloudformation:ExecuteChangeSet",
                "cloudformation:Describe*",
                "cloudformation:EstimateTemplateCost",
                "cloudformation:Get*",
                "cloudformation:List*",
                "cloudformation:ValidateTemplate",
                "cloudformation:DetectStackDrift",
                "cloudformation:DetectStackResourceDrift"
            ],
            "Resource": "*"
        },
        {
            "Sid": "IAMRoleAndOIDCManagement",
            "Effect": "Allow",
            "Action": [
                "iam:GetRole",
                "iam:GetRolePolicy",
                "iam:GetPolicy",
                "iam:GetPolicyVersion",
                "iam:ListAttachedRolePolicies",
                "iam:ListRolePolicies",
                "iam:ListPolicies",
                "iam:CreatePolicy",
                "iam:CreateRole",
                "iam:AttachRolePolicy",
                "iam:PutRolePolicy",
                "iam:DetachRolePolicy",
                "iam:DeleteRolePolicy",
                "iam:DeleteRole",
                "iam:CreateOpenIDConnectProvider",
                "iam:DeleteOpenIDConnectProvider",
                "iam:GetOpenIDConnectProvider",
                "iam:ListOpenIDConnectProviders"
            ],
            "Resource": "*"
        },
        {
            "Sid": "ListEntitiesForEKSManagedPolicies",
            "Effect": "Allow",
            "Action": "iam:ListEntitiesForPolicy",
            "Resource": [
                "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
                "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",
                "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly",
                "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
            ]
        },
        {
            "Sid": "PassClusterRoleToEKS",
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::<TARGET_ACCOUNT_ID>:role/<ROLE_NAME>",
            "Condition": {
                "StringEquals": {
                    "iam:PassedToService": "eks.amazonaws.com"
                }
            }
        },
        {
            "Sid": "PassNodeRoleToEC2AndEKS",
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::<TARGET_ACCOUNT_ID>:role/*",
            "Condition": {
                "StringEquals": {
                    "iam:PassedToService": [
                        "ec2.amazonaws.com",
                        "eks.amazonaws.com"
                    ]
                }
            }
        }
    ]
}

The policy grants the following access:

Statement Purpose
EC2Permissions Discover VPCs, subnets, security groups, AMIs, and instance types, and launch worker nodes through launch templates.
EKSPermissions Create, update, upgrade, and delete EKS clusters, node groups, Fargate profiles, and add-ons.
CloudFormationNodePools Create, update, and delete the CloudFormation stacks for node pools that use Using CloudFormation Template.
IAMRoleAndOIDCManagement Create node roles and the cluster OIDC provider (for IAM Roles for Service Accounts).
ListEntitiesForEKSManagedPolicies Check which roles already have the AWS-managed EKS policies attached.
PassClusterRoleToEKS Pass this role to EKS as the cluster service role.
PassNodeRoleToEC2AndEKS Pass node roles to EC2 and EKS when creating node groups. To tighten access, replace role/* with the specific node role ARNs.

NOTE:

If you use Using CloudFormation Template for node pools, CloudFormation creates the stack resources with the cross-account role’s credentials. Add permissions for every resource type in your template (for example, autoscaling:* for Auto Scaling groups) to this policy. If the template is loaded from an Amazon S3 URL, also add s3:GetObject on the template object. Remove the CloudFormationNodePools statement if you do not use CloudFormation templates.

Step 3: Create the role

Using the AWS CLI
# Create the role with the trust policy
aws iam create-role \
  --role-name <ROLE_NAME> \
  --assume-role-policy-document file://trust-policy.json \
  --description "Cross-account role assumed by Nirmata to manage EKS clusters"

# Attach the AWS-managed EKS cluster policy (required for the EKS service role)
aws iam attach-role-policy \
  --role-name <ROLE_NAME> \
  --policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy

# Add the Nirmata permission policy
aws iam put-role-policy \
  --role-name <ROLE_NAME> \
  --policy-name NirmataClusterServicePermissions \
  --policy-document file://nirmata-cluster-policy.json

# Get the role ARN to provide to Nirmata
aws iam get-role --role-name <ROLE_NAME> --query Role.Arn --output text
Using the AWS Console
  1. Log in to the target AWS account and open IAM.
  2. Click Roles > Create role.
  3. Select Custom trust policy, paste the contents of trust-policy.json, and click Next.
  4. Search for and select AmazonEKSClusterPolicy, then click Next.
  5. Enter the Role name (for example, AmazonEKSClusterRole) and an optional description, then click Create role.
  6. Open the new role, click Add permissions > Create inline policy.
  7. Select JSON, paste the contents of nirmata-cluster-policy.json, and click Next.
  8. Enter the policy name NirmataClusterServicePermissions and click Create policy.
  9. Copy the ARN from the role summary. It has the format arn:aws:iam::<TARGET_ACCOUNT_ID>:role/<ROLE_NAME>.

Step 4: Add the role to Nirmata

  1. In Nirmata, return to the Add Cloud Credentials screen where you copied the External ID.
  2. On the Settings tab, enter the role ARN in Cluster Role ARN.
  3. Click Next to go to the Validate tab.
  4. Click Finish when the validation shows Success.

(Optional) Configure a hub and spoke setup

Use this setup to manage clusters in several AWS accounts with a single Nirmata cloud credential. Nirmata assumes a role in a central hub account, and then the hub role assumes a role in each spoke (target) account where clusters are created. See How role-based authentication works for the flow.

  1. Spoke account: Create the cross-account role as described in Steps 1–3. In the trust policy, replace the AllowNirmataCrossAccountAssume statement with the following, which trusts the hub role instead of the Nirmata principal. This lets only the hub role assume the spoke role:

    {
        "Sid": "AllowHubRoleAssume",
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::<HUB_ACCOUNT_ID>:role/<HUB_ROLE_NAME>"
        },
        "Action": [
            "sts:AssumeRole",
            "sts:TagSession"
        ]
    }
    
  2. Hub account: Create the hub role with the trust policy from Step 1 (the AllowNirmataCrossAccountAssume statement with your External ID). Attach the following inline permission policy, which lets the hub role assume the spoke roles. List every spoke role ARN:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "AssumeSpokeRoles",
                "Effect": "Allow",
                "Action": [
                    "sts:AssumeRole",
                    "sts:TagSession"
                ],
                "Resource": [
                    "arn:aws:iam::<SPOKE_ACCOUNT_ID>:role/<SPOKE_ROLE_NAME>"
                ]
            }
        ]
    }
    
  3. Nirmata: Add the hub role ARN as the Cluster Role ARN of the cloud credential. Then, in the EKS cluster type, enter the spoke role ARN in Target Account Role ARN. See Cross Account EKS Cluster Creation .

NOTE:

  • Chained role sessions are limited to a maximum duration of one hour, regardless of the role’s Maximum session duration setting.
  • The EKS cluster role and node IAM role must be in the spoke account.

Verify the configuration

To confirm the role can be assumed and has the expected access, run the following from an identity that is trusted by the role:

aws sts assume-role \
  --role-arn arn:aws:iam::<TARGET_ACCOUNT_ID>:role/<ROLE_NAME> \
  --role-session-name nirmata-test \
  --external-id <EXTERNAL_ID>

Export the returned credentials and run aws eks list-clusters --region <REGION> to check EKS access.

Troubleshooting

Error Cause and resolution
AccessDenied ... is not authorized to perform: sts:AssumeRole The trust policy does not include the Nirmata principal, or the External ID does not match. Verify both values against the Nirmata cloud credential screen.
iam:PassRole access denied during cluster creation The role ARN in PassClusterRoleToEKS, or the node role ARN in PassNodeRoleToEC2AndEKS, does not match the role being passed.
Cluster creation fails with Role is not authorized to perform eks:... from the EKS service AmazonEKSClusterPolicy is not attached, or eks.amazonaws.com is missing from the trust policy.

Next Step: Create an EKS cluster in the target account .