AWSTemplateFormatVersion: 2010-09-09 Description: Enables IRSA for the aws-node daemonset (qs-1tsonl5rk). # https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html # https://docs.aws.amazon.com/eks/latest/userguide/cni-iam-role.html Metadata: cfn-lint: { config: { ignore_checks: [W9002, W9003, W9004, W9006] } } Parameters: ClusterName: Type: String OIDCProviderArn: Type: String OIDCProviderEndpoint: Type: String KubectlVersion: Type: String Conditions: Commercial: !Equals [!Ref AWS::Partition, aws] Resources: RBACRole: Type: AWSQS::Kubernetes::Resource UpdateReplacePolicy: Delete DeletionPolicy: Retain Properties: ClusterName: !Ref ClusterName Namespace: kube-system Manifest: | apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: aws-node-enable-irsa rules: - apiGroups: [apps] resources: [daemonsets] verbs: [patch, get] - apiGroups: [''] resources: [serviceaccounts] verbs: [patch, get] ServiceAccount: Type: AWSQS::Kubernetes::Resource UpdateReplacePolicy: Delete DeletionPolicy: Retain Properties: ClusterName: !Ref ClusterName Namespace: kube-system Manifest: !Sub | apiVersion: v1 kind: ServiceAccount metadata: name: aws-node-enable-irsa annotations: eks.amazonaws.com/role-arn: ${AWSNodeIAMRole.Arn} ServiceClusterRoleBinding: Type: AWSQS::Kubernetes::Resource DependsOn: [ServiceAccount, RBACRole] UpdateReplacePolicy: Delete DeletionPolicy: Retain Properties: ClusterName: !Ref ClusterName Namespace: kube-system Manifest: | apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: aws-node-enable-irsa subjects: - kind: ServiceAccount name: aws-node-enable-irsa namespace: kube-system roleRef: kind: ClusterRole name: aws-node-enable-irsa apiGroup: rbac.authorization.k8s.io AWSNodeIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: !Sub | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "${OIDCProviderArn}" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "${OIDCProviderEndpoint}:aud": [ "sts.amazonaws.com", "sts.${AWS::Region}.amazonaws.com" ], "${OIDCProviderEndpoint}:sub": "system:serviceaccount:kube-system:aws-node" } } } ] } Path: / ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonEKS_CNI_Policy - !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly - !If [Commercial, !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonElasticContainerRegistryPublicReadOnly', !Ref AWS::NoValue] Policies: - PolicyName: AmazonEKS_CNI_IPv6_Policy # https://docs.aws.amazon.com/eks/latest/userguide/cni-iam-role.html#cni-iam-role-create-ipv6-policy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - ec2:DescribeInstances - ec2:DescribeTags - ec2:DescribeNetworkInterfaces - ec2:DescribeInstanceTypes Resource: '*' - Effect: Allow Action: - ec2:AssignIpv6Addresses - ec2:CreateTags Resource: !Sub arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:network-interface/* JobResource: Type: AWSQS::Kubernetes::Resource DependsOn: ServiceClusterRoleBinding Properties: ClusterName: !Ref ClusterName Namespace: kube-system Manifest: !Sub | apiVersion: batch/v1 kind: Job metadata: name: aws-node-enable-irsa spec: template: spec: serviceAccountName: aws-node-enable-irsa nodeSelector: kubernetes.io/os: linux containers: - name: aws-node-enable-irsa image: public.ecr.aws/amazonlinux/amazonlinux:2 command: [/bin/bash, -c] args: - > sleep 10; if [ `uname -m` = 'x86_64' ]; then ARCH="amd64"; else ARCH="arm64"; fi; curl --retry 5 -sSo kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/${KubectlVersion}/bin/linux/$ARCH/kubectl; chmod +x ./kubectl; ./kubectl -n kube-system patch sa aws-node -p '{"metadata": {"annotations": {"eks.amazonaws.com/role-arn": "${AWSNodeIAMRole.Arn}" }}}'; ./kubectl -n kube-system patch daemonset aws-node -p '{"spec": {"template": {"metadata": {"annotations": {"irsa": "enabled"}}}}}'; RETRIES=0 ; while true ; do DESIRED=$(./kubectl get daemonset aws-node -n kube-system -o jsonpath={.status.desiredNumberScheduled}|| exit 1); UPDATED=$(./kubectl get daemonset aws-node -n kube-system -o jsonpath={.status.updatedNumberScheduled}|| exit 1); READY=$(./kubectl get daemonset aws-node -n kube-system -o jsonpath={.status.numberReady}|| exit 1); if [[ "$DESIRED" == "$UPDATED" && "$DESIRED" == "$READY" ]]; then break ; fi ; echo "Desired: $DESIRED Updated: $UPDATED Ready: $READY" ; ((RETRIES=RETRIES+1)); if [ $RETRIES -gt 40 ]; then echo "Timed out waiting for pods to become ready"; exit 1; fi; sleep 15; done restartPolicy: OnFailure backoffLimit: 4 Outputs: OIDCProviderArn: Value: !Ref OIDCProviderArn OIDCProviderEndpoint: Value: !Ref OIDCProviderEndpoint