The aws:eks/addon:Addon resource, part of the Pulumi AWS provider, installs and manages AWS-provided add-ons that extend EKS cluster functionality. This guide focuses on three capabilities: installing add-ons with version control, preserving custom configuration during upgrades, and passing JSON configuration schemas.
Add-ons require an existing EKS cluster and may reference IAM roles or OIDC providers for service account permissions. The examples are intentionally small. Combine them with your own cluster infrastructure and IAM configuration.
Install a basic add-on to an existing cluster
Most clusters start by installing core add-ons like vpc-cni to provide networking capabilities.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.eks.Addon("example", {
clusterName: exampleAwsEksCluster.name,
addonName: "vpc-cni",
});
import pulumi
import pulumi_aws as aws
example = aws.eks.Addon("example",
cluster_name=example_aws_eks_cluster["name"],
addon_name="vpc-cni")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/eks"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := eks.NewAddon(ctx, "example", &eks.AddonArgs{
ClusterName: pulumi.Any(exampleAwsEksCluster.Name),
AddonName: pulumi.String("vpc-cni"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.Eks.Addon("example", new()
{
ClusterName = exampleAwsEksCluster.Name,
AddonName = "vpc-cni",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.eks.Addon;
import com.pulumi.aws.eks.AddonArgs;
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 example = new Addon("example", AddonArgs.builder()
.clusterName(exampleAwsEksCluster.name())
.addonName("vpc-cni")
.build());
}
}
resources:
example:
type: aws:eks:Addon
properties:
clusterName: ${exampleAwsEksCluster.name}
addonName: vpc-cni
The clusterName property references your EKS cluster, and addonName specifies which AWS-managed component to install. Without specifying addonVersion, AWS installs the default version for your cluster’s Kubernetes version.
Upgrade add-ons while preserving custom configuration
When you customize add-on behavior using kubectl after installation, you need to control whether AWS overwrites those changes during upgrades.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.eks.Addon("example", {
clusterName: exampleAwsEksCluster.name,
addonName: "coredns",
addonVersion: "v1.10.1-eksbuild.1",
resolveConflictsOnUpdate: "PRESERVE",
});
import pulumi
import pulumi_aws as aws
example = aws.eks.Addon("example",
cluster_name=example_aws_eks_cluster["name"],
addon_name="coredns",
addon_version="v1.10.1-eksbuild.1",
resolve_conflicts_on_update="PRESERVE")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/eks"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := eks.NewAddon(ctx, "example", &eks.AddonArgs{
ClusterName: pulumi.Any(exampleAwsEksCluster.Name),
AddonName: pulumi.String("coredns"),
AddonVersion: pulumi.String("v1.10.1-eksbuild.1"),
ResolveConflictsOnUpdate: pulumi.String("PRESERVE"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.Eks.Addon("example", new()
{
ClusterName = exampleAwsEksCluster.Name,
AddonName = "coredns",
AddonVersion = "v1.10.1-eksbuild.1",
ResolveConflictsOnUpdate = "PRESERVE",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.eks.Addon;
import com.pulumi.aws.eks.AddonArgs;
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 example = new Addon("example", AddonArgs.builder()
.clusterName(exampleAwsEksCluster.name())
.addonName("coredns")
.addonVersion("v1.10.1-eksbuild.1")
.resolveConflictsOnUpdate("PRESERVE")
.build());
}
}
resources:
example:
type: aws:eks:Addon
properties:
clusterName: ${exampleAwsEksCluster.name}
addonName: coredns
addonVersion: v1.10.1-eksbuild.1
resolveConflictsOnUpdate: PRESERVE
The addonVersion property pins the add-on to a specific release. The resolveConflictsOnUpdate property with PRESERVE tells AWS to keep your kubectl-applied changes rather than overwriting them with add-on defaults. This lets you upgrade the add-on version without losing customizations.
Configure add-on behavior with custom JSON settings
Add-ons expose configuration schemas that control replica counts, resource limits, and other runtime behavior.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.eks.Addon("example", {
clusterName: "mycluster",
addonName: "coredns",
addonVersion: "v1.10.1-eksbuild.1",
resolveConflictsOnCreate: "OVERWRITE",
configurationValues: JSON.stringify({
replicaCount: 4,
resources: {
limits: {
cpu: "100m",
memory: "150Mi",
},
requests: {
cpu: "100m",
memory: "150Mi",
},
},
}),
});
import pulumi
import json
import pulumi_aws as aws
example = aws.eks.Addon("example",
cluster_name="mycluster",
addon_name="coredns",
addon_version="v1.10.1-eksbuild.1",
resolve_conflicts_on_create="OVERWRITE",
configuration_values=json.dumps({
"replicaCount": 4,
"resources": {
"limits": {
"cpu": "100m",
"memory": "150Mi",
},
"requests": {
"cpu": "100m",
"memory": "150Mi",
},
},
}))
package main
import (
"encoding/json"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/eks"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
tmpJSON0, err := json.Marshal(map[string]interface{}{
"replicaCount": 4,
"resources": map[string]interface{}{
"limits": map[string]interface{}{
"cpu": "100m",
"memory": "150Mi",
},
"requests": map[string]interface{}{
"cpu": "100m",
"memory": "150Mi",
},
},
})
if err != nil {
return err
}
json0 := string(tmpJSON0)
_, err = eks.NewAddon(ctx, "example", &eks.AddonArgs{
ClusterName: pulumi.String("mycluster"),
AddonName: pulumi.String("coredns"),
AddonVersion: pulumi.String("v1.10.1-eksbuild.1"),
ResolveConflictsOnCreate: pulumi.String("OVERWRITE"),
ConfigurationValues: pulumi.String(json0),
})
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 = new Aws.Eks.Addon("example", new()
{
ClusterName = "mycluster",
AddonName = "coredns",
AddonVersion = "v1.10.1-eksbuild.1",
ResolveConflictsOnCreate = "OVERWRITE",
ConfigurationValues = JsonSerializer.Serialize(new Dictionary<string, object?>
{
["replicaCount"] = 4,
["resources"] = new Dictionary<string, object?>
{
["limits"] = new Dictionary<string, object?>
{
["cpu"] = "100m",
["memory"] = "150Mi",
},
["requests"] = new Dictionary<string, object?>
{
["cpu"] = "100m",
["memory"] = "150Mi",
},
},
}),
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.eks.Addon;
import com.pulumi.aws.eks.AddonArgs;
import static com.pulumi.codegen.internal.Serialization.*;
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 example = new Addon("example", AddonArgs.builder()
.clusterName("mycluster")
.addonName("coredns")
.addonVersion("v1.10.1-eksbuild.1")
.resolveConflictsOnCreate("OVERWRITE")
.configurationValues(serializeJson(
jsonObject(
jsonProperty("replicaCount", 4),
jsonProperty("resources", jsonObject(
jsonProperty("limits", jsonObject(
jsonProperty("cpu", "100m"),
jsonProperty("memory", "150Mi")
)),
jsonProperty("requests", jsonObject(
jsonProperty("cpu", "100m"),
jsonProperty("memory", "150Mi")
))
))
)))
.build());
}
}
resources:
example:
type: aws:eks:Addon
properties:
clusterName: mycluster
addonName: coredns
addonVersion: v1.10.1-eksbuild.1
resolveConflictsOnCreate: OVERWRITE
configurationValues:
fn::toJSON:
replicaCount: 4
resources:
limits:
cpu: 100m
memory: 150Mi
requests:
cpu: 100m
memory: 150Mi
The configurationValues property accepts a JSON string that matches the add-on’s schema. You can extract each add-on’s schema using the AWS CLI’s describe-addon-configuration command. Here, the JSON sets CoreDNS to run 4 replicas with specific CPU and memory limits. The resolveConflictsOnCreate property with OVERWRITE ensures your configuration takes precedence over any existing settings.
Beyond these examples
These snippets focus on specific add-on features: installation and version management, conflict resolution during updates, and custom configuration via JSON schemas. They’re intentionally minimal rather than full cluster deployments.
The examples reference pre-existing infrastructure such as EKS clusters, and IAM OIDC providers for serviceAccountRoleArn (not shown in examples). They focus on configuring the add-on rather than provisioning the cluster around it.
To keep things focused, common add-on patterns are omitted, including:
- IAM role binding (serviceAccountRoleArn)
- Pod Identity associations (podIdentityAssociations)
- Preservation on deletion (preserve property)
- Conflict resolution on creation (resolveConflictsOnCreate)
These omissions are intentional: the goal is to illustrate how each add-on feature is wired, not provide drop-in cluster modules. See the EKS Addon resource reference for all available configuration options.
Let's manage AWS EKS Add-ons
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Conflicts
resolveConflictsOnUpdate to PRESERVE to keep configuration changes applied with kubectl during version upgrades.resolveConflictsOnCreate handles conflicts when migrating a self-managed add-on (valid values: NONE, OVERWRITE), while resolveConflictsOnUpdate handles conflicts during add-on updates (valid values: NONE, OVERWRITE, PRESERVE).aws eks describe-addon-configuration --addon-name <name> --addon-version <version> to extract the schema for your specific add-on and version.IAM & Permissions
serviceAccountRoleArn.Versioning & Updates
describe-addon-versions to list available versions for each add-on.configurationValues JSON string must match the valid JSON schema for your specific add-on and version, or the operation will fail.Resource Management
addonName and clusterName are immutable and cannot be changed after creation.preserve to true to retain the created resources when deleting the EKS add-on.Using a different cloud?
Explore containers guides for other cloud providers: