diff --git a/examples/38-cluster-subnets-sgs.yaml b/examples/38-cluster-subnets-sgs.yaml
new file mode 100644
index 0000000000..67e1b8202a
--- /dev/null
+++ b/examples/38-cluster-subnets-sgs.yaml
@@ -0,0 +1,22 @@
+# An example config for updating API server endpoint access, public access CIDRs, and control plane subnets and security groups.
+# To perform the update, run `eksctl utils update-cluster-vpc-config -f 38-cluster-subnets-sgs.yaml`
+
+apiVersion: eksctl.io/v1alpha5
+kind: ClusterConfig
+metadata:
+ name: cluster-38
+ region: us-west-2
+
+iam:
+ withOIDC: true
+
+vpc:
+ controlPlaneSubnetIDs: [subnet-1234, subnet-5678]
+ controlPlaneSecurityGroupIDs: [sg-1234, sg-5678]
+ clusterEndpoints:
+ publicAccess: true
+ privateAccess: true
+ publicAccessCIDRs: ["1.1.1.1/32"]
+
+managedNodeGroups:
+ - name: mng1
diff --git a/integration/tests/managed/managed_nodegroup_test.go b/integration/tests/managed/managed_nodegroup_test.go
index 32a4940889..f5fee494c4 100644
--- a/integration/tests/managed/managed_nodegroup_test.go
+++ b/integration/tests/managed/managed_nodegroup_test.go
@@ -6,10 +6,13 @@ package managed
import (
"context"
"fmt"
+ "strings"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/aws/aws-sdk-go-v2/service/ec2"
+ ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
awseks "github.com/aws/aws-sdk-go-v2/service/eks"
harness "github.com/dlespiau/kube-test-harness"
@@ -27,6 +30,7 @@ import (
clusterutils "github.com/weaveworks/eksctl/integration/utilities/cluster"
"github.com/weaveworks/eksctl/integration/utilities/kube"
api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
+ "github.com/weaveworks/eksctl/pkg/awsapi"
"github.com/weaveworks/eksctl/pkg/eks"
"github.com/weaveworks/eksctl/pkg/testutils"
)
@@ -521,6 +525,107 @@ var _ = Describe("(Integration) Create Managed Nodegroups", func() {
Expect(cmd).To(RunSuccessfully())
})
})
+
+ Context("eksctl utils update-cluster-vpc-config", Serial, func() {
+ makeAWSProvider := func(ctx context.Context, clusterConfig *api.ClusterConfig) api.ClusterProvider {
+ clusterProvider, err := eks.New(ctx, &api.ProviderConfig{Region: params.Region}, clusterConfig)
+ Expect(err).NotTo(HaveOccurred())
+ return clusterProvider.AWSProvider
+ }
+ getPrivateSubnetIDs := func(ctx context.Context, ec2API awsapi.EC2, vpcID string) []string {
+ out, err := ec2API.DescribeSubnets(ctx, &ec2.DescribeSubnetsInput{
+ Filters: []ec2types.Filter{
+ {
+ Name: aws.String("vpc-id"),
+ Values: []string{vpcID},
+ },
+ },
+ })
+ Expect(err).NotTo(HaveOccurred())
+ var subnetIDs []string
+ for _, s := range out.Subnets {
+ if !*s.MapPublicIpOnLaunch {
+ subnetIDs = append(subnetIDs, *s.SubnetId)
+ }
+ }
+ return subnetIDs
+ }
+ It("should update the VPC config", func() {
+ clusterConfig := makeClusterConfig()
+ ctx := context.Background()
+ awsProvider := makeAWSProvider(ctx, clusterConfig)
+ cluster, err := awsProvider.EKS().DescribeCluster(ctx, &awseks.DescribeClusterInput{
+ Name: aws.String(params.ClusterName),
+ })
+ Expect(err).NotTo(HaveOccurred(), "error describing cluster")
+ clusterSubnetIDs := getPrivateSubnetIDs(ctx, awsProvider.EC2(), *cluster.Cluster.ResourcesVpcConfig.VpcId)
+ Expect(len(cluster.Cluster.ResourcesVpcConfig.SecurityGroupIds) > 0).To(BeTrue(), "at least one security group ID must be associated with the cluster")
+
+ clusterVPC := &api.ClusterVPC{
+ ClusterEndpoints: &api.ClusterEndpoints{
+ PrivateAccess: api.Enabled(),
+ PublicAccess: api.Enabled(),
+ },
+ PublicAccessCIDRs: []string{"127.0.0.1/32"},
+ ControlPlaneSubnetIDs: clusterSubnetIDs,
+ ControlPlaneSecurityGroupIDs: []string{cluster.Cluster.ResourcesVpcConfig.SecurityGroupIds[0]},
+ }
+ By("accepting CLI options")
+ cmd := params.EksctlUtilsCmd.WithArgs(
+ "update-cluster-vpc-config",
+ "--cluster", params.ClusterName,
+ "--private-access",
+ "--public-access",
+ "--public-access-cidrs", strings.Join(clusterVPC.PublicAccessCIDRs, ","),
+ "--control-plane-subnet-ids", strings.Join(clusterVPC.ControlPlaneSubnetIDs, ","),
+ "--control-plane-security-group-ids", strings.Join(clusterVPC.ControlPlaneSecurityGroupIDs, ","),
+ "-v4",
+ "--approve",
+ ).
+ WithTimeout(45 * time.Minute)
+ session := cmd.Run()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ formatWithClusterAndRegion := func(format string, values ...any) string {
+ return fmt.Sprintf(format, append([]any{params.ClusterName, params.Region}, values...)...)
+ }
+ Expect(strings.Split(string(session.Buffer().Contents()), "\n")).To(ContainElements(
+ ContainSubstring(formatWithClusterAndRegion("control plane subnets and security groups for cluster %q in %q have been updated to: "+
+ "controlPlaneSubnetIDs=%v, controlPlaneSecurityGroupIDs=%v", clusterVPC.ControlPlaneSubnetIDs, clusterVPC.ControlPlaneSecurityGroupIDs)),
+ ContainSubstring(formatWithClusterAndRegion("Kubernetes API endpoint access for cluster %q in %q has been updated to: privateAccess=%v, publicAccess=%v",
+ *clusterVPC.ClusterEndpoints.PrivateAccess, *clusterVPC.ClusterEndpoints.PublicAccess)),
+ ContainSubstring(formatWithClusterAndRegion("public access CIDRs for cluster %q in %q have been updated to: %v", clusterVPC.PublicAccessCIDRs)),
+ ))
+
+ By("accepting a config file")
+ clusterConfig.VPC = clusterVPC
+ cmd = params.EksctlUtilsCmd.WithArgs(
+ "update-cluster-vpc-config",
+ "--config-file", "-",
+ "-v4",
+ "--approve",
+ ).
+ WithoutArg("--region", params.Region).
+ WithStdin(clusterutils.Reader(clusterConfig))
+ session = cmd.Run()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(strings.Split(string(session.Buffer().Contents()), "\n")).To(ContainElements(
+ ContainSubstring(formatWithClusterAndRegion("Kubernetes API endpoint access for cluster %q in %q is already up-to-date")),
+ ContainSubstring(formatWithClusterAndRegion("control plane subnet IDs for cluster %q in %q are already up-to-date")),
+ ContainSubstring(formatWithClusterAndRegion("control plane security group IDs for cluster %q in %q are already up-to-date")),
+ ))
+
+ By("resetting public access CIDRs")
+ cmd = params.EksctlUtilsCmd.WithArgs(
+ "update-cluster-vpc-config",
+ "--cluster", params.ClusterName,
+ "--public-access-cidrs", "0.0.0.0/0",
+ "-v4",
+ "--approve",
+ )
+ Expect(cmd).To(RunSuccessfully())
+ })
+ })
})
var _ = SynchronizedAfterSuite(func() {}, func() {
diff --git a/pkg/apis/eksctl.io/v1alpha5/assets/schema.json b/pkg/apis/eksctl.io/v1alpha5/assets/schema.json
index 122c0a0fde..c21d94e023 100755
--- a/pkg/apis/eksctl.io/v1alpha5/assets/schema.json
+++ b/pkg/apis/eksctl.io/v1alpha5/assets/schema.json
@@ -655,6 +655,22 @@
"description": "See [managing access to API](/usage/vpc-networking/#managing-access-to-the-kubernetes-api-server-endpoints)",
"x-intellij-html-description": "See managing access to API"
},
+ "controlPlaneSecurityGroupIDs": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "description": "configures the security groups for the control plane.",
+ "x-intellij-html-description": "configures the security groups for the control plane."
+ },
+ "controlPlaneSubnetIDs": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "description": "configures the subnets for the control plane.",
+ "x-intellij-html-description": "configures the subnets for the control plane."
+ },
"extraCIDRs": {
"items": {
"type": "string"
@@ -733,7 +749,9 @@
"autoAllocateIPv6",
"nat",
"clusterEndpoints",
- "publicAccessCIDRs"
+ "publicAccessCIDRs",
+ "controlPlaneSubnetIDs",
+ "controlPlaneSecurityGroupIDs"
],
"additionalProperties": false,
"description": "holds global subnet and all child subnets",
diff --git a/pkg/apis/eksctl.io/v1alpha5/validation.go b/pkg/apis/eksctl.io/v1alpha5/validation.go
index fd5d1c5e2f..bd42ffcb32 100644
--- a/pkg/apis/eksctl.io/v1alpha5/validation.go
+++ b/pkg/apis/eksctl.io/v1alpha5/validation.go
@@ -318,6 +318,10 @@ func (c *ClusterConfig) ValidateVPCConfig() error {
c.VPC.ExtraIPv6CIDRs = cidrs
}
+ if c.VPC.SecurityGroup != "" && len(c.VPC.ControlPlaneSecurityGroupIDs) > 0 {
+ return errors.New("only one of vpc.securityGroup and vpc.controlPlaneSecurityGroupIDs can be specified")
+ }
+
if (c.VPC.IPv6Cidr != "" || c.VPC.IPv6Pool != "") && !c.IPv6Enabled() {
return fmt.Errorf("Ipv6Cidr and Ipv6CidrPool are only supported when IPFamily is set to IPv6")
}
diff --git a/pkg/apis/eksctl.io/v1alpha5/validation_test.go b/pkg/apis/eksctl.io/v1alpha5/validation_test.go
index ce3abd67d8..d835aeb5a4 100644
--- a/pkg/apis/eksctl.io/v1alpha5/validation_test.go
+++ b/pkg/apis/eksctl.io/v1alpha5/validation_test.go
@@ -1332,6 +1332,41 @@ var _ = Describe("ClusterConfig validation", func() {
})
})
+
+ type vpcSecurityGroupEntry struct {
+ updateVPC func(*api.ClusterVPC)
+ expectedErr string
+ }
+ DescribeTable("vpc.securityGroup and vpc.controlPlaneSecurityGroupIDs", func(e vpcSecurityGroupEntry) {
+ e.updateVPC(cfg.VPC)
+ err := cfg.ValidateVPCConfig()
+ if e.expectedErr != "" {
+ Expect(err).To(MatchError(ContainSubstring(e.expectedErr)))
+ } else {
+ Expect(err).NotTo(HaveOccurred())
+ }
+ },
+ Entry("both set", vpcSecurityGroupEntry{
+ updateVPC: func(v *api.ClusterVPC) {
+ v.SecurityGroup = "sg-1234"
+ v.ControlPlaneSecurityGroupIDs = []string{"sg-1234"}
+ },
+ expectedErr: "only one of vpc.securityGroup and vpc.controlPlaneSecurityGroupIDs can be specified",
+ }),
+ Entry("vpc.securityGroup set", vpcSecurityGroupEntry{
+ updateVPC: func(v *api.ClusterVPC) {
+ v.SecurityGroup = "sg-1234"
+ },
+ }),
+ Entry("vpc.controlPlaneSecurityGroupIDs set", vpcSecurityGroupEntry{
+ updateVPC: func(v *api.ClusterVPC) {
+ v.ControlPlaneSecurityGroupIDs = []string{"sg-1234"}
+ },
+ }),
+ Entry("neither set", vpcSecurityGroupEntry{
+ updateVPC: func(v *api.ClusterVPC) {},
+ }),
+ )
})
Describe("ValidatePrivateCluster", func() {
diff --git a/pkg/apis/eksctl.io/v1alpha5/vpc.go b/pkg/apis/eksctl.io/v1alpha5/vpc.go
index 3ed1ccdb8d..14418ea4fa 100644
--- a/pkg/apis/eksctl.io/v1alpha5/vpc.go
+++ b/pkg/apis/eksctl.io/v1alpha5/vpc.go
@@ -175,6 +175,12 @@ type (
// k8s API endpoint
// +optional
PublicAccessCIDRs []string `json:"publicAccessCIDRs,omitempty"`
+ // ControlPlaneSubnetIDs configures the subnets for the control plane.
+ // +optional
+ ControlPlaneSubnetIDs []string `json:"controlPlaneSubnetIDs,omitempty"`
+ // ControlPlaneSecurityGroupIDs configures the security groups for the control plane.
+ // +optional
+ ControlPlaneSecurityGroupIDs []string `json:"controlPlaneSecurityGroupIDs,omitempty"`
}
// ClusterSubnets holds private and public subnets
ClusterSubnets struct {
diff --git a/pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go b/pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go
index 90ec1476ea..40c5a8092d 100644
--- a/pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go
+++ b/pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go
@@ -724,6 +724,16 @@ func (in *ClusterVPC) DeepCopyInto(out *ClusterVPC) {
*out = make([]string, len(*in))
copy(*out, *in)
}
+ if in.ControlPlaneSubnetIDs != nil {
+ in, out := &in.ControlPlaneSubnetIDs, &out.ControlPlaneSubnetIDs
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.ControlPlaneSecurityGroupIDs != nil {
+ in, out := &in.ControlPlaneSecurityGroupIDs, &out.ControlPlaneSecurityGroupIDs
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
return
}
diff --git a/pkg/cfn/builder/cluster.go b/pkg/cfn/builder/cluster.go
index aaf0614639..a60ff70017 100644
--- a/pkg/cfn/builder/cluster.go
+++ b/pkg/cfn/builder/cluster.go
@@ -27,7 +27,7 @@ type ClusterResourceSet struct {
ec2API awsapi.EC2
region string
vpcResourceSet VPCResourceSet
- securityGroups []*gfnt.Value
+ securityGroups *gfnt.Value
}
// NewClusterResourceSet returns a resource set for the new cluster.
@@ -115,7 +115,13 @@ func (c *ClusterResourceSet) AddAllResources(ctx context.Context) error {
func (c *ClusterResourceSet) addResourcesForSecurityGroups(vpcID *gfnt.Value) *clusterSecurityGroup {
var refControlPlaneSG, refClusterSharedNodeSG *gfnt.Value
- if c.spec.VPC.SecurityGroup == "" {
+ if sg := c.spec.VPC.SecurityGroup; sg != "" {
+ refControlPlaneSG = gfnt.NewString(sg)
+ c.securityGroups = gfnt.NewStringSlice(sg)
+ } else if securityGroupIDs := c.spec.VPC.ControlPlaneSecurityGroupIDs; len(securityGroupIDs) > 0 {
+ refControlPlaneSG = gfnt.NewString(securityGroupIDs[0])
+ c.securityGroups = gfnt.NewStringSlice(securityGroupIDs...)
+ } else {
refControlPlaneSG = c.newResource(cfnControlPlaneSGResource, &gfnec2.SecurityGroup{
GroupDescription: gfnt.NewString("Communication between the control plane and worker nodegroups"),
VpcId: vpcID,
@@ -146,10 +152,8 @@ func (c *ClusterResourceSet) addResourcesForSecurityGroups(vpcID *gfnt.Value) *c
})
}
}
- } else {
- refControlPlaneSG = gfnt.NewString(c.spec.VPC.SecurityGroup)
+ c.securityGroups = gfnt.NewSlice(refControlPlaneSG)
}
- c.securityGroups = []*gfnt.Value{refControlPlaneSG} // only this one SG is passed to EKS API, nodes are isolated
if c.spec.VPC.SharedNodeSecurityGroup == "" {
refClusterSharedNodeSG = c.newResource(cfnSharedNodeSGResource, &gfnec2.SecurityGroup{
@@ -263,12 +267,16 @@ func (c *ClusterResourceSet) newResource(name string, resource gfn.Resource) *gf
func (c *ClusterResourceSet) addResourcesForControlPlane(subnetDetails *SubnetDetails) {
clusterVPC := &gfneks.Cluster_ResourcesVpcConfig{
- SubnetIds: gfnt.NewSlice(subnetDetails.ControlPlaneSubnetRefs()...),
EndpointPublicAccess: gfnt.NewBoolean(*c.spec.VPC.ClusterEndpoints.PublicAccess),
EndpointPrivateAccess: gfnt.NewBoolean(*c.spec.VPC.ClusterEndpoints.PrivateAccess),
- SecurityGroupIds: gfnt.NewSlice(c.securityGroups...),
+ SecurityGroupIds: c.securityGroups,
PublicAccessCidrs: gfnt.NewStringSlice(c.spec.VPC.PublicAccessCIDRs...),
}
+ if subnetIDs := c.spec.VPC.ControlPlaneSubnetIDs; len(subnetIDs) > 0 {
+ clusterVPC.SubnetIds = gfnt.NewStringSlice(subnetIDs...)
+ } else {
+ clusterVPC.SubnetIds = gfnt.NewSlice(subnetDetails.ControlPlaneSubnetRefs()...)
+ }
serviceRoleARN := gfnt.MakeFnGetAttString("ServiceRole", "Arn")
if api.IsSetAndNonEmptyString(c.spec.IAM.ServiceRoleARN) {
diff --git a/pkg/ctl/cmdutils/update_cluster_vpc.go b/pkg/ctl/cmdutils/update_cluster_vpc.go
index 2539278af5..13d546ea44 100644
--- a/pkg/ctl/cmdutils/update_cluster_vpc.go
+++ b/pkg/ctl/cmdutils/update_cluster_vpc.go
@@ -1,7 +1,8 @@
package cmdutils
import (
- "errors"
+ "fmt"
+ "strings"
"github.com/kris-nova/logger"
@@ -16,6 +17,10 @@ type UpdateClusterVPCOptions struct {
PublicAccess bool
// PublicAccessCIDRs configures the public access CIDRs.
PublicAccessCIDRs []string
+ // ControlPlaneSubnetIDs configures the subnets for the control plane.
+ ControlPlaneSubnetIDs []string
+ // ControlPlaneSecurityGroupIDs configures the security group IDs for the control plane.
+ ControlPlaneSecurityGroupIDs []string
}
// NewUpdateClusterVPCLoader will load config or use flags for 'eksctl utils update-cluster-vpc-config'.
@@ -26,6 +31,8 @@ func NewUpdateClusterVPCLoader(cmd *Cmd, options UpdateClusterVPCOptions) Cluste
"private-access",
"public-access",
"public-access-cidrs",
+ "control-plane-subnet-ids",
+ "control-plane-security-group-ids",
}
l.flagsIncompatibleWithConfigFile.Insert(supportedOptions...)
@@ -42,30 +49,35 @@ func NewUpdateClusterVPCLoader(cmd *Cmd, options UpdateClusterVPCOptions) Cluste
}
}
if !hasRequiredOptions {
- return errors.New("at least one of --public-access, --private-access and --public-access-cidrs must be specified")
+ options := make([]string, 0, len(supportedOptions))
+ for _, o := range supportedOptions {
+ options = append(options, "--"+o)
+ }
+ return fmt.Errorf("at least one of these options must be specified: %s", strings.Join(options, ", "))
}
clusterConfig := cmd.ClusterConfig
-
- if clusterConfig.VPC.ClusterEndpoints == nil {
- clusterConfig.VPC.ClusterEndpoints = api.ClusterEndpointAccessDefaults()
- }
if flag := l.CobraCommand.Flag("private-access"); flag != nil && flag.Changed {
- clusterConfig.VPC.ClusterEndpoints.PrivateAccess = &options.PrivateAccess
- } else {
- clusterConfig.VPC.ClusterEndpoints.PrivateAccess = nil
+ clusterConfig.VPC.ClusterEndpoints = &api.ClusterEndpoints{
+ PrivateAccess: &options.PrivateAccess,
+ }
}
-
if flag := l.CobraCommand.Flag("public-access"); flag != nil && flag.Changed {
- clusterConfig.VPC.ClusterEndpoints.PublicAccess = &options.PublicAccess
- } else {
- clusterConfig.VPC.ClusterEndpoints.PublicAccess = nil
+ if clusterConfig.VPC.ClusterEndpoints == nil {
+ clusterConfig.VPC.ClusterEndpoints = &api.ClusterEndpoints{
+ PublicAccess: &options.PublicAccess,
+ }
+ } else {
+ clusterConfig.VPC.ClusterEndpoints.PublicAccess = &options.PublicAccess
+ }
}
clusterConfig.VPC.PublicAccessCIDRs = options.PublicAccessCIDRs
+ clusterConfig.VPC.ControlPlaneSubnetIDs = options.ControlPlaneSubnetIDs
+ clusterConfig.VPC.ControlPlaneSecurityGroupIDs = options.ControlPlaneSecurityGroupIDs
return nil
}
l.validateWithConfigFile = func() error {
- logger.Info("only changes to vpc.clusterEndpoints and vpc.publicAccessCIDRs are updated in the EKS API, changes to any other fields will be ignored")
+ logger.Info("only changes to vpc.clusterEndpoints, vpc.publicAccessCIDRs, vpc.controlPlaneSubnetIDs and vpc.controlPlaneSecurityGroupIDs are updated in the EKS API, changes to any other fields will be ignored")
if l.ClusterConfig.VPC == nil {
l.ClusterConfig.VPC = api.NewClusterVPC(false)
}
diff --git a/pkg/ctl/utils/mocks/VPCConfigUpdater.go b/pkg/ctl/utils/mocks/VPCConfigUpdater.go
index 1cd78980b4..6500898363 100644
--- a/pkg/ctl/utils/mocks/VPCConfigUpdater.go
+++ b/pkg/ctl/utils/mocks/VPCConfigUpdater.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.4. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocks
diff --git a/pkg/ctl/utils/set_public_access_cidrs.go b/pkg/ctl/utils/set_public_access_cidrs.go
index bbd22d8665..7154b6e8ec 100644
--- a/pkg/ctl/utils/set_public_access_cidrs.go
+++ b/pkg/ctl/utils/set_public_access_cidrs.go
@@ -15,6 +15,7 @@ func publicAccessCIDRsCmdWithHandler(cmd *cmdutils.Cmd, handler func(cmd *cmduti
cfg := api.NewClusterConfig()
cmd.ClusterConfig = cfg
+ cmd.CobraCommand.Deprecated = "this command is deprecated and will be removed soon. Use `eksctl utils update-cluster-vpc-config --public-access-cidrs=<> instead."
cmd.SetDescription("set-public-access-cidrs", "Update public access CIDRs", "CIDR blocks that EKS uses to create a security group on the public endpoint")
cmd.CobraCommand.RunE = func(_ *cobra.Command, args []string) error {
@@ -56,6 +57,8 @@ func doUpdatePublicAccessCIDRs(cmd *cmdutils.Cmd) error {
}
cfg.VPC.ClusterEndpoints = nil
+ cfg.VPC.ControlPlaneSubnetIDs = nil
+ cfg.VPC.ControlPlaneSecurityGroupIDs = nil
vpcHelper := &VPCHelper{
VPCUpdater: ctl,
ClusterMeta: cfg.Metadata,
diff --git a/pkg/ctl/utils/update_cluster_endpoint_access.go b/pkg/ctl/utils/update_cluster_endpoint_access.go
index 39655bd5ae..66d39ed649 100644
--- a/pkg/ctl/utils/update_cluster_endpoint_access.go
+++ b/pkg/ctl/utils/update_cluster_endpoint_access.go
@@ -15,6 +15,7 @@ func updateClusterEndpointsCmd(cmd *cmdutils.Cmd) {
cfg := api.NewClusterConfig()
cmd.ClusterConfig = cfg
+ cmd.CobraCommand.Deprecated = "this command is deprecated and will be removed soon. Use `eksctl utils update-cluster-vpc-config --public-access=<> --private-access=<> instead."
cmd.SetDescription("update-cluster-endpoints", "Update Kubernetes API endpoint access configuration", "")
var (
@@ -61,6 +62,8 @@ func doUpdateClusterEndpoints(cmd *cmdutils.Cmd, newPrivate bool, newPublic bool
}
cfg.VPC.PublicAccessCIDRs = nil
+ cfg.VPC.ControlPlaneSubnetIDs = nil
+ cfg.VPC.ControlPlaneSecurityGroupIDs = nil
vpcHelper := &VPCHelper{
VPCUpdater: ctl,
ClusterMeta: cfg.Metadata,
diff --git a/pkg/ctl/utils/update_cluster_vpc_config.go b/pkg/ctl/utils/update_cluster_vpc_config.go
index 03334966c5..060a265bc5 100644
--- a/pkg/ctl/utils/update_cluster_vpc_config.go
+++ b/pkg/ctl/utils/update_cluster_vpc_config.go
@@ -46,6 +46,10 @@ func updateClusterVPCConfigWithHandler(cmd *cmdutils.Cmd, handler func(cmd *cmdu
cmd.FlagSetGroup.InFlagSet("Public Access CIDRs", func(fs *pflag.FlagSet) {
fs.StringSliceVar(&options.PublicAccessCIDRs, "public-access-cidrs", nil, "CIDR blocks that EKS uses to create a security group on the public endpoint")
})
+ cmd.FlagSetGroup.InFlagSet("Control plane subnets and security groups", func(fs *pflag.FlagSet) {
+ fs.StringSliceVar(&options.ControlPlaneSubnetIDs, "control-plane-subnet-ids", nil, "Subnet IDs for the control plane")
+ fs.StringSliceVar(&options.ControlPlaneSecurityGroupIDs, "control-plane-security-group-ids", nil, "Security group IDs for the control plane")
+ })
cmdutils.AddCommonFlagsForAWS(cmd, &cmd.ProviderConfig, false)
}
diff --git a/pkg/ctl/utils/update_cluster_vpc_config_test.go b/pkg/ctl/utils/update_cluster_vpc_config_test.go
index 5a616a8115..3733b1c9fb 100644
--- a/pkg/ctl/utils/update_cluster_vpc_config_test.go
+++ b/pkg/ctl/utils/update_cluster_vpc_config_test.go
@@ -21,6 +21,6 @@ var _ = DescribeTable("invalid usage of update-cluster-vpc-config", func(e updat
Entry("missing a required parameter", updateClusterVPCEntry{
args: []string{"--cluster", "test"},
- expectedErr: "at least one of --public-access, --private-access and --public-access-cidrs must be specified",
+ expectedErr: "at least one of these options must be specified: --private-access, --public-access, --public-access-cidrs, --control-plane-subnet-ids, --control-plane-security-group-ids",
}),
)
diff --git a/pkg/ctl/utils/vpc_helper.go b/pkg/ctl/utils/vpc_helper.go
index bebf549d40..2155d0d40f 100644
--- a/pkg/ctl/utils/vpc_helper.go
+++ b/pkg/ctl/utils/vpc_helper.go
@@ -7,7 +7,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/kris-nova/logger"
- "k8s.io/apimachinery/pkg/util/sets"
+ "golang.org/x/exp/slices"
"github.com/aws/aws-sdk-go-v2/service/eks"
ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
@@ -48,10 +48,57 @@ func (v *VPCHelper) UpdateClusterVPCConfig(ctx context.Context, vpc *api.Cluster
return err
}
}
+ if vpc.ControlPlaneSubnetIDs != nil || vpc.ControlPlaneSecurityGroupIDs != nil {
+ if err := v.updateSubnetsSecurityGroups(ctx, vpc); err != nil {
+ return err
+ }
+ }
cmdutils.LogPlanModeWarning(v.PlanMode)
return nil
}
+func (v *VPCHelper) updateSubnetsSecurityGroups(ctx context.Context, vpc *api.ClusterVPC) error {
+ current := v.Cluster.ResourcesVpcConfig
+ hasUpdate := false
+ vpcUpdate := &ekstypes.VpcConfigRequest{
+ SubnetIds: current.SubnetIds,
+ SecurityGroupIds: current.SecurityGroupIds,
+ }
+
+ compareValues := func(currentValues, newValues []string, resourceName string, updateFn func()) {
+ if !slices.Equal(currentValues, newValues) {
+ updateFn()
+ hasUpdate = true
+ cmdutils.LogIntendedAction(v.PlanMode, "update %s for cluster %q in %q to: %v", resourceName,
+ v.ClusterMeta.Name, v.ClusterMeta.Region, newValues)
+ } else {
+ logger.Success("%s for cluster %q in %q are already up-to-date", resourceName, v.ClusterMeta.Name, v.ClusterMeta.Region)
+ }
+ }
+ if vpc.ControlPlaneSubnetIDs != nil {
+ compareValues(current.SubnetIds, vpc.ControlPlaneSubnetIDs, "control plane subnet IDs", func() {
+ vpcUpdate.SubnetIds = vpc.ControlPlaneSubnetIDs
+ })
+ }
+
+ if vpc.ControlPlaneSecurityGroupIDs != nil {
+ compareValues(current.SecurityGroupIds, vpc.ControlPlaneSecurityGroupIDs, "control plane security group IDs", func() {
+ vpcUpdate.SecurityGroupIds = vpc.ControlPlaneSecurityGroupIDs
+ })
+ }
+
+ if v.PlanMode || !hasUpdate {
+ return nil
+ }
+ if err := v.updateVPCConfig(ctx, vpcUpdate); err != nil {
+ return err
+ }
+ cmdutils.LogCompletedAction(false, "control plane subnets and security groups for cluster %q in %q have been updated to: "+
+ "controlPlaneSubnetIDs=%v, controlPlaneSecurityGroupIDs=%v", v.ClusterMeta.Name, v.ClusterMeta.Region, vpcUpdate.SubnetIds, vpcUpdate.SecurityGroupIds)
+
+ return nil
+}
+
func (v *VPCHelper) updateEndpointAccess(ctx context.Context, desired api.ClusterEndpoints) error {
current := v.Cluster.ResourcesVpcConfig
if desired.PublicAccess == nil {
@@ -129,5 +176,5 @@ func cidrsEqual(currentValues, newValues []string) bool {
if len(newValues) == 0 && len(currentValues) == 1 && currentValues[0] == "0.0.0.0/0" {
return true
}
- return sets.NewString(currentValues...).Equal(sets.NewString(newValues...))
+ return slices.Equal(currentValues, newValues)
}
diff --git a/pkg/ctl/utils/vpc_helper_test.go b/pkg/ctl/utils/vpc_helper_test.go
index cd66052e7c..cbcca769b4 100644
--- a/pkg/ctl/utils/vpc_helper_test.go
+++ b/pkg/ctl/utils/vpc_helper_test.go
@@ -197,4 +197,148 @@ var _ = DescribeTable("VPCHelper", func(e vpcHelperEntry) {
expectedErr: "this operation is not supported on Outposts clusters",
}),
+
+ Entry("cluster matches desired config when subnets and security groups are specified", vpcHelperEntry{
+ clusterVPC: &ekstypes.VpcConfigResponse{
+ EndpointPublicAccess: true,
+ EndpointPrivateAccess: false,
+ PublicAccessCidrs: []string{"0.0.0.0/0"},
+ SecurityGroupIds: []string{"sg-1234"},
+ SubnetIds: []string{"subnet-1234"},
+ },
+ vpc: &api.ClusterVPC{
+ ClusterEndpoints: &api.ClusterEndpoints{
+ PublicAccess: api.Enabled(),
+ PrivateAccess: api.Disabled(),
+ },
+ ControlPlaneSecurityGroupIDs: []string{"sg-1234"},
+ ControlPlaneSubnetIDs: []string{"subnet-1234"},
+ },
+ }),
+
+ Entry("cluster security groups do not match desired config", vpcHelperEntry{
+ clusterVPC: &ekstypes.VpcConfigResponse{
+ EndpointPublicAccess: true,
+ EndpointPrivateAccess: false,
+ PublicAccessCidrs: []string{"0.0.0.0/0"},
+ SecurityGroupIds: []string{"sg-1234"},
+ SubnetIds: []string{"subnet-1234"},
+ },
+ vpc: &api.ClusterVPC{
+ ClusterEndpoints: &api.ClusterEndpoints{
+ PublicAccess: api.Enabled(),
+ PrivateAccess: api.Disabled(),
+ },
+ ControlPlaneSecurityGroupIDs: []string{"sg-1234", "sg-5678"},
+ ControlPlaneSubnetIDs: []string{"subnet-1234"},
+ },
+
+ expectedUpdates: []*eks.UpdateClusterConfigInput{
+ {
+ Name: aws.String("test"),
+ ResourcesVpcConfig: &ekstypes.VpcConfigRequest{
+ SecurityGroupIds: []string{"sg-1234", "sg-5678"},
+ SubnetIds: []string{"subnet-1234"},
+ },
+ },
+ },
+ }),
+
+ Entry("cluster subnets do not match desired config", vpcHelperEntry{
+ clusterVPC: &ekstypes.VpcConfigResponse{
+ EndpointPublicAccess: true,
+ EndpointPrivateAccess: false,
+ PublicAccessCidrs: []string{"0.0.0.0/0"},
+ SecurityGroupIds: []string{"sg-1234", "sg-5678"},
+ SubnetIds: []string{"subnet-1234"},
+ },
+ vpc: &api.ClusterVPC{
+ ClusterEndpoints: &api.ClusterEndpoints{
+ PublicAccess: api.Enabled(),
+ PrivateAccess: api.Disabled(),
+ },
+ ControlPlaneSecurityGroupIDs: []string{"sg-1234", "sg-5678"},
+ ControlPlaneSubnetIDs: []string{"subnet-1234", "subnet-5678"},
+ },
+
+ expectedUpdates: []*eks.UpdateClusterConfigInput{
+ {
+ Name: aws.String("test"),
+ ResourcesVpcConfig: &ekstypes.VpcConfigRequest{
+ SecurityGroupIds: []string{"sg-1234", "sg-5678"},
+ SubnetIds: []string{"subnet-1234", "subnet-5678"},
+ },
+ },
+ },
+ }),
+
+ Entry("cluster security group and subnets do not match desired config", vpcHelperEntry{
+ clusterVPC: &ekstypes.VpcConfigResponse{
+ EndpointPublicAccess: true,
+ EndpointPrivateAccess: false,
+ PublicAccessCidrs: []string{"0.0.0.0/0"},
+ SecurityGroupIds: []string{"sg-1234", "sg-5678"},
+ SubnetIds: []string{"subnet-1234"},
+ },
+ vpc: &api.ClusterVPC{
+ ClusterEndpoints: &api.ClusterEndpoints{
+ PublicAccess: api.Enabled(),
+ PrivateAccess: api.Disabled(),
+ },
+ ControlPlaneSecurityGroupIDs: []string{"sg-1234", "sg-5678"},
+ ControlPlaneSubnetIDs: []string{"subnet-1234", "subnet-5678"},
+ },
+
+ expectedUpdates: []*eks.UpdateClusterConfigInput{
+ {
+ Name: aws.String("test"),
+ ResourcesVpcConfig: &ekstypes.VpcConfigRequest{
+ SecurityGroupIds: []string{"sg-1234", "sg-5678"},
+ SubnetIds: []string{"subnet-1234", "subnet-5678"},
+ },
+ },
+ },
+ }),
+
+ Entry("no fields match desired config", vpcHelperEntry{
+ clusterVPC: &ekstypes.VpcConfigResponse{
+ EndpointPublicAccess: false,
+ EndpointPrivateAccess: true,
+ PublicAccessCidrs: []string{"0.0.0.0/0"},
+ SecurityGroupIds: []string{"sg-1234"},
+ SubnetIds: []string{"subnet-1234"},
+ },
+ vpc: &api.ClusterVPC{
+ ClusterEndpoints: &api.ClusterEndpoints{
+ PublicAccess: api.Enabled(),
+ PrivateAccess: api.Disabled(),
+ },
+ PublicAccessCIDRs: []string{"1.1.1.1/1"},
+ ControlPlaneSecurityGroupIDs: []string{"sg-1234", "sg-5678"},
+ ControlPlaneSubnetIDs: []string{"subnet-1234", "subnet-5678"},
+ },
+
+ expectedUpdates: []*eks.UpdateClusterConfigInput{
+ {
+ Name: aws.String("test"),
+ ResourcesVpcConfig: &ekstypes.VpcConfigRequest{
+ EndpointPublicAccess: api.Enabled(),
+ EndpointPrivateAccess: api.Disabled(),
+ },
+ },
+ {
+ Name: aws.String("test"),
+ ResourcesVpcConfig: &ekstypes.VpcConfigRequest{
+ PublicAccessCidrs: []string{"1.1.1.1/1"},
+ },
+ },
+ {
+ Name: aws.String("test"),
+ ResourcesVpcConfig: &ekstypes.VpcConfigRequest{
+ SecurityGroupIds: []string{"sg-1234", "sg-5678"},
+ SubnetIds: []string{"subnet-1234", "subnet-5678"},
+ },
+ },
+ },
+ }),
)
diff --git a/pkg/eks/mocks/ConfigProvider.go b/pkg/eks/mocks/ConfigProvider.go
index 5e809b75b7..4d355e02d5 100644
--- a/pkg/eks/mocks/ConfigProvider.go
+++ b/pkg/eks/mocks/ConfigProvider.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocks
diff --git a/pkg/eks/mocks/KubeNodeGroup.go b/pkg/eks/mocks/KubeNodeGroup.go
index 38f1266e8d..838077b6c1 100644
--- a/pkg/eks/mocks/KubeNodeGroup.go
+++ b/pkg/eks/mocks/KubeNodeGroup.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocks
diff --git a/pkg/eks/mocksv2/ASG.go b/pkg/eks/mocksv2/ASG.go
index a349b2c35c..7e0e7517a0 100644
--- a/pkg/eks/mocksv2/ASG.go
+++ b/pkg/eks/mocksv2/ASG.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/CloudFormation.go b/pkg/eks/mocksv2/CloudFormation.go
index 2fedae3a33..c166d54efd 100644
--- a/pkg/eks/mocksv2/CloudFormation.go
+++ b/pkg/eks/mocksv2/CloudFormation.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/CloudTrail.go b/pkg/eks/mocksv2/CloudTrail.go
index e42d9037fe..b36b3eb52e 100644
--- a/pkg/eks/mocksv2/CloudTrail.go
+++ b/pkg/eks/mocksv2/CloudTrail.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/CloudWatchLogs.go b/pkg/eks/mocksv2/CloudWatchLogs.go
index 2704574faf..eb560e8951 100644
--- a/pkg/eks/mocksv2/CloudWatchLogs.go
+++ b/pkg/eks/mocksv2/CloudWatchLogs.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/CredentialsProvider.go b/pkg/eks/mocksv2/CredentialsProvider.go
index e90646c8a4..ecfad51e3a 100644
--- a/pkg/eks/mocksv2/CredentialsProvider.go
+++ b/pkg/eks/mocksv2/CredentialsProvider.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/EC2.go b/pkg/eks/mocksv2/EC2.go
index 2da8e309f6..c3d7bf155a 100644
--- a/pkg/eks/mocksv2/EC2.go
+++ b/pkg/eks/mocksv2/EC2.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/EKS.go b/pkg/eks/mocksv2/EKS.go
index ac9e59ea7f..621beea3ab 100644
--- a/pkg/eks/mocksv2/EKS.go
+++ b/pkg/eks/mocksv2/EKS.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/ELB.go b/pkg/eks/mocksv2/ELB.go
index 83d8a2da97..b931191739 100644
--- a/pkg/eks/mocksv2/ELB.go
+++ b/pkg/eks/mocksv2/ELB.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/ELBV2.go b/pkg/eks/mocksv2/ELBV2.go
index 02fa44db67..0a0a3d0bb9 100644
--- a/pkg/eks/mocksv2/ELBV2.go
+++ b/pkg/eks/mocksv2/ELBV2.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/IAM.go b/pkg/eks/mocksv2/IAM.go
index d335605596..b631faf946 100644
--- a/pkg/eks/mocksv2/IAM.go
+++ b/pkg/eks/mocksv2/IAM.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/Outposts.go b/pkg/eks/mocksv2/Outposts.go
index 76c2509631..63653daa51 100644
--- a/pkg/eks/mocksv2/Outposts.go
+++ b/pkg/eks/mocksv2/Outposts.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/SSM.go b/pkg/eks/mocksv2/SSM.go
index 3d3f15d52a..e84a86c534 100644
--- a/pkg/eks/mocksv2/SSM.go
+++ b/pkg/eks/mocksv2/SSM.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/pkg/eks/mocksv2/STS.go b/pkg/eks/mocksv2/STS.go
index 98f66be61c..dbb2684aec 100644
--- a/pkg/eks/mocksv2/STS.go
+++ b/pkg/eks/mocksv2/STS.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.32.2. DO NOT EDIT.
+// Code generated by mockery v2.33.1. DO NOT EDIT.
package mocksv2
diff --git a/userdocs/mkdocs.yml b/userdocs/mkdocs.yml
index 66ac005bfa..6ce84d4a44 100644
--- a/userdocs/mkdocs.yml
+++ b/userdocs/mkdocs.yml
@@ -179,6 +179,7 @@ nav:
- usage/vpc-configuration.md
- usage/vpc-subnet-settings.md
- usage/vpc-cluster-access.md
+ - usage/cluster-subnets-security-groups.md
- usage/vpc-ip-family.md
- IAM:
- usage/minimum-iam-policies.md
diff --git a/userdocs/src/getting-started.md b/userdocs/src/getting-started.md
index 8c055fa2d5..fefb363dfa 100644
--- a/userdocs/src/getting-started.md
+++ b/userdocs/src/getting-started.md
@@ -1,6 +1,8 @@
# Getting started
!!! tip "New for 2023"
+ `eksctl` now supports [updating the subnets and security groups](/usage/cluster-subnets-security-groups) associated with the EKS control plane.
+
`eksctl` now supports creating fully private clusters on [AWS Outposts](/usage/outposts).
`eksctl` now supports new ISO regions `us-iso-east-1` and `us-isob-east-1`.
diff --git a/userdocs/src/usage/cluster-subnets-security-groups.md b/userdocs/src/usage/cluster-subnets-security-groups.md
new file mode 100644
index 0000000000..1fb83cf135
--- /dev/null
+++ b/userdocs/src/usage/cluster-subnets-security-groups.md
@@ -0,0 +1,83 @@
+# Updating control plane subnets and security groups
+
+## Updating control plane subnets
+When a cluster is created with eksctl, a set of public and private subnets are created and passed to the EKS API.
+EKS creates 2 to 4 cross-account elastic network interfaces (ENIs) in those subnets to enable communication between the EKS
+managed Kubernetes control plane and your VPC.
+
+To update the subnets used by the EKS control plane, run:
+
+```console
+eksctl utils update-cluster-vpc-config --cluster= --control-plane-subnet-ids=subnet-1234,subnet-5678
+```
+
+To update the setting using a config file:
+
+```yaml
+apiVersion: eksctl.io/v1alpha5
+kind: ClusterConfig
+metadata:
+ name: cluster
+ region: us-west-2
+
+vpc:
+ controlPlaneSubnetIDs: [subnet-1234, subnet-5678]
+```
+
+```console
+eksctl utils update-cluster-vpc-config -f config.yaml
+```
+
+Without the `--approve` flag, eksctl only logs the proposed changes. Once you are satisfied with the proposed changes, rerun the command with
+the `--approve` flag.
+
+## Updating control plane security groups
+To manage traffic between the control plane and worker nodes, EKS supports passing additional security groups that are applied to the cross-account network interfaces
+provisioned by EKS. To update the security groups for the EKS control plane, run:
+
+```console
+eksctl utils update-cluster-vpc-config --cluster= --control-plane-security-group-ids=sg-1234,sg-5678 --approve
+```
+
+To update the setting using a config file:
+
+```yaml
+apiVersion: eksctl.io/v1alpha5
+kind: ClusterConfig
+metadata:
+ name: cluster
+ region: us-west-2
+
+vpc:
+ controlPlaneSecurityGroupIDs: [sg-1234, sg-5678]
+```
+
+```console
+eksctl utils update-cluster-vpc-config -f config.yaml
+```
+
+To update both control plane subnets and security groups for a cluster, run:
+
+```console
+eksctl utils update-cluster-vpc-config --cluster= --control-plane-subnet-ids=<> --control-plane-security-group-ids=<> --approve
+```
+
+To update both fields using a config file:
+
+```yaml
+apiVersion: eksctl.io/v1alpha5
+kind: ClusterConfig
+metadata:
+ name: cluster
+ region: us-west-2
+
+vpc:
+ controlPlaneSubnetIDs: [subnet-1234, subnet-5678]
+ controlPlaneSecurityGroupIDs: [sg-1234, sg-5678]
+```
+
+```console
+eksctl utils update-cluster-vpc-config -f config.yaml
+```
+
+For a complete example, refer to [https://github.com/eksctl-io/eksctl/blob/main/examples/38-cluster-subnets-sgs.yaml](cluster-subnets-sgs.yaml).
\ No newline at end of file
diff --git a/userdocs/src/usage/vpc-cluster-access.md b/userdocs/src/usage/vpc-cluster-access.md
index 128757c2e7..2a9fec28dc 100644
--- a/userdocs/src/usage/vpc-cluster-access.md
+++ b/userdocs/src/usage/vpc-cluster-access.md
@@ -33,14 +33,18 @@ There are some additional caveats when configuring Kubernetes API endpoint acces
The following is an example of how one could configure the Kubernetes API endpoint access using the `utils` sub-command:
+```console
+eksctl utils update-cluster-vpc-config --cluster= --private-access=true --public-access=false
```
-eksctl utils update-cluster-endpoints --name= --private-access=true --public-access=false
-```
+
+!!! warning
+ `eksctl utils update-cluster-endpoints` has been deprecated in favour of `eksctl utils update-cluster-vpc-config`
+ and will be removed soon.
To update the setting using a `ClusterConfig` file, use:
```console
-eksctl utils update-cluster-endpoints -f config.yaml --approve
+eksctl utils update-cluster-vpc-config -f config.yaml --approve
```
Note that if you don't pass a flag, it will keep the current value. Once you are satisfied with the proposed changes,
@@ -59,13 +63,17 @@ vpc:
To update the restrictions on an existing cluster, use:
```console
-eksctl utils set-public-access-cidrs --cluster= 1.1.1.1/32,2.2.2.0/24
+eksctl utils update-cluster-vpc-config --cluster= 1.1.1.1/32,2.2.2.0/24
```
+!!! warning
+ `eksctl utils set-public-access-cidrs` has been deprecated in favour of `eksctl utils update-cluster-vpc-config`
+ and will be removed soon.
+
To update the restrictions using a `ClusterConfig` file, set the new CIDRs in `vpc.publicAccessCIDRs` and run:
```console
-eksctl utils set-public-access-cidrs -f config.yaml
+eksctl utils update-cluster-vpc-config -f config.yaml
```
!!! warning
@@ -81,3 +89,24 @@ eksctl utils set-public-access-cidrs -f config.yaml
the internet. (Source: https://github.com/aws/containers-roadmap/issues/108#issuecomment-552766489)
Implementation notes: https://github.com/aws/containers-roadmap/issues/108#issuecomment-552698875
+
+
+To update both API server endpoint access and public access CIDRs for a cluster in a single command, run:
+
+```console
+eksctl utils update-cluster-vpc-config --cluster= --public-access=true --private-access=true --public-access-cidrs=1.1.1.1/32,2.2.2.0/24
+```
+
+To update the setting using a config file:
+
+```yaml
+vpc:
+ clusterEndpoints:
+ publicAccess:
+ privateAccess:
+ publicAccessCIDRs: ["1.1.1.1/32"]
+```
+
+```console
+eksctl utils update-cluster-vpc-config --cluster= -f config.yaml
+```