Deploy AWS EKS Clusters

The aws:eks/cluster:Cluster resource, part of the Pulumi AWS provider, provisions the EKS control plane: the API server, scheduler, and controller manager with specified networking and authentication. This guide focuses on three capabilities: standard cluster creation with API authentication, EKS Auto Mode for automated infrastructure management, and hybrid nodes and Outposts deployment models.

EKS clusters require IAM roles for the control plane (and nodes for Auto Mode), VPC subnets for networking, and optionally Outpost infrastructure for local deployments. The examples are intentionally small. Combine them with your own node groups, add-ons, and workload configurations.

Create a standard EKS cluster with API authentication

Most deployments start with a cluster that runs workloads on EC2-based node groups, requiring an IAM role for the control plane and VPC subnets for networking.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const cluster = new aws.iam.Role("cluster", {
    name: "eks-cluster-example",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: [
                "sts:AssumeRole",
                "sts:TagSession",
            ],
            Effect: "Allow",
            Principal: {
                Service: "eks.amazonaws.com",
            },
        }],
    }),
});
const clusterAmazonEKSClusterPolicy = new aws.iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
    role: cluster.name,
});
const example = new aws.eks.Cluster("example", {
    name: "example",
    accessConfig: {
        authenticationMode: "API",
    },
    roleArn: cluster.arn,
    version: "1.31",
    vpcConfig: {
        subnetIds: [
            az1.id,
            az2.id,
            az3.id,
        ],
    },
}, {
    dependsOn: [clusterAmazonEKSClusterPolicy],
});
import pulumi
import json
import pulumi_aws as aws

cluster = aws.iam.Role("cluster",
    name="eks-cluster-example",
    assume_role_policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Action": [
                "sts:AssumeRole",
                "sts:TagSession",
            ],
            "Effect": "Allow",
            "Principal": {
                "Service": "eks.amazonaws.com",
            },
        }],
    }))
cluster_amazon_eks_cluster_policy = aws.iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
    role=cluster.name)
example = aws.eks.Cluster("example",
    name="example",
    access_config={
        "authentication_mode": "API",
    },
    role_arn=cluster.arn,
    version="1.31",
    vpc_config={
        "subnet_ids": [
            az1["id"],
            az2["id"],
            az3["id"],
        ],
    },
    opts = pulumi.ResourceOptions(depends_on=[cluster_amazon_eks_cluster_policy]))
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/eks"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"Version": "2012-10-17",
			"Statement": []map[string]interface{}{
				map[string]interface{}{
					"Action": []string{
						"sts:AssumeRole",
						"sts:TagSession",
					},
					"Effect": "Allow",
					"Principal": map[string]interface{}{
						"Service": "eks.amazonaws.com",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		cluster, err := iam.NewRole(ctx, "cluster", &iam.RoleArgs{
			Name:             pulumi.String("eks-cluster-example"),
			AssumeRolePolicy: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		clusterAmazonEKSClusterPolicy, err := iam.NewRolePolicyAttachment(ctx, "cluster_AmazonEKSClusterPolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"),
			Role:      cluster.Name,
		})
		if err != nil {
			return err
		}
		_, err = eks.NewCluster(ctx, "example", &eks.ClusterArgs{
			Name: pulumi.String("example"),
			AccessConfig: &eks.ClusterAccessConfigArgs{
				AuthenticationMode: pulumi.String("API"),
			},
			RoleArn: cluster.Arn,
			Version: pulumi.String("1.31"),
			VpcConfig: &eks.ClusterVpcConfigArgs{
				SubnetIds: pulumi.StringArray{
					az1.Id,
					az2.Id,
					az3.Id,
				},
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			clusterAmazonEKSClusterPolicy,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var cluster = new Aws.Iam.Role("cluster", new()
    {
        Name = "eks-cluster-example",
        AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Action"] = new[]
                    {
                        "sts:AssumeRole",
                        "sts:TagSession",
                    },
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "eks.amazonaws.com",
                    },
                },
            },
        }),
    });

    var clusterAmazonEKSClusterPolicy = new Aws.Iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
        Role = cluster.Name,
    });

    var example = new Aws.Eks.Cluster("example", new()
    {
        Name = "example",
        AccessConfig = new Aws.Eks.Inputs.ClusterAccessConfigArgs
        {
            AuthenticationMode = "API",
        },
        RoleArn = cluster.Arn,
        Version = "1.31",
        VpcConfig = new Aws.Eks.Inputs.ClusterVpcConfigArgs
        {
            SubnetIds = new[]
            {
                az1.Id,
                az2.Id,
                az3.Id,
            },
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            clusterAmazonEKSClusterPolicy,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.RolePolicyAttachment;
import com.pulumi.aws.iam.RolePolicyAttachmentArgs;
import com.pulumi.aws.eks.Cluster;
import com.pulumi.aws.eks.ClusterArgs;
import com.pulumi.aws.eks.inputs.ClusterAccessConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterVpcConfigArgs;
import static com.pulumi.codegen.internal.Serialization.*;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var cluster = new Role("cluster", RoleArgs.builder()
            .name("eks-cluster-example")
            .assumeRolePolicy(serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Statement", jsonArray(jsonObject(
                        jsonProperty("Action", jsonArray(
                            "sts:AssumeRole", 
                            "sts:TagSession"
                        )),
                        jsonProperty("Effect", "Allow"),
                        jsonProperty("Principal", jsonObject(
                            jsonProperty("Service", "eks.amazonaws.com")
                        ))
                    )))
                )))
            .build());

        var clusterAmazonEKSClusterPolicy = new RolePolicyAttachment("clusterAmazonEKSClusterPolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSClusterPolicy")
            .role(cluster.name())
            .build());

        var example = new Cluster("example", ClusterArgs.builder()
            .name("example")
            .accessConfig(ClusterAccessConfigArgs.builder()
                .authenticationMode("API")
                .build())
            .roleArn(cluster.arn())
            .version("1.31")
            .vpcConfig(ClusterVpcConfigArgs.builder()
                .subnetIds(                
                    az1.id(),
                    az2.id(),
                    az3.id())
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(clusterAmazonEKSClusterPolicy)
                .build());

    }
}
resources:
  example:
    type: aws:eks:Cluster
    properties:
      name: example
      accessConfig:
        authenticationMode: API
      roleArn: ${cluster.arn}
      version: '1.31'
      vpcConfig:
        subnetIds:
          - ${az1.id}
          - ${az2.id}
          - ${az3.id}
    options:
      dependsOn:
        - ${clusterAmazonEKSClusterPolicy}
  cluster:
    type: aws:iam:Role
    properties:
      name: eks-cluster-example
      assumeRolePolicy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Action:
                - sts:AssumeRole
                - sts:TagSession
              Effect: Allow
              Principal:
                Service: eks.amazonaws.com
  clusterAmazonEKSClusterPolicy:
    type: aws:iam:RolePolicyAttachment
    name: cluster_AmazonEKSClusterPolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
      role: ${cluster.name}

