Create AWS IAM roles

GKE on AWS uses AWS IAM roles to create and manage your clusters and node pools. This page describes how to create the following default roles:

GKE Multi-Cloud API service agent role
The GKE Multi-Cloud API uses this AWS IAM role to manage resources using AWS APIs. This role is used by a Google-managed service account known as a service agent.
Control plane AWS IAM role
Your cluster control plane uses this role to control node pools.
Node pool AWS IAM role
The control plane uses this role to create node pool VMs.

This page shows you how to create roles for the following situations:

  • A default set of permissions, useful for testing
  • Permissions to operate on resources with certain AWS tags

You can choose one of these options to meet your organization's security policies. For more information on tags, see Tagging AWS resources.

Optionally, you can scope permissions for the AWS IAM roles GKE on AWS uses to meet your organization's requirements. For more information, see AWS IAM roles.

Before you begin

If you choose to create permissions that operate on resources with certain tags, you must choose a tag and a value. You then use these values to tag the resources within your cluster to restrict access to them.

Create GKE Multi-Cloud API service agent role

To create the GKE Multi-Cloud API service agent role, use the following command to retrieve your Google Cloud project number and use it to create the role.

PROJECT_ID="$(gcloud config get-value project)"
PROJECT_NUMBER=$(gcloud projects describe "$PROJECT_ID" \
    --format "value(projectNumber)")

aws iam create-role --role-name API_ROLE \
    --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
        {
        "Sid": "",
        "Effect": "Allow",
        "Principal": {
            "Federated": "accounts.google.com"
        },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
            "StringEquals": {
            "accounts.google.com:sub": "service-'$PROJECT_NUMBER'@gcp-sa-gkemulticloud.iam.gserviceaccount.com"
            }
      }
    }
  ]
}'

Replace API_ROLE with a name for this role.

Save the Amazon resource name (ARN) generated by this command for later use.

Create scoped permissions

Choose below if you would like to create a policy for the GKE Multi-Cloud API service agent role with default permissions or scoped to resources with certain tags. Then, run the following command:

Default

aws iam create-policy --policy-name API_POLICY \
  --policy-document '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": [
        "ec2:AuthorizeSecurityGroupEgress",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:CreateLaunchTemplate",
        "ec2:CreateNetworkInterface",
        "ec2:CreateSecurityGroup",
        "ec2:CreateTags",
        "ec2:CreateVolume",
        "ec2:DeleteLaunchTemplate",
        "ec2:DeleteNetworkInterface",
        "ec2:DeleteSecurityGroup",
        "ec2:DeleteTags",
        "ec2:DeleteVolume",
        "ec2:DescribeAccountAttributes",
        "ec2:DescribeInstances",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeKeyPairs",
        "ec2:DescribeLaunchTemplates",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DescribeSecurityGroupRules",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeVpcs",
        "ec2:GetConsoleOutput",
        "ec2:ModifyInstanceAttribute",
        "ec2:ModifyNetworkInterfaceAttribute",
        "ec2:RevokeSecurityGroupEgress",
        "ec2:RevokeSecurityGroupIngress",
        "ec2:RunInstances",
        "iam:AWSServiceName",
        "iam:CreateServiceLinkedRole",
        "iam:GetInstanceProfile",
        "iam:PassRole",
        "autoscaling:CreateAutoScalingGroup",
        "autoscaling:CreateOrUpdateTags",
        "autoscaling:DeleteAutoScalingGroup",
        "autoscaling:DeleteTags",
        "autoscaling:DescribeAutoScalingGroups",
        "autoscaling:DisableMetricsCollection",
        "autoscaling:EnableMetricsCollection",
        "autoscaling:TerminateInstanceInAutoScalingGroup",
        "autoscaling:UpdateAutoScalingGroup",
        "elasticloadbalancing:AddTags",
        "elasticloadbalancing:CreateListener",
        "elasticloadbalancing:CreateLoadBalancer",
        "elasticloadbalancing:CreateTargetGroup",
        "elasticloadbalancing:DeleteListener",
        "elasticloadbalancing:DeleteLoadBalancer",
        "elasticloadbalancing:DeleteTargetGroup",
        "elasticloadbalancing:DescribeListeners",
        "elasticloadbalancing:DescribeLoadBalancers",
        "elasticloadbalancing:DescribeTargetGroups",
        "elasticloadbalancing:DescribeTargetHealth",
        "elasticloadbalancing:ModifyTargetGroupAttributes",
        "elasticloadbalancing:RemoveTags",
        "kms:DescribeKey",
        "kms:Encrypt",
        "kms:GenerateDataKeyWithoutPlaintext"
      ],
      "Resource": "*"
    }
  ]
}'

