Describe the bug
When a nodegroup is created for an IPv6 cluster, Nodegroup grants the node role the IPv6 address-assignment permissions the VPC CNI needs. The resource ARN for that statement is a hardcoded string in the aws partition:
aws-eks/lib/managed-nodegroup.ts#L543-L552
if (props.cluster.ipFamily == IpFamily.IP_V6) {
ngRole.addToPrincipalPolicy(new PolicyStatement({
// eslint-disable-next-line @cdklabs/no-literal-partition
resources: ['arn:aws:ec2:*:*:network-interface/*'],
actions: [
'ec2:AssignIpv6Addresses',
'ec2:UnassignIpv6Addresses',
],
}));
}
The same block exists verbatim in aws-eks-v2/lib/managed-nodegroup.ts#L519-L528.
An ARN naming the aws partition can never match a resource in aws-us-gov or aws-cn, so in those partitions the statement is a no-op: it grants nothing, and it does so silently — synthesis succeeds, cdk deploy succeeds, and the nodes come up without the permission the CNI needs to assign IPv6 addresses to pods.
Synthesizing the same app into three regions shows the ARN never changes, while CDK's own partition-aware ARNs in the same template do:
| stack region |
partition |
Resource in the IPv6 statement |
a sibling managed-policy ARN in the same template |
us-east-1 |
aws |
arn:aws:ec2:*:*:network-interface/* |
{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]} |
us-gov-west-1 |
aws-us-gov |
arn:aws:ec2:*:*:network-interface/* |
{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]} |
cn-north-1 |
aws-cn |
arn:aws:ec2:*:*:network-interface/* |
{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]} |
The eslint-disable for @cdklabs/no-literal-partition sits directly above the line, so the rule that exists to catch exactly this was suppressed rather than satisfied. AGENTS.md § ARN Construction states the rule this breaks:
Use Stack.of(scope).formatArn() — never hardcode ARN strings
Scope of the impact. AWS documents that ipv6 cannot be specified for clusters in China Regions, so aws-cn is not reachable in practice today. GovCloud (US) carries no such documented restriction, and that is where this bites: an IPv6 EKS cluster in us-gov-west-1 gets a node role whose IPv6 grant does nothing. The symptom is pods that never receive an address, with the CNI unable to call ec2:AssignIpv6Addresses.
Regression Issue
Last Known Working CDK Library Version
No response
Expected Behavior
The grant should be scoped to the partition the stack is deployed into, the same way every other ARN CDK emits is:
{
"Action": ["ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses"],
"Effect": "Allow",
"Resource": { "Fn::Join": ["", ["arn:", { "Ref": "AWS::Partition" }, ":ec2:*:*:network-interface/*"]] }
}
or, for an app with @aws-cdk/core:enablePartitionLiterals and a concrete region, the resolved literal for that partition — arn:aws-us-gov:ec2:*:*:network-interface/*.
Current Behavior
Resource is the literal arn:aws:ec2:*:*:network-interface/* in every partition. In aws-us-gov and aws-cn the statement matches no resource, so the two ec2:*Ipv6Addresses actions are effectively not granted. Nothing warns at synth or deploy time.
Reproduction Steps
import { App, Stack } from 'aws-cdk-lib';
import * as eks from 'aws-cdk-lib/aws-eks';
import * as lambda from 'aws-cdk-lib/aws-lambda';
const app = new App();
const stack = new Stack(app, 'S', { env: { account: '123456789012', region: 'us-gov-west-1' } });
const cluster = new eks.Cluster(stack, 'C', {
version: eks.KubernetesVersion.V1_32,
ipFamily: eks.IpFamily.IP_V6,
defaultCapacity: 0,
kubectlLayer: new lambda.LayerVersion(stack, 'KubectlLayer', { code: lambda.Code.fromAsset('layer') }),
});
cluster.addNodegroupCapacity('NG', {});
const tpl = app.synth().getStackByName('S').template;
for (const res of Object.values(tpl.Resources) as any[]) {
if (res.Type !== 'AWS::IAM::Policy') continue;
for (const s of res.Properties.PolicyDocument.Statement) {
if (JSON.stringify(s).includes('network-interface')) console.log(JSON.stringify(s, null, 2));
}
}
Output (identical for cn-north-1):
{
"Action": ["ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses"],
"Effect": "Allow",
"Resource": "arn:aws:ec2:*:*:network-interface/*"
}
Possible Solution
Format the ARN instead of hardcoding it, and drop the eslint-disable:
if (props.cluster.ipFamily == IpFamily.IP_V6) {
ngRole.addToPrincipalPolicy(new PolicyStatement({
resources: [Stack.of(this).formatArn({
service: 'ec2',
region: '*',
account: '*',
resource: 'network-interface',
resourceName: '*',
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
})],
actions: [
'ec2:AssignIpv6Addresses',
'ec2:UnassignIpv6Addresses',
],
}));
}
The same change applies to the aws-eks-v2 copy. In the aws partition the rendered ARN is unchanged in meaning ({"Ref":"AWS::Partition"} resolves to aws), so this only widens correctness — but note it does change the literal string in the template, so existing integ snapshots covering IPv6 nodegroups will need updating.
aws-eks/lib/alb-controller.ts already solves the identical problem for the ALB controller policy by rewriting arn:aws: to arn:${Aws.PARTITION}:, which is a useful precedent for how the maintainers have handled this before.
Additional Information/Context
Same class of defect as #33212 (hardcoded partition in the S3 auto-delete-objects handler) and tracked generally by #28474.
I have not verified the runtime symptom on a live GovCloud cluster — the report is from the synthesized template plus the IAM semantics of a cross-partition resource ARN. If EKS IPv6 turns out to be unavailable in every non-aws partition, the ARN is still wrong and still worth fixing, but the practical impact would be limited to the moment it becomes available.
AWS CDK Library version (aws-cdk-lib)
2.264.0
AWS CDK CLI version
N/A (synthesis-time, framework only)
Node.js Version
v24.1.0
OS
macOS 15 (Darwin 25.2.0)
Language
TypeScript
Language Version
No response
Other information
No response
Describe the bug
When a nodegroup is created for an IPv6 cluster,
Nodegroupgrants the node role the IPv6 address-assignment permissions the VPC CNI needs. The resource ARN for that statement is a hardcoded string in theawspartition:aws-eks/lib/managed-nodegroup.ts#L543-L552The same block exists verbatim in
aws-eks-v2/lib/managed-nodegroup.ts#L519-L528.An ARN naming the
awspartition can never match a resource inaws-us-govoraws-cn, so in those partitions the statement is a no-op: it grants nothing, and it does so silently — synthesis succeeds,cdk deploysucceeds, and the nodes come up without the permission the CNI needs to assign IPv6 addresses to pods.Synthesizing the same app into three regions shows the ARN never changes, while CDK's own partition-aware ARNs in the same template do:
Resourcein the IPv6 statementus-east-1awsarn:aws:ec2:*:*:network-interface/*{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]}us-gov-west-1aws-us-govarn:aws:ec2:*:*:network-interface/*{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]}cn-north-1aws-cnarn:aws:ec2:*:*:network-interface/*{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]}The
eslint-disablefor@cdklabs/no-literal-partitionsits directly above the line, so the rule that exists to catch exactly this was suppressed rather than satisfied. AGENTS.md § ARN Construction states the rule this breaks:Scope of the impact. AWS documents that
ipv6cannot be specified for clusters in China Regions, soaws-cnis not reachable in practice today. GovCloud (US) carries no such documented restriction, and that is where this bites: an IPv6 EKS cluster inus-gov-west-1gets a node role whose IPv6 grant does nothing. The symptom is pods that never receive an address, with the CNI unable to callec2:AssignIpv6Addresses.Regression Issue
Last Known Working CDK Library Version
No response
Expected Behavior
The grant should be scoped to the partition the stack is deployed into, the same way every other ARN CDK emits is:
{ "Action": ["ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses"], "Effect": "Allow", "Resource": { "Fn::Join": ["", ["arn:", { "Ref": "AWS::Partition" }, ":ec2:*:*:network-interface/*"]] } }or, for an app with
@aws-cdk/core:enablePartitionLiteralsand a concrete region, the resolved literal for that partition —arn:aws-us-gov:ec2:*:*:network-interface/*.Current Behavior
Resourceis the literalarn:aws:ec2:*:*:network-interface/*in every partition. Inaws-us-govandaws-cnthe statement matches no resource, so the twoec2:*Ipv6Addressesactions are effectively not granted. Nothing warns at synth or deploy time.Reproduction Steps
Output (identical for
cn-north-1):{ "Action": ["ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses"], "Effect": "Allow", "Resource": "arn:aws:ec2:*:*:network-interface/*" }Possible Solution
Format the ARN instead of hardcoding it, and drop the
eslint-disable:The same change applies to the
aws-eks-v2copy. In theawspartition the rendered ARN is unchanged in meaning ({"Ref":"AWS::Partition"}resolves toaws), so this only widens correctness — but note it does change the literal string in the template, so existing integ snapshots covering IPv6 nodegroups will need updating.aws-eks/lib/alb-controller.tsalready solves the identical problem for the ALB controller policy by rewritingarn:aws:toarn:${Aws.PARTITION}:, which is a useful precedent for how the maintainers have handled this before.Additional Information/Context
Same class of defect as #33212 (hardcoded partition in the S3 auto-delete-objects handler) and tracked generally by #28474.
I have not verified the runtime symptom on a live GovCloud cluster — the report is from the synthesized template plus the IAM semantics of a cross-partition resource ARN. If EKS IPv6 turns out to be unavailable in every non-
awspartition, the ARN is still wrong and still worth fixing, but the practical impact would be limited to the moment it becomes available.AWS CDK Library version (aws-cdk-lib)
2.264.0
AWS CDK CLI version
N/A (synthesis-time, framework only)
Node.js Version
v24.1.0
OS
macOS 15 (Darwin 25.2.0)
Language
TypeScript
Language Version
No response
Other information
No response