The roleArn grants the control plane permissions to manage AWS resources on your behalf. The vpcConfig.subnetIds places the cluster’s network interfaces in your VPC subnets. The accessConfig.authenticationMode set to “API” enables EKS access entries for authentication. The version property locks the Kubernetes version; you must update it explicitly to upgrade.

Enable EKS Auto Mode for automated infrastructure

EKS Auto Mode eliminates manual node group management by provisioning compute, storage, and load balancing based on pod requirements.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const node = new aws.iam.Role("node", {
    name: "eks-auto-node-example",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: ["sts:AssumeRole"],
            Effect: "Allow",
            Principal: {
                Service: "ec2.amazonaws.com",
            },
        }],
    }),
});
const cluster = new aws.iam.Role("cluster", {
    name: "eks-cluster-example",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: [
                "sts:AssumeRole",
                "sts:TagSession",
            ],
            Effect: "Allow",
            Principal: {
                Service: "eks.amazonaws.com",
            },
        }],
    }),
});
const clusterAmazonEKSClusterPolicy = new aws.iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
    role: cluster.name,
});
const clusterAmazonEKSComputePolicy = new aws.iam.RolePolicyAttachment("cluster_AmazonEKSComputePolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSComputePolicy",
    role: cluster.name,
});
const clusterAmazonEKSBlockStoragePolicy = new aws.iam.RolePolicyAttachment("cluster_AmazonEKSBlockStoragePolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy",
    role: cluster.name,
});
const clusterAmazonEKSLoadBalancingPolicy = new aws.iam.RolePolicyAttachment("cluster_AmazonEKSLoadBalancingPolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy",
    role: cluster.name,
});
const clusterAmazonEKSNetworkingPolicy = new aws.iam.RolePolicyAttachment("cluster_AmazonEKSNetworkingPolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy",
    role: cluster.name,
});
const example = new aws.eks.Cluster("example", {
    name: "example",
    accessConfig: {
        authenticationMode: "API",
    },
    roleArn: cluster.arn,
    version: "1.31",
    bootstrapSelfManagedAddons: false,
    computeConfig: {
        enabled: true,
        nodePools: ["general-purpose"],
        nodeRoleArn: node.arn,
    },
    kubernetesNetworkConfig: {
        elasticLoadBalancing: {
            enabled: true,
        },
    },
    storageConfig: {
        blockStorage: {
            enabled: true,
        },
    },
    vpcConfig: {
        endpointPrivateAccess: true,
        endpointPublicAccess: true,
        subnetIds: [
            az1.id,
            az2.id,
            az3.id,
        ],
    },
}, {
    dependsOn: [
        clusterAmazonEKSClusterPolicy,
        clusterAmazonEKSComputePolicy,
        clusterAmazonEKSBlockStoragePolicy,
        clusterAmazonEKSLoadBalancingPolicy,
        clusterAmazonEKSNetworkingPolicy,
    ],
});
const nodeAmazonEKSWorkerNodeMinimalPolicy = new aws.iam.RolePolicyAttachment("node_AmazonEKSWorkerNodeMinimalPolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy",
    role: node.name,
});
const nodeAmazonEC2ContainerRegistryPullOnly = new aws.iam.RolePolicyAttachment("node_AmazonEC2ContainerRegistryPullOnly", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly",
    role: node.name,
});
import pulumi
import json
import pulumi_aws as aws

node = aws.iam.Role("node",
    name="eks-auto-node-example",
    assume_role_policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Action": ["sts:AssumeRole"],
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com",
            },
        }],
    }))
cluster = aws.iam.Role("cluster",
    name="eks-cluster-example",
    assume_role_policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Action": [
                "sts:AssumeRole",
                "sts:TagSession",
            ],
            "Effect": "Allow",
            "Principal": {
                "Service": "eks.amazonaws.com",
            },
        }],
    }))
cluster_amazon_eks_cluster_policy = aws.iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
    role=cluster.name)
cluster_amazon_eks_compute_policy = aws.iam.RolePolicyAttachment("cluster_AmazonEKSComputePolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSComputePolicy",
    role=cluster.name)
cluster_amazon_eks_block_storage_policy = aws.iam.RolePolicyAttachment("cluster_AmazonEKSBlockStoragePolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy",
    role=cluster.name)
cluster_amazon_eks_load_balancing_policy = aws.iam.RolePolicyAttachment("cluster_AmazonEKSLoadBalancingPolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy",
    role=cluster.name)
cluster_amazon_eks_networking_policy = aws.iam.RolePolicyAttachment("cluster_AmazonEKSNetworkingPolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy",
    role=cluster.name)
example = aws.eks.Cluster("example",
    name="example",
    access_config={
        "authentication_mode": "API",
    },
    role_arn=cluster.arn,
    version="1.31",
    bootstrap_self_managed_addons=False,
    compute_config={
        "enabled": True,
        "node_pools": ["general-purpose"],
        "node_role_arn": node.arn,
    },
    kubernetes_network_config={
        "elastic_load_balancing": {
            "enabled": True,
        },
    },
    storage_config={
        "block_storage": {
            "enabled": True,
        },
    },
    vpc_config={
        "endpoint_private_access": True,
        "endpoint_public_access": True,
        "subnet_ids": [
            az1["id"],
            az2["id"],
            az3["id"],
        ],
    },
    opts = pulumi.ResourceOptions(depends_on=[
            cluster_amazon_eks_cluster_policy,
            cluster_amazon_eks_compute_policy,
            cluster_amazon_eks_block_storage_policy,
            cluster_amazon_eks_load_balancing_policy,
            cluster_amazon_eks_networking_policy,
        ]))
node_amazon_eks_worker_node_minimal_policy = aws.iam.RolePolicyAttachment("node_AmazonEKSWorkerNodeMinimalPolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy",
    role=node.name)