Replace API_POLICY with a name for the GKE Multi-Cloud API AWS IAM policy.

Optionally, you can restrict the resources this policy applies to a certain account by setting the value of Resource to arn:aws:iam::ACCOUNT_ID:* and replace ACCOUNT_ID with your AWS account ID.

Scoped to tags

aws iam create-policy --policy-name API_POLICY_ec2 \
  --policy-document '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeAccountAttributes",
        "ec2:DescribeInstances",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeKeyPairs",
        "ec2:DescribeLaunchTemplates",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSecurityGroupRules",
        "ec2:DescribeSubnets",
        "ec2:DescribeVpcs",
        "ec2:GetConsoleOutput"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:CreateSecurityGroup"],
      "Resource": ["arn:aws:ec2:*:*:security-group/*"],
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:CreateSecurityGroup"],
      "Resource": ["arn:aws:ec2:*:*:vpc/*"]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:AuthorizeSecurityGroupEgress",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:DeleteSecurityGroup",
        "ec2:RevokeSecurityGroupEgress",
        "ec2:RevokeSecurityGroupIngress"
      ],
      "Resource": ["arn:aws:ec2:*:*:security-group/*"],
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:AuthorizeSecurityGroupEgress",
        "ec2:AuthorizeSecurityGroupIngress"
      ],
      "Resource": ["arn:aws:ec2:*:*:security-group-rule/*"],
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:CreateLaunchTemplate"],
      "Resource": ["arn:aws:ec2:*:*:launch-template/*"],
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DeleteLaunchTemplate"
      ],
      "Resource": ["arn:aws:ec2:*:*:launch-template/*"],
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:RunInstances"],
      "Resource": ["arn:aws:ec2:*:*:image/ami-*"],
      "Condition": {
        "StringEquals": {
          "ec2:Owner": ["099720109477", "amazon"]
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:RunInstances"],
      "Resource": [
        "arn:aws:ec2:*:*:subnet/*"
      ],
      "Condition": {
        "ArnLike": {
          "ec2:LaunchTemplate": "arn:aws:ec2:*:*:launch-template/*"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:RunInstances"],
      "Resource": [
        "arn:aws:ec2:*:*:instance/*",
        "arn:aws:ec2:*:*:key-pair/*",
        "arn:aws:ec2:*:*:launch-template/*",
        "arn:aws:ec2:*:*:network-interface/*",
        "arn:aws:ec2:*:*:security-group/*",
        "arn:aws:ec2:*:*:volume/*"
      ],
      "Condition": {
        "Bool": {
          "ec2:IsLaunchTemplateResource": "true"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateVolume"
      ],
      "Resource": ["arn:aws:ec2:*:*:volume/*"],
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DeleteVolume"
      ],
      "Resource": ["arn:aws:ec2:*:*:volume/*"],
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface"
      ],
      "Resource": ["arn:aws:ec2:*:*:network-interface/*"],
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:CreateNetworkInterface"],
      "Resource": ["arn:aws:ec2:*:*:security-group/*"],
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:CreateNetworkInterface"],
      "Resource": ["arn:aws:ec2:*:*:subnet/*"]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DeleteNetworkInterface",
        "ec2:ModifyNetworkInterfaceAttribute"
      ],
      "Resource": [
        "arn:aws:ec2:*:*:network-interface/*",
        "arn:aws:ec2:*:*:security-group/*"
      ],
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Action": [
        "ec2:CreateTags"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:ec2:*:*:launch-template/*",
        "arn:aws:ec2:*:*:network-interface/*",
        "arn:aws:ec2:*:*:security-group/*",
        "arn:aws:ec2:*:*:security-group-rule/*",
        "arn:aws:ec2:*:*:volume/*"
      ],
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Action": [
        "ec2:CreateTags"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:ec2:*:*:instance/*"
      ],
      "Condition": {
         "StringEquals": {
          "ec2:CreateAction" : "RunInstances"
        }
      }
    },
    {
      "Action": [
        "ec2:CreateTags",
        "ec2:DeleteTags"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:ec2:*:*:instance/*",
        "arn:aws:ec2:*:*:launch-template/*",
        "arn:aws:ec2:*:*:network-interface/*",
        "arn:aws:ec2:*:*:security-group/*",
        "arn:aws:ec2:*:*:security-group-rule/*",
        "arn:aws:ec2:*:*:volume/*"
      ],
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:ModifyInstanceAttribute"
      ],
      "Resource": [
        "arn:aws:ec2:*:*:instance/*",
        "arn:aws:ec2:*:*:security-group/*"
      ],
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
        }
      }
    }
  ]
}'

Replace the following:

  • API_POLICY: a prefix for GKE Multi-Cloud API AWS IAM policies.
  • ACCESS_CONTROL_TAG_KEY: the tag key that applies to this policy
  • ACCESS_CONTROL_TAG_VALUE: the tag value that applies to this policy

Optionally, you can further restrict this policy to an AWS region and account ID. To restrict the policy to a region and account ID, replace values of Resource such as arn:aws:ec2:*:*:security-group/* with arn:aws:ec2:AWS_REGION:ACCOUNT_ID:security-group/*.

Copy the ARN created for this policy for use in the following command.

Create additional policies for using tags

If you are restricting access with tags, follow these steps to create additional policies. If you are using the default policy, skip to Attach policies to the GKE Multi-Cloud API role.

  1. Create a policy to control access to AWS IAM with the following command:

    aws iam create-policy --policy-name API_POLICY_iam \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["iam:CreateServiceLinkedRole"],
          "Resource": [
            "arn:aws:iam::*:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling"
          ],
          "Condition": {
            "StringEquals": {
              "iam:AWSServiceName": "autoscaling.amazonaws.com"
            }
          }
        },
        {
          "Effect": "Allow",
          "Action": ["iam:CreateServiceLinkedRole"],
          "Resource": [
            "arn:aws:iam::*:role/aws-service-role/elasticloadbalancing.amazonaws.com/AWSServiceRoleForElasticLoadBalancing"
          ],
          "Condition": {
            "StringEquals": {
              "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"
            }
          }
        },
        {
          "Effect": "Allow",
          "Action": ["iam:PassRole"],
          "Resource": ["arn:aws:iam::*:role/*"],
          "Condition": {
            "StringEquals": {
              "iam:PassedToService": "ec2.amazonaws.com"
            }
          }
        }
        ,
        {
          "Effect": "Allow",
          "Action": ["iam:GetInstanceProfile"],
          "Resource": ["arn:aws:iam::*:instance-profile/*"]
        }
      ]
    }'
    
  2. Create a policy to control access to AWS EC2 Auto Scaling resources with the following command:

    aws iam create-policy --policy-name API_POLICY_autoscaling \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["autoscaling:DescribeAutoScalingGroups"],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "autoscaling:CreateAutoScalingGroup",
            "autoscaling:CreateOrUpdateTags"
          ],
          "Resource": [
            "arn:aws:autoscaling:*:*:autoScalingGroup:*:autoScalingGroupName/gke-*"
          ],
          "Condition": {
            "StringEquals": {
              "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
            }
          }
        },
        {
          "Effect": "Allow",
          "Action": [
            "autoscaling:CreateOrUpdateTags",
            "autoscaling:DeleteAutoScalingGroup",
            "autoscaling:DeleteTags",
            "autoscaling:DisableMetricsCollection",
            "autoscaling:EnableMetricsCollection",
            "autoscaling:TerminateInstanceInAutoScalingGroup",
            "autoscaling:UpdateAutoScalingGroup"
          ],
          "Resource": [
            "arn:aws:autoscaling:*:*:autoScalingGroup:*:autoScalingGroupName/gke-*"
          ],
          "Condition": {
            "StringEquals": {
              "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
            }
          }
        }
      ]
    }'
    

    Save the ARN generated by this command for later use.

  3. Create a policy to control access to AWS Elastic Load Balancer resources.

    aws iam create-policy --policy-name API_POLICY_elasticloadbalancing \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:DescribeListeners",
            "elasticloadbalancing:DescribeLoadBalancers",
            "elasticloadbalancing:DescribeTargetGroups",
            "elasticloadbalancing:DescribeTargetHealth"
          ],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:CreateTargetGroup",
            "elasticloadbalancing:AddTags"
          ],
          "Resource": ["arn:aws:elasticloadbalancing:*:*:targetgroup/gke-*"],
          "Condition": {
            "StringEquals": {
              "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
            }
          }
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:AddTags",
            "elasticloadbalancing:DeleteTargetGroup",
            "elasticloadbalancing:ModifyTargetGroupAttributes",
            "elasticloadbalancing:RemoveTags"
          ],
          "Resource": ["arn:aws:elasticloadbalancing:*:*:targetgroup/gke-*"],
          "Condition": {
            "StringEquals": {
              "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
            }
          }
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:CreateListener",
            "elasticloadbalancing:CreateLoadBalancer",
            "elasticloadbalancing:AddTags"
          ],
          "Resource": [
            "arn:aws:elasticloadbalancing:*:*:listener/net/gke-*",
            "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/gke-*"
          ],
          "Condition": {
            "StringEquals": {
              "aws:RequestTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
            }
          }
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:AddTags",
            "elasticloadbalancing:DeleteListener",
            "elasticloadbalancing:DeleteLoadBalancer",
            "elasticloadbalancing:RemoveTags"
          ],
          "Resource": [
            "arn:aws:elasticloadbalancing:*:*:listener/net/gke-*",
            "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/gke-*"
          ],
          "Condition": {
            "StringEquals": {
              "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
            }
          }
        }
      ]
    }'
    

    Save the ARN generated by this command for later use.

  4. Create a policy to control access to AWS Key Management Service resources.

    See Creating KMS keys with specific permissions for more information on permissions for each individual key.

    aws iam create-policy --policy-name API_POLICY_kms \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["kms:DescribeKey"],
          "Resource": ["arn:aws:kms:*:*:key/*"]
        },
        {
          "Effect": "Allow",
          "Action": ["kms:Encrypt"],
          "Resource": CONTROL_PLANE_CONFIG_KMS_KEY_ARN
        },
        {
          "Effect": "Allow",
          "Action": ["kms:Encrypt"],
          "Resource": NODE_POOL_CONFIG_KMS_KEY_ARN
        },
        {
          "Effect": "Allow",
          "Action": ["kms:GenerateDataKeyWithoutPlaintext"],
          "Resource": CONTROL_PLANE_MAIN_VOLUME_KMS_KEY_ARN
        }
      ]
    }'
    

    Replace the following:

    Save the ARN generated by this command for later use.

Attach policies to the GKE Multi-Cloud API role

For each policy you created in the previous steps, run the following command to attach the policy to the GKE Multi-Cloud API role:

aws iam attach-role-policy \
    --policy-arn API_POLICY_ARN \
    --role-name API_ROLE

Replace

  • API_POLICY_ARN with the API policy ARN of each policy you created previously
  • API_ROLE with the GKE Multi-Cloud API service agent role name

Create a cluster or node pool

If you create roles and policies scoped to tagged resources, you must specify the tags when you create a cluster or node pool. You do this with the --tags parameter to the cluster and node pool creation commands.

Create a cluster

To create your cluster, follow the instructions to create a cluster and include the --tags parameter as in the following:

gcloud container aws clusters create CLUSTER_NAME \
...
    --tags="ACCESS_CONTROL_TAG_KEY=ACCESS_CONTROL_TAG_VALUE"

Replace the following:

  • ACCESS_CONTROL_TAG_KEY: the tag key that applies to this policy
  • ACCESS_CONTROL_TAG_VALUE: the tag value that applies to this policy

Create a node pool

To create a node pool, follow the instructions to create a node pool and include the --tags parameter as in the following:

gcloud container aws node-pools create NODE_POOL_NAME \
...
    --tags "ACCESS_CONTROL_TAG_KEY=ACCESS_CONTROL_TAG_VALUE"

Replace the following:

  • ACCESS_CONTROL_TAG_KEY: the tag key that applies to this policy
  • ACCESS_CONTROL_TAG_VALUE: the tag value that applies to this policy

Create the control plane role

To create the control plane role with default permissions, follow these steps:

  1. To create this role, run the following command:

    aws iam create-role --role-name CONTROL_PLANE_ROLE \
        --assume-role-policy-document '{
        "Version": "2012-10-17",
        "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                 "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
      ]
    }'
    

    Replace CONTROL_PLANE_ROLE with a name for the control plane role.

  2. Create an IAM policy for this role. If you created multiple KMS keys for database and configuration encryption, include all of their ARNs as a comma-separated list.

    Choose if you are connecting directly to your control plane, or connect directly to your control plane and use tags.

    Direct connection

    aws iam create-policy --policy-name CONTROL_PLANE_POLICY \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "",
          "Effect": "Allow",
          "Action": [
            "ec2:AttachNetworkInterface",
            "ec2:AttachVolume",
            "ec2:AuthorizeSecurityGroupIngress",
            "ec2:CreateRoute",
            "ec2:CreateSecurityGroup",
            "ec2:CreateSnapshot",
            "ec2:CreateTags",
            "ec2:CreateVolume",
            "ec2:DeleteRoute",
            "ec2:DeleteSecurityGroup",
            "ec2:DeleteSnapshot",
            "ec2:DeleteTags",
            "ec2:DeleteVolume",
            "ec2:DescribeAccountAttributes",
            "ec2:DescribeAvailabilityZones",
            "ec2:DescribeDhcpOptions",
            "ec2:DescribeInstances",
            "ec2:DescribeInstanceTypes",
            "ec2:DescribeInternetGateways",
            "ec2:DescribeLaunchTemplateVersions",
            "ec2:DescribeRegions",
            "ec2:DescribeRouteTables",
            "ec2:DescribeSecurityGroups",
            "ec2:DescribeSnapshots",
            "ec2:DescribeSubnets",
            "ec2:DescribeTags",
            "ec2:DescribeVolumes",
            "ec2:DescribeVolumesModifications",
            "ec2:DescribeVpcs",
            "ec2:DetachVolume",
            "ec2:ModifyInstanceAttribute",
            "ec2:ModifyVolume",
            "ec2:RevokeSecurityGroupIngress",
            "autoscaling:DescribeAutoScalingGroups",
            "autoscaling:DescribeAutoScalingInstances",
            "autoscaling:DescribeLaunchConfigurations",
            "autoscaling:DescribeTags",
            "autoscaling:SetDesiredCapacity",
            "autoscaling:TerminateInstanceInAutoScalingGroup",
            "elasticloadbalancing:AddTags",
            "elasticloadbalancing:ApplySecurityGroupsToLoadBalancer",
            "elasticloadbalancing:AttachLoadBalancerToSubnets",
            "elasticloadbalancing:ConfigureHealthCheck",
            "elasticloadbalancing:CreateListener",
            "elasticloadbalancing:CreateLoadBalancer",
            "elasticloadbalancing:CreateLoadBalancerListeners",
            "elasticloadbalancing:CreateLoadBalancerPolicy",
            "elasticloadbalancing:CreateTargetGroup",
            "elasticloadbalancing:DeleteListener",
            "elasticloadbalancing:DeleteLoadBalancer",
            "elasticloadbalancing:DeleteLoadBalancerListeners",
            "elasticloadbalancing:DeleteTargetGroup",
            "elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
            "elasticloadbalancing:DeregisterTargets",
            "elasticloadbalancing:DescribeListeners",
            "elasticloadbalancing:DescribeLoadBalancerAttributes",
            "elasticloadbalancing:DescribeLoadBalancerPolicies",
            "elasticloadbalancing:DescribeLoadBalancers",
            "elasticloadbalancing:DescribeTargetGroups",
            "elasticloadbalancing:DescribeTargetHealth",
            "elasticloadbalancing:DetachLoadBalancerFromSubnets",
            "elasticloadbalancing:ModifyListener",
            "elasticloadbalancing:ModifyLoadBalancerAttributes",
            "elasticloadbalancing:ModifyTargetGroup",
            "elasticloadbalancing:RegisterInstancesWithLoadBalancer",
            "elasticloadbalancing:RegisterTargets",
            "elasticloadbalancing:SetLoadBalancerPoliciesForBackendServer",
            "elasticloadbalancing:SetLoadBalancerPoliciesOfListener",
            "elasticfilesystem:CreateAccessPoint",
            "elasticfilesystem:DeleteAccessPoint",
            "elasticfilesystem:DescribeAccessPoints",
            "elasticfilesystem:DescribeFileSystems",
            "elasticfilesystem:DescribeMountTargets",
            "kms:CreateGrant",
            "kms:Decrypt",
            "kms:Encrypt",
            "kms:GrantIsForAWSResource"
          ],
          "Resource": "*"
        }
      ]
    }'
    

    Replace the following:

    • CONTROL_PLANE_POLICY with a name for the control plane policy

    Direct with tags

    aws iam create-policy --policy-name CONTROL_PLANE_POLICY_autoscaling \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "autoscaling:DescribeAutoScalingGroups",
            "autoscaling:DescribeAutoScalingInstances",
            "autoscaling:DescribeLaunchConfigurations",
            "autoscaling:DescribeTags"
          ],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "autoscaling:SetDesiredCapacity",
            "autoscaling:TerminateInstanceInAutoScalingGroup"
          ],
          "Resource": [
            "arn:aws:autoscaling:*:*:autoScalingGroup:*:autoScalingGroupName/gke-*"
          ],
          "Condition": {
            "StringEquals": {
              "aws:ResourceTag/ACCESS_CONTROL_TAG_KEY": "ACCESS_CONTROL_TAG_VALUE"
            }
          }
        }
      ]
    }'
    
    aws iam create-policy --policy-name CONTROL_PLANE_POLICY_ec2 \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "ec2:DescribeAccountAttributes",
            "ec2:DescribeAvailabilityZones",
            "ec2:DescribeDhcpOptions",
            "ec2:DescribeInstances",
            "ec2:DescribeInstanceTypes",
            "ec2:DescribeInternetGateways",
            "ec2:DescribeLaunchTemplateVersions",
            "ec2:DescribeRegions",
            "ec2:DescribeRouteTables",
            "ec2:DescribeSecurityGroups",
            "ec2:DescribeSnapshots",
            "ec2:DescribeSubnets",
            "ec2:DescribeTags",
            "ec2:DescribeVolumes",
            "ec2:DescribeVolumesModifications",
            "ec2:DescribeVpcs"
          ],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:AttachNetworkInterface"],
          "Resource": [
            "arn:aws:ec2:*:*:instance/*",
            "arn:aws:ec2:*:*:network-interface/*"
          ]
        },
        {
          "Effect": "Allow",
          "Action": [
            "ec2:CreateVolume",
            "ec2:CreateTags",
            "ec2:AttachVolume",
            "ec2:DeleteVolume",
            "ec2:DetachVolume",
            "ec2:ModifyVolume"
          ],
          "Resource": ["arn:aws:ec2:*:*:volume/*"]
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:AttachVolume", "ec2:DetachVolume"],
          "Resource": ["arn:aws:ec2:*:*:instance/*"]
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:CreateSecurityGroup", "ec2:CreateTags"],
          "Resource": ["arn:aws:ec2:*:*:security-group/*"]
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:CreateSecurityGroup"],
          "Resource": ["arn:aws:ec2:*:*:vpc/*"]
        },
        {
          "Effect": "Allow",
          "Action": [
            "ec2:AuthorizeSecurityGroupIngress",
            "ec2:DeleteSecurityGroup",
            "ec2:RevokeSecurityGroupIngress"
          ],
          "Resource": ["arn:aws:ec2:*:*:security-group/*"]
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:CreateSnapshot", "ec2:CreateTags", "ec2:DeleteSnapshot"],
          "Resource": ["arn:aws:ec2:*:*:snapshot/*"]
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:CreateSnapshot"],
          "Resource": ["arn:aws:ec2:*:*:volume/*"]
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:CreateRoute", "ec2:DeleteRoute"],
          "Resource": ["arn:aws:ec2:*:*:route-table/*"]
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:ModifyInstanceAttribute"],
          "Resource": [
            "arn:aws:ec2:*:*:instance/*",
            "arn:aws:ec2:*:*:security-group/*",
            "arn:aws:ec2:*:*:volume/*"
          ]
        },
        {
          "Effect": "Allow",
          "Action": ["ec2:DeleteTags"],
          "Resource": [
            "arn:aws:ec2:*:*:security-group/*",
            "arn:aws:ec2:*:*:snapshot/*",
            "arn:aws:ec2:*:*:volume/*"
          ]
        }
      ]
    }'
    
    aws iam create-policy --policy-name CONTROL_PLANE_POLICY_elasticloadbalancing \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:DescribeLoadBalancers",
            "elasticloadbalancing:DescribeLoadBalancerAttributes",
            "elasticloadbalancing:DescribeListeners",
            "elasticloadbalancing:DescribeLoadBalancerPolicies",
            "elasticloadbalancing:DescribeTargetGroups",
            "elasticloadbalancing:DescribeTargetHealth"
          ],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:AddTags",
            "elasticloadbalancing:AttachLoadBalancerToSubnets",
            "elasticloadbalancing:ApplySecurityGroupsToLoadBalancer",
            "elasticloadbalancing:CreateListener",
            "elasticloadbalancing:CreateLoadBalancer",
            "elasticloadbalancing:CreateLoadBalancerPolicy",
            "elasticloadbalancing:CreateLoadBalancerListeners",
            "elasticloadbalancing:ConfigureHealthCheck",
            "elasticloadbalancing:DeleteLoadBalancer",
            "elasticloadbalancing:DeleteLoadBalancerListeners",
            "elasticloadbalancing:DetachLoadBalancerFromSubnets",
            "elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
            "elasticloadbalancing:ModifyLoadBalancerAttributes",
            "elasticloadbalancing:RegisterInstancesWithLoadBalancer",
            "elasticloadbalancing:SetLoadBalancerPoliciesForBackendServer",
            "elasticloadbalancing:SetLoadBalancerPoliciesOfListener"
          ],
          "Resource": ["arn:aws:elasticloadbalancing:*:*:loadbalancer/*"]
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:AddTags",
            "elasticloadbalancing:CreateTargetGroup",
            "elasticloadbalancing:DeleteTargetGroup",
            "elasticloadbalancing:DeregisterTargets",
            "elasticloadbalancing:ModifyTargetGroup",
            "elasticloadbalancing:RegisterTargets"
          ],
          "Resource": ["arn:aws:elasticloadbalancing:*:*:targetgroup/*"]
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticloadbalancing:DeleteListener",
            "elasticloadbalancing:ModifyListener"
          ],
          "Resource": ["arn:aws:elasticloadbalancing:*:*:listener/*"]
        }
      ]
    }'
    
    aws iam create-policy --policy-name CONTROL_PLANE_POLICY_elasticfilesystem \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "elasticfilesystem:DescribeAccessPoints",
            "elasticfilesystem:DescribeFileSystems",
            "elasticfilesystem:DescribeMountTargets"
          ],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticfilesystem:CreateAccessPoint"
          ],
          "Resource": [
            "arn:aws:elasticfilesystem:*:*:file-system/*"
          ]
        },
        {
          "Effect": "Allow",
          "Action": [
            "elasticfilesystem:DeleteAccessPoint"
          ],
          "Resource": [
            "arn:aws:elasticfilesystem:*:*:access-point/*"
          ]
        }
      ]
    }'
    
    aws iam create-policy --policy-name CONTROL_PLANE_POLICY_kms \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["kms:Decrypt", "kms:Encrypt"],
          "Resource": DATABASE_KMS_KEY_ARN
        },
        {
          "Effect": "Allow",
          "Action": ["kms:Decrypt"],
          "Resource": CONTROL_PLANE_CONFIG_KMS_KEY_ARN
        },
        {
          "Effect": "Allow",
          "Action": ["kms:CreateGrant"],
          "Resource": CONTROL_PLANE_MAIN_VOLUME_KMS_KEY_ARN,
          "Condition": {
            "Bool": {
              "kms:GrantIsForAWSResource": "true"
            }
          }
        }
      ]
    }'
    

    Replace the following:

    Copy the value of each Policy.Arn for use in a following command.

  3. If you are using an HTTP proxy, create an additional policy for AWS Secrets Manager.

    aws iam create-policy --policy-name CONTROL_PLANE_POLICY_secretsmanager \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "secretsmanager:GetSecretValue"
          ],
          "Resource": PROXY_CONFIG_ARN
        }
      ]
    }'
    

    Replace the following:

    Copy the value of each Policy.Arn for use in the next command.

  4. Attach the new policy to the corresponding role.

    aws iam attach-role-policy \
        --policy-arn CONTROL_PLANE_POLICY_ARN \
        --role-name CONTROL_PLANE_ROLE
    

    Replace the following:

    • CONTROL_PLANE_ROLE
    • CONTROL_PLANE_POLICY_ARN with the Policy.Arn value from the previous command
  5. To create an IAM instance profile, run the following command.

    aws iam create-instance-profile \
        --instance-profile-name CONTROL_PLANE_PROFILE
    

    Replace CONTROL_PLANE_PROFILE with a name for the GKE Multi-Cloud control plane profile.

  6. Finally, add the new role to the instance profile you've just created.

    aws iam add-role-to-instance-profile \
        --instance-profile-name CONTROL_PLANE_PROFILE \
        --role-name CONTROL_PLANE_ROLE
    

    Replace the following:

    • CONTROL_PLANE_PROFILE
    • CONTROL_PLANE_ROLE

    You will use the instance profile name you choose here later during cluster creation.

Create the node pool role

To create the node pool role with default permissions, follow these steps:

  1. Create a role for the VMs in the node pool to use.

    aws iam create-role --role-name NODE_POOL_ROLE \
        --assume-role-policy-document '{
        "Version": "2012-10-17",
        "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
            "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
      ]
    }'
    

    Replace NODE_POOL_ROLE with the name of your node pool role.

  2. Create a policy with the permissions needed by node pool VMs. Choose if you are connecting directly to your control plane or are using an HTTP proxy.

    Direct

    aws iam create-policy --policy-name NODE_POOL_POLICY_kms \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["kms:Decrypt"],
          "Resource": NODE_POOL_CONFIG_KMS_KEY_ARN
        }
      ]
    }'
    

    Replace the following:

    HTTP Proxy

    aws iam create-policy --policy-name NODE_POOL_POLICY_kms \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["kms:Decrypt"],
          "Resource": NODE_POOL_CONFIG_KMS_KEY_ARN
        }
      ]
    }'
    
    aws iam create-policy --policy-name NODE_POOL_POLICY_secretsmanager \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "secretsmanager:GetSecretValue"
          ],
          "Resource": PROXY_CONFIG_ARN
        }
      ]
    }'
    

    Replace the following:

    • KMS_KEY_ARN: your node pool's KMS key.
    • NODE_POOL_POLICY: your node pool policy name.
    • PROXY_CONFIG_ARN: the ARN of your proxy configuration. For more information see, Create a proxy configuration.

    Copy the value of each Policy.Arn for use in the next command.

  3. Attach the role to its policy.

    aws iam attach-role-policy --role-name NODE_POOL_ROLE \
        --policy-arn "NODE_POOL_POLICY_ARN"
    

    Replace the following:

    • NODE_POOL_ROLE: the name of your node pool role.
    • NODE_POOL_POLICY_ARN: the Policy.Arn value from the previous command.
  4. If your workloads need to work with the ECR container registry, attach the follow policy for ECR access:

    Public registry

    Create a policy with the following permissions:

      aws iam create-policy --policy-name NODE_POOL_POLICY_ecr \
      --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "ecr-public:GetAuthorizationToken",
            "sts:GetServiceBearerToken"
          ],
          "Resource": "*"
        }
      ]
    }'
    

    Copy the value of Policy.Arn as NODE_POOL_ECR_POLICY_ARN

    Attach it to the node-pool role you created.

    aws iam attach-role-policy --role-name NODE_POOL_ROLE \
        --policy-arn "NODE_POOL_ECR_POLICY_ARN"
    

    Replace the following:

    • NODE_POOL_ROLE: the name of your node pool role.
    • NODE_POOL_ECR_POLICY_ARN: the Policy.Arn value from the previous command.

    Private registry

    Attach AmazonEC2ContainerRegistryReadOnly to the node-pool role for private registry access:

    aws iam attach-role-policy --role-name NODE_POOL_ROLE \
        --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
    

    Replace NODE_POOL_ROLE with the name of your node pool role.

  5. Create an instance profile for this role.

    aws iam create-instance-profile \
        --instance-profile-name NODE_POOL_PROFILE
    

    Replace NODE_POOL_PROFILE with the name of your node pool profile.

  6. Add the new role to the instance profile.

    aws iam add-role-to-instance-profile \
        --instance-profile-name NODE_POOL_PROFILE \
        --role-name NODE_POOL_ROLE
    

    Replace the following:

    • NODE_POOL_PROFILE: the name of your node pool profile.
    • NODE_POOL_ROLE: the name of your node pool role.

What's next