How do I get an OIDC provider URL from AWS::EKS::Cluster in AWS CloudFormation?
Last updated: 2020-08-20
I want to fetch the OpenID Connect (OIDC) provider URL from AWS::EKS::Cluster. Then, I want to use the URL in an AWS Identity and Access Management (IAM) role for a service account trust relationship using AWS CloudFormation.
Short description
AWS::EKS::Cluster doesn't provide a return value to get the OIDC provider URL. You must use AWS Lambda-backed custom resources in AWS CloudFormation to get the cluster OIDC provider URL from an existing Amazon Elastic Kubernetes Service (Amazon EKS) cluster. Then, you can use the value returned from the cluster to create an IAM role. The IAM role allows you to establish a trust relationship with OIDC.
Note: The following resolution uses Python 3.7 and is applicable for Kubernetes version 1.14 and later.
Resolution
The following example demonstrates how to associate a service account in an Amazon EKS cluster with an IAM role. The IAM role provides fine-grained permissions to Amazon EKS pods and secure access to the AWS API for the service accounts in the Amazon EKS cluster. Then, the stack creates an IAM role associated with the Amazon EKS cluster's OIDC value.
1. Create an AWS CloudFormation template called CustomLambdaEksOidc.template based on the following example:
Parameters:
EKSClusterName:
Type: String
Description: Provide the name for EKS Cluster
Resources:
ClusterName:
Type: Custom::ClusterName
Properties:
ServiceToken: !GetAtt LambdaFunction.Arn
cluster_name:
Ref: EKSClusterName
MyIAMRole:
Type: AWS::IAM::Role
DependsOn: ClusterName
Properties:
AssumeRolePolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${AWS::AccountId}:oidc-provider/${ClusterName.oidc}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"${ClusterName.oidc}:aud": "system:serviceaccount:default:my-serviceaccount"
}
}
}
]
}
LambdaIAMRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'eks:DescribeCluster'
Resource: '*'
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: 'arn:aws:logs:*:*:*'
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import cfnresponse
import boto3
import json
def lambda_handler(event, context):
print("Received event: " + json.dumps(event, indent=2))
oidc_response = ''
responseData = {}
try:
if event['RequestType'] == 'Delete':
print("Request Type:",event['RequestType'])
print("Delete Request - No Physical resources to delete")
elif event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
print("Request Type:",event['RequestType'])
cluster_name = event['ResourceProperties']['cluster_name']
oidc_response_url = fetchClusterOIDC(cluster_name)
oidc_response=oidc_response_url.split("https://")[1]
responseData['oidc'] = oidc_response
print("Sending response to custom resource for event type " + event['RequestType'])
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
return oidc_response
except Exception as e:
print('Failed to process:', e)
responseStatus = 'FAILURE'
responseData = {'Failure': 'Something bad happened.'}
cfnresponse.send(event, context, cfnresponse.FAILED, responseData, "CustomResourcePhysicalID")
def fetchClusterOIDC(cluster_name):
print("Getting Cluster OIDC value for cluster name "+ cluster_name)
oidc = ''
client = boto3.client('eks')
try:
response = client.describe_cluster(
name=cluster_name
)
if response['ResponseMetadata']['HTTPStatusCode'] == 200:
print("Success response recieved for describing cluster "+ cluster_name)
oidc = (response['cluster']['identity']['oidc']['issuer'])
print('OIDC output recieved '+ oidc + ' for Cluster Name ' + cluster_name)
return oidc
except Exception as e:
print('Failed to fetch Cluster OIDC value for cluster name ' + cluster_name, e)
Handler: index.lambda_handler
MemorySize: 1024
Role: !GetAtt LambdaIAMRole.Arn
Runtime: python3.7
Timeout: 300
Outputs:
OIDC:
Description: EKS Cluster OIDC Value
Value:
Fn::GetAtt:
- ClusterName
- oidc
2. To launch an AWS CloudFormation stack with the CustomLambdaEksOidc.template file, use the AWS CloudFormation console or the AWS Command Line Interface (AWS CLI).
To use the AWS CLI, run the following command:
aws cloudformation create-stack --stack-name lambda-eks-oidc --template-body file://CustomLambdaEksOidc.template --parameters ParameterKey=EKSClusterName,ParameterValue=demo-newsblog --capabilities CAPABILITY_NAMED_IAM --region us-east-1
Note: When you launch your AWS CloudFormation stack, replace demo-newsblog with your Amazon EKS cluster name. Replace us-east-1 with your AWS Region.
Did this article help?
Do you need billing or technical support?