node_amazon_ec2_container_registry_pull_only = aws.iam.RolePolicyAttachment("node_AmazonEC2ContainerRegistryPullOnly",
    policy_arn="arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly",
    role=node.name)
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/eks"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"Version": "2012-10-17",
			"Statement": []map[string]interface{}{
				map[string]interface{}{
					"Action": []string{
						"sts:AssumeRole",
					},
					"Effect": "Allow",
					"Principal": map[string]interface{}{
						"Service": "ec2.amazonaws.com",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		node, err := iam.NewRole(ctx, "node", &iam.RoleArgs{
			Name:             pulumi.String("eks-auto-node-example"),
			AssumeRolePolicy: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		tmpJSON1, err := json.Marshal(map[string]interface{}{
			"Version": "2012-10-17",
			"Statement": []map[string]interface{}{
				map[string]interface{}{
					"Action": []string{
						"sts:AssumeRole",
						"sts:TagSession",
					},
					"Effect": "Allow",
					"Principal": map[string]interface{}{
						"Service": "eks.amazonaws.com",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json1 := string(tmpJSON1)
		cluster, err := iam.NewRole(ctx, "cluster", &iam.RoleArgs{
			Name:             pulumi.String("eks-cluster-example"),
			AssumeRolePolicy: pulumi.String(json1),
		})
		if err != nil {
			return err
		}
		clusterAmazonEKSClusterPolicy, err := iam.NewRolePolicyAttachment(ctx, "cluster_AmazonEKSClusterPolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"),
			Role:      cluster.Name,
		})
		if err != nil {
			return err
		}
		clusterAmazonEKSComputePolicy, err := iam.NewRolePolicyAttachment(ctx, "cluster_AmazonEKSComputePolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSComputePolicy"),
			Role:      cluster.Name,
		})
		if err != nil {
			return err
		}
		clusterAmazonEKSBlockStoragePolicy, err := iam.NewRolePolicyAttachment(ctx, "cluster_AmazonEKSBlockStoragePolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy"),
			Role:      cluster.Name,
		})
		if err != nil {
			return err
		}
		clusterAmazonEKSLoadBalancingPolicy, err := iam.NewRolePolicyAttachment(ctx, "cluster_AmazonEKSLoadBalancingPolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy"),
			Role:      cluster.Name,
		})
		if err != nil {
			return err
		}
		clusterAmazonEKSNetworkingPolicy, err := iam.NewRolePolicyAttachment(ctx, "cluster_AmazonEKSNetworkingPolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy"),
			Role:      cluster.Name,
		})
		if err != nil {
			return err
		}
		_, err = eks.NewCluster(ctx, "example", &eks.ClusterArgs{
			Name: pulumi.String("example"),
			AccessConfig: &eks.ClusterAccessConfigArgs{
				AuthenticationMode: pulumi.String("API"),
			},
			RoleArn:                    cluster.Arn,
			Version:                    pulumi.String("1.31"),
			BootstrapSelfManagedAddons: pulumi.Bool(false),
			ComputeConfig: &eks.ClusterComputeConfigArgs{
				Enabled: pulumi.Bool(true),
				NodePools: pulumi.StringArray{
					pulumi.String("general-purpose"),
				},
				NodeRoleArn: node.Arn,
			},
			KubernetesNetworkConfig: &eks.ClusterKubernetesNetworkConfigArgs{
				ElasticLoadBalancing: &eks.ClusterKubernetesNetworkConfigElasticLoadBalancingArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			StorageConfig: &eks.ClusterStorageConfigArgs{
				BlockStorage: &eks.ClusterStorageConfigBlockStorageArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			VpcConfig: &eks.ClusterVpcConfigArgs{
				EndpointPrivateAccess: pulumi.Bool(true),
				EndpointPublicAccess:  pulumi.Bool(true),
				SubnetIds: pulumi.StringArray{
					az1.Id,
					az2.Id,
					az3.Id,
				},
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			clusterAmazonEKSClusterPolicy,
			clusterAmazonEKSComputePolicy,
			clusterAmazonEKSBlockStoragePolicy,
			clusterAmazonEKSLoadBalancingPolicy,
			clusterAmazonEKSNetworkingPolicy,
		}))
		if err != nil {
			return err
		}
		_, err = iam.NewRolePolicyAttachment(ctx, "node_AmazonEKSWorkerNodeMinimalPolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy"),
			Role:      node.Name,
		})
		if err != nil {
			return err
		}
		_, err = iam.NewRolePolicyAttachment(ctx, "node_AmazonEC2ContainerRegistryPullOnly", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly"),
			Role:      node.Name,
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var node = new Aws.Iam.Role("node", new()
    {
        Name = "eks-auto-node-example",
        AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Action"] = new[]
                    {
                        "sts:AssumeRole",
                    },
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "ec2.amazonaws.com",
                    },
                },
            },
        }),
    });

    var cluster = new Aws.Iam.Role("cluster", new()
    {
        Name = "eks-cluster-example",
        AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Action"] = new[]
                    {
                        "sts:AssumeRole",
                        "sts:TagSession",
                    },
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "eks.amazonaws.com",
                    },
                },
            },
        }),
    });

    var clusterAmazonEKSClusterPolicy = new Aws.Iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
        Role = cluster.Name,
    });

    var clusterAmazonEKSComputePolicy = new Aws.Iam.RolePolicyAttachment("cluster_AmazonEKSComputePolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSComputePolicy",
        Role = cluster.Name,
    });

    var clusterAmazonEKSBlockStoragePolicy = new Aws.Iam.RolePolicyAttachment("cluster_AmazonEKSBlockStoragePolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy",
        Role = cluster.Name,
    });

    var clusterAmazonEKSLoadBalancingPolicy = new Aws.Iam.RolePolicyAttachment("cluster_AmazonEKSLoadBalancingPolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy",
        Role = cluster.Name,
    });

    var clusterAmazonEKSNetworkingPolicy = new Aws.Iam.RolePolicyAttachment("cluster_AmazonEKSNetworkingPolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy",
        Role = cluster.Name,
    });

    var example = new Aws.Eks.Cluster("example", new()
    {
        Name = "example",
        AccessConfig = new Aws.Eks.Inputs.ClusterAccessConfigArgs
        {
            AuthenticationMode = "API",
        },
        RoleArn = cluster.Arn,
        Version = "1.31",
        BootstrapSelfManagedAddons = false,
        ComputeConfig = new Aws.Eks.Inputs.ClusterComputeConfigArgs
        {
            Enabled = true,
            NodePools = new[]
            {
                "general-purpose",
            },
            NodeRoleArn = node.Arn,
        },
        KubernetesNetworkConfig = new Aws.Eks.Inputs.ClusterKubernetesNetworkConfigArgs
        {
            ElasticLoadBalancing = new Aws.Eks.Inputs.ClusterKubernetesNetworkConfigElasticLoadBalancingArgs
            {
                Enabled = true,
            },
        },
        StorageConfig = new Aws.Eks.Inputs.ClusterStorageConfigArgs
        {
            BlockStorage = new Aws.Eks.Inputs.ClusterStorageConfigBlockStorageArgs
            {
                Enabled = true,
            },
        },
        VpcConfig = new Aws.Eks.Inputs.ClusterVpcConfigArgs
        {
            EndpointPrivateAccess = true,
            EndpointPublicAccess = true,
            SubnetIds = new[]
            {
                az1.Id,
                az2.Id,
                az3.Id,
            },
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            clusterAmazonEKSClusterPolicy,
            clusterAmazonEKSComputePolicy,
            clusterAmazonEKSBlockStoragePolicy,
            clusterAmazonEKSLoadBalancingPolicy,
            clusterAmazonEKSNetworkingPolicy,
        },
    });

    var nodeAmazonEKSWorkerNodeMinimalPolicy = new Aws.Iam.RolePolicyAttachment("node_AmazonEKSWorkerNodeMinimalPolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy",
        Role = node.Name,
    });

    var nodeAmazonEC2ContainerRegistryPullOnly = new Aws.Iam.RolePolicyAttachment("node_AmazonEC2ContainerRegistryPullOnly", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly",
        Role = node.Name,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.RolePolicyAttachment;
import com.pulumi.aws.iam.RolePolicyAttachmentArgs;
import com.pulumi.aws.eks.Cluster;
import com.pulumi.aws.eks.ClusterArgs;
import com.pulumi.aws.eks.inputs.ClusterAccessConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterComputeConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterKubernetesNetworkConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterKubernetesNetworkConfigElasticLoadBalancingArgs;
import com.pulumi.aws.eks.inputs.ClusterStorageConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterStorageConfigBlockStorageArgs;
import com.pulumi.aws.eks.inputs.ClusterVpcConfigArgs;
import static com.pulumi.codegen.internal.Serialization.*;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var node = new Role("node", RoleArgs.builder()
            .name("eks-auto-node-example")
            .assumeRolePolicy(serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Statement", jsonArray(jsonObject(
                        jsonProperty("Action", jsonArray("sts:AssumeRole")),
                        jsonProperty("Effect", "Allow"),
                        jsonProperty("Principal", jsonObject(
                            jsonProperty("Service", "ec2.amazonaws.com")
                        ))
                    )))
                )))
            .build());

        var cluster = new Role("cluster", RoleArgs.builder()
            .name("eks-cluster-example")
            .assumeRolePolicy(serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Statement", jsonArray(jsonObject(
                        jsonProperty("Action", jsonArray(
                            "sts:AssumeRole", 
                            "sts:TagSession"
                        )),
                        jsonProperty("Effect", "Allow"),
                        jsonProperty("Principal", jsonObject(
                            jsonProperty("Service", "eks.amazonaws.com")
                        ))
                    )))
                )))
            .build());

        var clusterAmazonEKSClusterPolicy = new RolePolicyAttachment("clusterAmazonEKSClusterPolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSClusterPolicy")
            .role(cluster.name())
            .build());

        var clusterAmazonEKSComputePolicy = new RolePolicyAttachment("clusterAmazonEKSComputePolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSComputePolicy")
            .role(cluster.name())
            .build());

        var clusterAmazonEKSBlockStoragePolicy = new RolePolicyAttachment("clusterAmazonEKSBlockStoragePolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy")
            .role(cluster.name())
            .build());

        var clusterAmazonEKSLoadBalancingPolicy = new RolePolicyAttachment("clusterAmazonEKSLoadBalancingPolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy")
            .role(cluster.name())
            .build());

        var clusterAmazonEKSNetworkingPolicy = new RolePolicyAttachment("clusterAmazonEKSNetworkingPolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy")
            .role(cluster.name())
            .build());

        var example = new Cluster("example", ClusterArgs.builder()
            .name("example")
            .accessConfig(ClusterAccessConfigArgs.builder()
                .authenticationMode("API")
                .build())
            .roleArn(cluster.arn())
            .version("1.31")
            .bootstrapSelfManagedAddons(false)
            .computeConfig(ClusterComputeConfigArgs.builder()
                .enabled(true)
                .nodePools("general-purpose")
                .nodeRoleArn(node.arn())
                .build())
            .kubernetesNetworkConfig(ClusterKubernetesNetworkConfigArgs.builder()
                .elasticLoadBalancing(ClusterKubernetesNetworkConfigElasticLoadBalancingArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .storageConfig(ClusterStorageConfigArgs.builder()
                .blockStorage(ClusterStorageConfigBlockStorageArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .vpcConfig(ClusterVpcConfigArgs.builder()
                .endpointPrivateAccess(true)
                .endpointPublicAccess(true)
                .subnetIds(                
                    az1.id(),
                    az2.id(),
                    az3.id())
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(                
                    clusterAmazonEKSClusterPolicy,
                    clusterAmazonEKSComputePolicy,
                    clusterAmazonEKSBlockStoragePolicy,
                    clusterAmazonEKSLoadBalancingPolicy,
                    clusterAmazonEKSNetworkingPolicy)
                .build());

        var nodeAmazonEKSWorkerNodeMinimalPolicy = new RolePolicyAttachment("nodeAmazonEKSWorkerNodeMinimalPolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy")
            .role(node.name())
            .build());

        var nodeAmazonEC2ContainerRegistryPullOnly = new RolePolicyAttachment("nodeAmazonEC2ContainerRegistryPullOnly", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly")
            .role(node.name())
            .build());

    }
}
resources:
  example:
    type: aws:eks:Cluster
    properties:
      name: example
      accessConfig:
        authenticationMode: API
      roleArn: ${cluster.arn}
      version: '1.31'
      bootstrapSelfManagedAddons: false
      computeConfig:
        enabled: true
        nodePools:
          - general-purpose
        nodeRoleArn: ${node.arn}
      kubernetesNetworkConfig:
        elasticLoadBalancing:
          enabled: true
      storageConfig:
        blockStorage:
          enabled: true
      vpcConfig:
        endpointPrivateAccess: true
        endpointPublicAccess: true
        subnetIds:
          - ${az1.id}
          - ${az2.id}
          - ${az3.id}
    options:
      dependsOn:
        - ${clusterAmazonEKSClusterPolicy}
        - ${clusterAmazonEKSComputePolicy}
        - ${clusterAmazonEKSBlockStoragePolicy}
        - ${clusterAmazonEKSLoadBalancingPolicy}
        - ${clusterAmazonEKSNetworkingPolicy}
  node:
    type: aws:iam:Role
    properties:
      name: eks-auto-node-example
      assumeRolePolicy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Action:
                - sts:AssumeRole
              Effect: Allow
              Principal:
                Service: ec2.amazonaws.com
  nodeAmazonEKSWorkerNodeMinimalPolicy:
    type: aws:iam:RolePolicyAttachment
    name: node_AmazonEKSWorkerNodeMinimalPolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy
      role: ${node.name}
  nodeAmazonEC2ContainerRegistryPullOnly:
    type: aws:iam:RolePolicyAttachment
    name: node_AmazonEC2ContainerRegistryPullOnly
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly
      role: ${node.name}
  cluster:
    type: aws:iam:Role
    properties:
      name: eks-cluster-example
      assumeRolePolicy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Action:
                - sts:AssumeRole
                - sts:TagSession
              Effect: Allow
              Principal:
                Service: eks.amazonaws.com
  clusterAmazonEKSClusterPolicy:
    type: aws:iam:RolePolicyAttachment
    name: cluster_AmazonEKSClusterPolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
      role: ${cluster.name}
  clusterAmazonEKSComputePolicy:
    type: aws:iam:RolePolicyAttachment
    name: cluster_AmazonEKSComputePolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSComputePolicy
      role: ${cluster.name}
  clusterAmazonEKSBlockStoragePolicy:
    type: aws:iam:RolePolicyAttachment
    name: cluster_AmazonEKSBlockStoragePolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy
      role: ${cluster.name}
  clusterAmazonEKSLoadBalancingPolicy:
    type: aws:iam:RolePolicyAttachment
    name: cluster_AmazonEKSLoadBalancingPolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy
      role: ${cluster.name}
  clusterAmazonEKSNetworkingPolicy:
    type: aws:iam:RolePolicyAttachment
    name: cluster_AmazonEKSNetworkingPolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy
      role: ${cluster.name}

When computeConfig.enabled is true, EKS automatically provisions nodes from the specified nodePools. The storageConfig.blockStorage.enabled and kubernetesNetworkConfig.elasticLoadBalancing.enabled properties must also be true to activate Auto Mode. Setting bootstrapSelfManagedAddons to false prevents conflicts with Auto Mode’s managed add-ons. The nodeRoleArn grants permissions for Auto Mode nodes to join the cluster and pull container images.

Configure hybrid nodes with remote network CIDRs

EKS Hybrid Nodes extend Kubernetes to infrastructure outside AWS, such as on-premises servers or edge locations, while maintaining control plane integration.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const cluster = new aws.iam.Role("cluster", {
    name: "eks-cluster-example",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: [
                "sts:AssumeRole",
                "sts:TagSession",
            ],
            Effect: "Allow",
            Principal: {
                Service: "eks.amazonaws.com",
            },
        }],
    }),
});
const clusterAmazonEKSClusterPolicy = new aws.iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
    role: cluster.name,
});
const example = new aws.eks.Cluster("example", {
    name: "example",
    accessConfig: {
        authenticationMode: "API",
    },
    roleArn: cluster.arn,
    version: "1.31",
    remoteNetworkConfig: {
        remoteNodeNetworks: {
            cidrs: ["172.16.0.0/18"],
        },
        remotePodNetworks: {
            cidrs: ["172.16.64.0/18"],
        },
    },
    vpcConfig: {
        endpointPrivateAccess: true,
        endpointPublicAccess: true,
        subnetIds: [
            az1.id,
            az2.id,
            az3.id,
        ],
    },
}, {
    dependsOn: [clusterAmazonEKSClusterPolicy],
});
import pulumi
import json
import pulumi_aws as aws

cluster = aws.iam.Role("cluster",
    name="eks-cluster-example",
    assume_role_policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Action": [
                "sts:AssumeRole",
                "sts:TagSession",
            ],
            "Effect": "Allow",
            "Principal": {
                "Service": "eks.amazonaws.com",
            },
        }],
    }))
cluster_amazon_eks_cluster_policy = aws.iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
    role=cluster.name)
example = aws.eks.Cluster("example",
    name="example",
    access_config={
        "authentication_mode": "API",
    },
    role_arn=cluster.arn,
    version="1.31",
    remote_network_config={
        "remote_node_networks": {
            "cidrs": ["172.16.0.0/18"],
        },
        "remote_pod_networks": {
            "cidrs": ["172.16.64.0/18"],
        },
    },
    vpc_config={
        "endpoint_private_access": True,
        "endpoint_public_access": True,
        "subnet_ids": [
            az1["id"],
            az2["id"],
            az3["id"],
        ],
    },
    opts = pulumi.ResourceOptions(depends_on=[cluster_amazon_eks_cluster_policy]))
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/eks"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"Version": "2012-10-17",
			"Statement": []map[string]interface{}{
				map[string]interface{}{
					"Action": []string{
						"sts:AssumeRole",
						"sts:TagSession",
					},
					"Effect": "Allow",
					"Principal": map[string]interface{}{
						"Service": "eks.amazonaws.com",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		cluster, err := iam.NewRole(ctx, "cluster", &iam.RoleArgs{
			Name:             pulumi.String("eks-cluster-example"),
			AssumeRolePolicy: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		clusterAmazonEKSClusterPolicy, err := iam.NewRolePolicyAttachment(ctx, "cluster_AmazonEKSClusterPolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"),
			Role:      cluster.Name,
		})
		if err != nil {
			return err
		}
		_, err = eks.NewCluster(ctx, "example", &eks.ClusterArgs{
			Name: pulumi.String("example"),
			AccessConfig: &eks.ClusterAccessConfigArgs{
				AuthenticationMode: pulumi.String("API"),
			},
			RoleArn: cluster.Arn,
			Version: pulumi.String("1.31"),
			RemoteNetworkConfig: &eks.ClusterRemoteNetworkConfigArgs{
				RemoteNodeNetworks: &eks.ClusterRemoteNetworkConfigRemoteNodeNetworksArgs{
					Cidrs: pulumi.StringArray{
						pulumi.String("172.16.0.0/18"),
					},
				},
				RemotePodNetworks: &eks.ClusterRemoteNetworkConfigRemotePodNetworksArgs{
					Cidrs: pulumi.StringArray{
						pulumi.String("172.16.64.0/18"),
					},
				},
			},
			VpcConfig: &eks.ClusterVpcConfigArgs{
				EndpointPrivateAccess: pulumi.Bool(true),
				EndpointPublicAccess:  pulumi.Bool(true),
				SubnetIds: pulumi.StringArray{
					az1.Id,
					az2.Id,
					az3.Id,
				},
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			clusterAmazonEKSClusterPolicy,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var cluster = new Aws.Iam.Role("cluster", new()
    {
        Name = "eks-cluster-example",
        AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Action"] = new[]
                    {
                        "sts:AssumeRole",
                        "sts:TagSession",
                    },
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "eks.amazonaws.com",
                    },
                },
            },
        }),
    });

    var clusterAmazonEKSClusterPolicy = new Aws.Iam.RolePolicyAttachment("cluster_AmazonEKSClusterPolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
        Role = cluster.Name,
    });

    var example = new Aws.Eks.Cluster("example", new()
    {
        Name = "example",
        AccessConfig = new Aws.Eks.Inputs.ClusterAccessConfigArgs
        {
            AuthenticationMode = "API",
        },
        RoleArn = cluster.Arn,
        Version = "1.31",
        RemoteNetworkConfig = new Aws.Eks.Inputs.ClusterRemoteNetworkConfigArgs
        {
            RemoteNodeNetworks = new Aws.Eks.Inputs.ClusterRemoteNetworkConfigRemoteNodeNetworksArgs
            {
                Cidrs = new[]
                {
                    "172.16.0.0/18",
                },
            },
            RemotePodNetworks = new Aws.Eks.Inputs.ClusterRemoteNetworkConfigRemotePodNetworksArgs
            {
                Cidrs = new[]
                {
                    "172.16.64.0/18",
                },
            },
        },
        VpcConfig = new Aws.Eks.Inputs.ClusterVpcConfigArgs
        {
            EndpointPrivateAccess = true,
            EndpointPublicAccess = true,
            SubnetIds = new[]
            {
                az1.Id,
                az2.Id,
                az3.Id,
            },
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            clusterAmazonEKSClusterPolicy,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.RolePolicyAttachment;
import com.pulumi.aws.iam.RolePolicyAttachmentArgs;
import com.pulumi.aws.eks.Cluster;
import com.pulumi.aws.eks.ClusterArgs;
import com.pulumi.aws.eks.inputs.ClusterAccessConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterRemoteNetworkConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterRemoteNetworkConfigRemoteNodeNetworksArgs;
import com.pulumi.aws.eks.inputs.ClusterRemoteNetworkConfigRemotePodNetworksArgs;
import com.pulumi.aws.eks.inputs.ClusterVpcConfigArgs;
import static com.pulumi.codegen.internal.Serialization.*;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var cluster = new Role("cluster", RoleArgs.builder()
            .name("eks-cluster-example")
            .assumeRolePolicy(serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Statement", jsonArray(jsonObject(
                        jsonProperty("Action", jsonArray(
                            "sts:AssumeRole", 
                            "sts:TagSession"
                        )),
                        jsonProperty("Effect", "Allow"),
                        jsonProperty("Principal", jsonObject(
                            jsonProperty("Service", "eks.amazonaws.com")
                        ))
                    )))
                )))
            .build());

        var clusterAmazonEKSClusterPolicy = new RolePolicyAttachment("clusterAmazonEKSClusterPolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSClusterPolicy")
            .role(cluster.name())
            .build());

        var example = new Cluster("example", ClusterArgs.builder()
            .name("example")
            .accessConfig(ClusterAccessConfigArgs.builder()
                .authenticationMode("API")
                .build())
            .roleArn(cluster.arn())
            .version("1.31")
            .remoteNetworkConfig(ClusterRemoteNetworkConfigArgs.builder()
                .remoteNodeNetworks(ClusterRemoteNetworkConfigRemoteNodeNetworksArgs.builder()
                    .cidrs("172.16.0.0/18")
                    .build())
                .remotePodNetworks(ClusterRemoteNetworkConfigRemotePodNetworksArgs.builder()
                    .cidrs("172.16.64.0/18")
                    .build())
                .build())
            .vpcConfig(ClusterVpcConfigArgs.builder()
                .endpointPrivateAccess(true)
                .endpointPublicAccess(true)
                .subnetIds(                
                    az1.id(),
                    az2.id(),
                    az3.id())
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(clusterAmazonEKSClusterPolicy)
                .build());

    }
}
resources:
  example:
    type: aws:eks:Cluster
    properties:
      name: example
      accessConfig:
        authenticationMode: API
      roleArn: ${cluster.arn}
      version: '1.31'
      remoteNetworkConfig:
        remoteNodeNetworks:
          cidrs:
            - 172.16.0.0/18
        remotePodNetworks:
          cidrs:
            - 172.16.64.0/18
      vpcConfig:
        endpointPrivateAccess: true
        endpointPublicAccess: true
        subnetIds:
          - ${az1.id}
          - ${az2.id}
          - ${az3.id}
    options:
      dependsOn:
        - ${clusterAmazonEKSClusterPolicy}
  cluster:
    type: aws:iam:Role
    properties:
      name: eks-cluster-example
      assumeRolePolicy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Action:
                - sts:AssumeRole
                - sts:TagSession
              Effect: Allow
              Principal:
                Service: eks.amazonaws.com
  clusterAmazonEKSClusterPolicy:
    type: aws:iam:RolePolicyAttachment
    name: cluster_AmazonEKSClusterPolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
      role: ${cluster.name}

The remoteNetworkConfig defines CIDR ranges for nodes and pods running outside AWS. The remoteNodeNetworks.cidrs specifies where hybrid nodes will run; remotePodNetworks.cidrs defines the pod network. These ranges must not overlap with your VPC CIDR or each other.

Deploy a local cluster on AWS Outposts

AWS Outposts runs the EKS control plane on-premises rather than in an AWS region, supporting low-latency or data residency requirements.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = aws.outposts.getOutpost({
    name: "example",
});
const cluster = new aws.iam.Role("cluster", {
    name: "eks-cluster-example",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: [
                "sts:AssumeRole",
                "sts:TagSession",
            ],
            Effect: "Allow",
            Principal: {
                Service: [
                    "eks.amazonaws.com",
                    "ec2.amazonaws.com",
                ],
            },
        }],
    }),
});
const clusterAmazonEKSLocalOutpostClusterPolicy = new aws.iam.RolePolicyAttachment("cluster_AmazonEKSLocalOutpostClusterPolicy", {
    policyArn: "arn:aws:iam::aws:policy/AmazonEKSLocalOutpostClusterPolicy",
    role: cluster.name,
});
const exampleCluster = new aws.eks.Cluster("example", {
    name: "example",
    accessConfig: {
        authenticationMode: "CONFIG_MAP",
    },
    roleArn: cluster.arn,
    version: "1.31",
    vpcConfig: {
        endpointPrivateAccess: true,
        endpointPublicAccess: false,
        subnetIds: [
            az1.id,
            az2.id,
            az3.id,
        ],
    },
    outpostConfig: {
        controlPlaneInstanceType: "m5.large",
        outpostArns: [example.then(example => example.arn)],
    },
}, {
    dependsOn: [clusterAmazonEKSLocalOutpostClusterPolicy],
});
import pulumi
import json
import pulumi_aws as aws

example = aws.outposts.get_outpost(name="example")
cluster = aws.iam.Role("cluster",
    name="eks-cluster-example",
    assume_role_policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Action": [
                "sts:AssumeRole",
                "sts:TagSession",
            ],
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "eks.amazonaws.com",
                    "ec2.amazonaws.com",
                ],
            },
        }],
    }))
cluster_amazon_eks_local_outpost_cluster_policy = aws.iam.RolePolicyAttachment("cluster_AmazonEKSLocalOutpostClusterPolicy",
    policy_arn="arn:aws:iam::aws:policy/AmazonEKSLocalOutpostClusterPolicy",
    role=cluster.name)
example_cluster = aws.eks.Cluster("example",
    name="example",
    access_config={
        "authentication_mode": "CONFIG_MAP",
    },
    role_arn=cluster.arn,
    version="1.31",
    vpc_config={
        "endpoint_private_access": True,
        "endpoint_public_access": False,
        "subnet_ids": [
            az1["id"],
            az2["id"],
            az3["id"],
        ],
    },
    outpost_config={
        "control_plane_instance_type": "m5.large",
        "outpost_arns": [example.arn],
    },
    opts = pulumi.ResourceOptions(depends_on=[cluster_amazon_eks_local_outpost_cluster_policy]))
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/eks"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/outposts"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := outposts.GetOutpost(ctx, &outposts.GetOutpostArgs{
			Name: pulumi.StringRef("example"),
		}, nil)
		if err != nil {
			return err
		}
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"Version": "2012-10-17",
			"Statement": []map[string]interface{}{
				map[string]interface{}{
					"Action": []string{
						"sts:AssumeRole",
						"sts:TagSession",
					},
					"Effect": "Allow",
					"Principal": map[string]interface{}{
						"Service": []string{
							"eks.amazonaws.com",
							"ec2.amazonaws.com",
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		cluster, err := iam.NewRole(ctx, "cluster", &iam.RoleArgs{
			Name:             pulumi.String("eks-cluster-example"),
			AssumeRolePolicy: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		clusterAmazonEKSLocalOutpostClusterPolicy, err := iam.NewRolePolicyAttachment(ctx, "cluster_AmazonEKSLocalOutpostClusterPolicy", &iam.RolePolicyAttachmentArgs{
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/AmazonEKSLocalOutpostClusterPolicy"),
			Role:      cluster.Name,
		})
		if err != nil {
			return err
		}
		_, err = eks.NewCluster(ctx, "example", &eks.ClusterArgs{
			Name: pulumi.String("example"),
			AccessConfig: &eks.ClusterAccessConfigArgs{
				AuthenticationMode: pulumi.String("CONFIG_MAP"),
			},
			RoleArn: cluster.Arn,
			Version: pulumi.String("1.31"),
			VpcConfig: &eks.ClusterVpcConfigArgs{
				EndpointPrivateAccess: pulumi.Bool(true),
				EndpointPublicAccess:  pulumi.Bool(false),
				SubnetIds: pulumi.StringArray{
					az1.Id,
					az2.Id,
					az3.Id,
				},
			},
			OutpostConfig: &eks.ClusterOutpostConfigArgs{
				ControlPlaneInstanceType: pulumi.String("m5.large"),
				OutpostArns: pulumi.StringArray{
					pulumi.String(example.Arn),
				},
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			clusterAmazonEKSLocalOutpostClusterPolicy,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = Aws.Outposts.GetOutpost.Invoke(new()
    {
        Name = "example",
    });

    var cluster = new Aws.Iam.Role("cluster", new()
    {
        Name = "eks-cluster-example",
        AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Action"] = new[]
                    {
                        "sts:AssumeRole",
                        "sts:TagSession",
                    },
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = new[]
                        {
                            "eks.amazonaws.com",
                            "ec2.amazonaws.com",
                        },
                    },
                },
            },
        }),
    });

    var clusterAmazonEKSLocalOutpostClusterPolicy = new Aws.Iam.RolePolicyAttachment("cluster_AmazonEKSLocalOutpostClusterPolicy", new()
    {
        PolicyArn = "arn:aws:iam::aws:policy/AmazonEKSLocalOutpostClusterPolicy",
        Role = cluster.Name,
    });

    var exampleCluster = new Aws.Eks.Cluster("example", new()
    {
        Name = "example",
        AccessConfig = new Aws.Eks.Inputs.ClusterAccessConfigArgs
        {
            AuthenticationMode = "CONFIG_MAP",
        },
        RoleArn = cluster.Arn,
        Version = "1.31",
        VpcConfig = new Aws.Eks.Inputs.ClusterVpcConfigArgs
        {
            EndpointPrivateAccess = true,
            EndpointPublicAccess = false,
            SubnetIds = new[]
            {
                az1.Id,
                az2.Id,
                az3.Id,
            },
        },
        OutpostConfig = new Aws.Eks.Inputs.ClusterOutpostConfigArgs
        {
            ControlPlaneInstanceType = "m5.large",
            OutpostArns = new[]
            {
                example.Apply(getOutpostResult => getOutpostResult.Arn),
            },
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            clusterAmazonEKSLocalOutpostClusterPolicy,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.outposts.OutpostsFunctions;
import com.pulumi.aws.outposts.inputs.GetOutpostArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.RolePolicyAttachment;
import com.pulumi.aws.iam.RolePolicyAttachmentArgs;
import com.pulumi.aws.eks.Cluster;
import com.pulumi.aws.eks.ClusterArgs;
import com.pulumi.aws.eks.inputs.ClusterAccessConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterVpcConfigArgs;
import com.pulumi.aws.eks.inputs.ClusterOutpostConfigArgs;
import static com.pulumi.codegen.internal.Serialization.*;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var example = OutpostsFunctions.getOutpost(GetOutpostArgs.builder()
            .name("example")
            .build());

        var cluster = new Role("cluster", RoleArgs.builder()
            .name("eks-cluster-example")
            .assumeRolePolicy(serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Statement", jsonArray(jsonObject(
                        jsonProperty("Action", jsonArray(
                            "sts:AssumeRole", 
                            "sts:TagSession"
                        )),
                        jsonProperty("Effect", "Allow"),
                        jsonProperty("Principal", jsonObject(
                            jsonProperty("Service", jsonArray(
                                "eks.amazonaws.com", 
                                "ec2.amazonaws.com"
                            ))
                        ))
                    )))
                )))
            .build());

        var clusterAmazonEKSLocalOutpostClusterPolicy = new RolePolicyAttachment("clusterAmazonEKSLocalOutpostClusterPolicy", RolePolicyAttachmentArgs.builder()
            .policyArn("arn:aws:iam::aws:policy/AmazonEKSLocalOutpostClusterPolicy")
            .role(cluster.name())
            .build());

        var exampleCluster = new Cluster("exampleCluster", ClusterArgs.builder()
            .name("example")
            .accessConfig(ClusterAccessConfigArgs.builder()
                .authenticationMode("CONFIG_MAP")
                .build())
            .roleArn(cluster.arn())
            .version("1.31")
            .vpcConfig(ClusterVpcConfigArgs.builder()
                .endpointPrivateAccess(true)
                .endpointPublicAccess(false)
                .subnetIds(                
                    az1.id(),
                    az2.id(),
                    az3.id())
                .build())
            .outpostConfig(ClusterOutpostConfigArgs.builder()
                .controlPlaneInstanceType("m5.large")
                .outpostArns(example.arn())
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(clusterAmazonEKSLocalOutpostClusterPolicy)
                .build());

    }
}
resources:
  exampleCluster:
    type: aws:eks:Cluster
    name: example
    properties:
      name: example
      accessConfig:
        authenticationMode: CONFIG_MAP
      roleArn: ${cluster.arn}
      version: '1.31'
      vpcConfig:
        endpointPrivateAccess: true
        endpointPublicAccess: false
        subnetIds:
          - ${az1.id}
          - ${az2.id}
          - ${az3.id}
      outpostConfig:
        controlPlaneInstanceType: m5.large
        outpostArns:
          - ${example.arn}
    options:
      dependsOn:
        - ${clusterAmazonEKSLocalOutpostClusterPolicy}
  cluster:
    type: aws:iam:Role
    properties:
      name: eks-cluster-example
      assumeRolePolicy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Action:
                - sts:AssumeRole
                - sts:TagSession
              Effect: Allow
              Principal:
                Service:
                  - eks.amazonaws.com
                  - ec2.amazonaws.com
  clusterAmazonEKSLocalOutpostClusterPolicy:
    type: aws:iam:RolePolicyAttachment
    name: cluster_AmazonEKSLocalOutpostClusterPolicy
    properties:
      policyArn: arn:aws:iam::aws:policy/AmazonEKSLocalOutpostClusterPolicy
      role: ${cluster.name}
variables:
  example:
    fn::invoke:
      function: aws:outposts:getOutpost
      arguments:
        name: example

The outpostConfig.controlPlaneInstanceType specifies the EC2 instance type for control plane nodes running on your Outpost. The outpostArns identifies which Outpost hosts the cluster. Outpost clusters require endpointPrivateAccess set to true and endpointPublicAccess set to false, since the control plane runs locally. The authenticationMode must be “CONFIG_MAP” for Outpost deployments.

Beyond these examples

These snippets focus on specific cluster-level features: standard clusters with API authentication, EKS Auto Mode for automated infrastructure, and hybrid nodes and Outposts deployment. They’re intentionally minimal rather than full Kubernetes platforms.

The examples reference pre-existing infrastructure such as VPC subnets for cluster networking, IAM roles and policies (created inline but requiring adaptation), and AWS Outposts infrastructure for local clusters. They focus on configuring the control plane rather than provisioning everything around it.

To keep things focused, common cluster patterns are omitted, including:

  • Control plane logging (enabledClusterLogTypes)
  • Encryption configuration (encryptionConfig)
  • Deletion protection (deletionProtection)
  • Zonal shift configuration (zonalShiftConfig)
  • Node group provisioning (separate resource)
  • Add-on management (separate resources)

These omissions are intentional: the goal is to illustrate how each cluster feature is wired, not provide drop-in Kubernetes platforms. See the EKS Cluster resource reference for all available configuration options.

Let's deploy AWS EKS Clusters

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

IAM & Permissions
Why can't EKS delete my cluster's security groups?
EKS cannot delete managed EC2 infrastructure like security groups if IAM role policies aren’t properly configured. Add explicit dependsOn to your cluster resource referencing aws.iam.RolePolicy or aws.iam.RolePolicyAttachment resources.
How do I set up IAM roles for an EKS cluster?
Create an IAM role with a trust policy for eks.amazonaws.com, attach AmazonEKSClusterPolicy, and use dependsOn to ensure policies are attached before cluster creation.
What IAM policies are required for EKS Auto Mode?
Attach five policies to the cluster role: AmazonEKSClusterPolicy, AmazonEKSComputePolicy, AmazonEKSBlockStoragePolicy, AmazonEKSLoadBalancingPolicy, and AmazonEKSNetworkingPolicy.
EKS Auto Mode
What's required to enable EKS Auto Mode?
You must set three flags together: computeConfig.enabled, kubernetesNetworkConfig.elasticLoadBalancing.enabled, and storageConfig.blockStorage.enabled all to true. Additionally, set bootstrapSelfManagedAddons to false. To disable Auto Mode, set all three flags to false.
What happens if I change bootstrapSelfManagedAddons?
Changing bootstrapSelfManagedAddons forces a new cluster to be created because it’s immutable. Set this value correctly during initial cluster creation.
Cluster Configuration
What are the naming rules for EKS clusters?
Cluster names must be 1-100 characters, begin with an alphanumeric character, and contain only alphanumeric characters, dashes, and underscores (pattern: ^[0-9A-Za-z][A-Za-z0-9\-_]*$).
How does EKS cluster version management work?
You must manually configure and increase the version property to upgrade. If not specified, the latest available version is used at creation with no automatic upgrades. Downgrades are not supported by EKS.
What cluster properties are immutable?
Three properties cannot be changed after creation: name, roleArn, and bootstrapSelfManagedAddons. Changing any of these forces a new cluster to be created.
How do I enable deletion protection?
Set deletionProtection to true. When enabled, the cluster cannot be deleted unless deletion protection is first disabled. It defaults to false.
Networking & VPC
What VPC requirements does EKS have?
EKS VPC resources have specific requirements for Kubernetes. Configure vpcConfig with subnet IDs and review the Cluster VPC Considerations and Cluster Security Group Considerations documentation.
How do I configure EKS Hybrid Nodes?
Set remoteNetworkConfig with remoteNodeNetworks and remotePodNetworks CIDRs for your hybrid infrastructure.
Advanced Configurations
Can I create an EKS cluster on AWS Outpost?
Yes, use outpostConfig with controlPlaneInstanceType and outpostArns. Set endpointPublicAccess to false and endpointPrivateAccess to true.

Using a different cloud?

Explore containers guides for other cloud providers: