DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • How to Build and Deploy an AI Agent on Kubernetes With AWS Bedrock, FastAPI and Helm
  • Demystifying Kubernetes on AWS: A Comparative Analysis of Deployment Options
  • How to Use Jenkins Effectively With ECS/EKS Cluster
  • Building a Platform Abstraction for AWS Networks Using Crossplane

Trending

  • Amazon Quick: AWS's Agentic Workspace, Explained for Engineers
  • Compliance Automated Standard Solution (COMPASS), Part 11: Compliance as Code, the OSCAL MCP Server Way
  • Alternative Structured Concurrency
  • Why Round-Robin Won't Save You: Load Balancing Challenges in Data Streaming Services With Heterogeneous Traffic
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Building a Platform Abstraction for EKS Cluster Using Crossplane

Building a Platform Abstraction for EKS Cluster Using Crossplane

Build Crossplane abstractions that let your devs request EKS clusters without writing infrastructure code. Create XRD and composition to hide details.

By 
Ramesh Sinha user avatar
Ramesh Sinha
·
Sep. 17, 25 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
3.8K Views

Join the DZone community and get the full member experience.

Join For Free

Building on what we started earlier in an earlier article, here we’re going to learn how to extend our platform and create a platform abstraction for provisioning an AWS EKS cluster. EKS is AWS’s managed Kubernetes offering.

Quick Refresher

Crossplane is a Kubernetes CRD-based add-on that abstracts cloud implementations and lets us manage Infrastructure as code. 

Prerequisites

  • Set up Docker Kubernetes.
  • Follow the Crossplane installation based on the previous article.
  • Follow the provider configuration based on the previous article.
  • Apply all the network YAMLs from the previous article (including the updated network composition discussed later). This will create the necessary network resources for the EKS cluster.

Some Plumbing 

When creating an EKS cluster, AWS needs to:

  • Spin up the control plane (managed by AWS)
  • Attach security groups 
  • Configure networking (ENIs, etc)
  • Access the VPC and subnets
  • Manage API endpoints
  • Interact with other AWS services (e.g., CloudWatch for logging, Route53)

To do this securely, AWS requires an IAM role that it can assume. We create that role here and reference it during cluster creation; details are provided below. Without this role, you'll get errors like "access denied" when creating the cluster.

Steps to Create the AWS IAM Role

  1. Log in to the AWS Console and go to the IAM creation page.
  2. In the left sidebar, click Roles
  3. Click Create Role.
  4. Choose AWS service as the trusted entity type.
  5. Select the EKS use case, and choose the EKS Cluster.
  6. Attach the following policies:
    • AmazonEKSClusterPolicy
    • AmazonEKSServicePolicy
    • AmazonEC2FullAccess
    • AmazonEKSWorkerNodePolicy
    • AmazonEC2ContainerRegistryReadOnly
    • AmazonEKS_CNI_Policy
  7. Provide the name  eks-crossplane-cluster and optionally add tags.

Since we'll also create NodeGroups, which require additional permissions, for simplicity, I'm granting the Crossplane user (created in the previous article) permission to PassRole for the Crossplane cluster role, and this permission allows this user to tell AWS services (EKS) to assume the Crossplane cluster role on its behalf. 

Basically, this user can say, "Hey, EKS service, create a node group and use this role when doing it." To accomplish this, add the following inline policy to the Crossplane user:

JSON
 
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::914797696655:role/eks-crossplane-clsuter"
    }
  ]
}


Note: Typically, to follow the principle of Least Privilege, you should separate roles with policies:

  • Control plane role with EKS admin permissions
  • Node role with permissions for node group creation.

In the previous article, I had created only one subnet in the network composition, but the EKS control plane requires at least two AZs, with one subnet per AZ. You should modify the network composition from the previous article to add another subnet. To do so,  just add the following to the network composition YAML, and don't forget to apply the composition and claim to re-create the network.

YAML
 
- name: subnet-b
     base:
       apiVersion: ec2.aws.upbound.io/v1beta1
       kind: Subnet
       spec:
         forProvider:
           cidrBlock: 10.0.2.0/24
           availabilityZone: us-east-1b
           mapPublicIpOnLaunch: true
           region: us-east-1
         providerConfigRef:
           name: default
     patches:
       - fromFieldPath: status.vpcId
         toFieldPath: spec.forProvider.vpcId
         type: FromCompositeFieldPath
       - fromFieldPath: spec.claimRef.name
         toFieldPath: spec.forProvider.tags.Name
         type: FromCompositeFieldPath
         transforms:
           - type: string
             string:
               fmt: "%s-subnet-b"
       - fromFieldPath: status.atProvider.id
         toFieldPath: status.subnetIds[1]
         type: ToCompositeFieldPath


We will also need a provider to support EKS resource creation, to create the necessary provider, save the following content into .yaml file.

YAML
 
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: xpkg.upbound.io/crossplane-contrib/provider-aws:v0.54.2
  controllerConfigRef:
    name: default


And apply using:

YAML
 
kubectl apply -f <your-file-name>.yaml


Crossplane Composite Resource Definition (XRD)

Below, we’re going to build a Composite Resource Definition for the EKS cluster.

Before diving in, one thing to note: If you’ve already created the network resources using the previous article, you may have noticed that the network composition includes a field that places the subnet ID into the composition resource’s status, specifically under status.subnetIds[0]. This value comes from the cloud's Subnet resource and is needed by other XCluster compositions. By placing it in the status field, the network composition makes it possible for other Crossplane compositions to reference and use it.

Similar to what we did for network creation in the previous article, we’re going to create a Crossplane XRD, a Crossplane Composition, and finally a Claim that will result in the creation of an EKS cluster. At the end, I’ve included a table that serves as an analogy to help illustrate the relationship between the Composite Resource Definition (XRD), Composite Resource (XR), Composition, and Claim.

To create an EKS XRD, save the following content into .yaml file:

YAML
 
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xclusters.aws.platformref.crossplane.io
spec:
  group: aws.platformref.crossplane.io
  names:
    kind: XCluster
    plural: xclusters
  claimNames:
    kind: Cluster
    plural: clusters
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          required:
            - spec
          properties:
            spec:
              type: object
              required:
                - parameters
              properties:
                parameters:
                  type: object
                  required:
                    - region
                    - roleArn
                    - networkRef
                  properties:
                    region:
                      type: string
                      description: AWS region to deploy the EKS cluster in.
                    roleArn:
                      type: string
                      description: IAM role ARN for the EKS control plane.
                    networkRef:
                      type: object
                      description: Reference to a pre-created XNetwork.
                      required:
                        - name
                      properties:
                        name:
                          type: string
            status:
              type: object
              properties:
                network:
                  type: object
                  required:
                    - subnetIds
                  properties:
                    subnetIds:
                      type: array
                      items:
                        type: string


And apply using:

YAML
 
kubectl apply -f <your-file-name>.yaml


Crossplane Composition

Composition is the implementation; it tells Crossplane how to build all the underlying resources (Control Plane, NodeGroup).

To create an EKS composition, save the below content into a  .yaml file:

YAML
 
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: cluster.aws.platformref.crossplane.io
spec:
  compositeTypeRef:
    apiVersion: aws.platformref.crossplane.io/v1alpha1
    kind: XCluster
  resources:
    - name: network
      base:
        apiVersion: aws.platformref.crossplane.io/v1alpha1
        kind: XNetwork
      patches:
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.networkRef.name
          toFieldPath: metadata.name
        - type: ToCompositeFieldPath
          fromFieldPath: status.subnetIds
          toFieldPath: status.network.subnetIds
        - type: ToCompositeFieldPath
          fromFieldPath: status.subnetIds[0]
          toFieldPath: status.network.subnetIds[0]
      readinessChecks:
        - type: None

    - name: eks
      base:
        apiVersion: eks.aws.crossplane.io/v1beta1
        kind: Cluster
        spec:
          forProvider:
            region: us-east-1
            roleArn: ""
            resourcesVpcConfig:
              subnetIds: []
              endpointPrivateAccess: true
              endpointPublicAccess: true
          providerConfigRef:
            name: default
      patches:
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.region
          toFieldPath: spec.forProvider.region
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.roleArn
          toFieldPath: spec.forProvider.roleArn
        - type: FromCompositeFieldPath
          fromFieldPath: status.network.subnetIds
          toFieldPath: spec.forProvider.resourcesVpcConfig.subnetIds

    - name: nodegroup
      base:
        apiVersion: eks.aws.crossplane.io/v1alpha1
        kind: NodeGroup
        spec:
          forProvider:
            region: us-east-1
            clusterNameSelector:
              matchControllerRef: true
            nodeRole: ""
            subnets: []
            scalingConfig:
              desiredSize: 2
              maxSize: 3
              minSize: 1
            instanceTypes:
              - t3.medium
            amiType: AL2_x86_64
            diskSize: 20
          providerConfigRef:
            name: default
      patches:
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.region
          toFieldPath: spec.forProvider.region
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.roleArn
          toFieldPath: spec.forProvider.nodeRole
        - type: FromCompositeFieldPath
          fromFieldPath: status.network.subnetIds
          toFieldPath: spec.forProvider.subnets


And apply using:

YAML
 
kubectl apply -f <your-file-name>.yaml


Claim 

I'm taking the liberty to explain the claim in more detail here.

First, it's important to note that a claim is an entirely optional entity in Crossplane. It is essentially a Kubernetes Custom Resource Definition (CRD) that the platform team can expose to application developers as a self-service interface for requesting infrastructure, such as an EKS cluster.

Think of it as an API payload: a lightweight, developer-friendly abstraction layer.

In the earlier CompositeResourceDefinition (XRD), we created the Kind XCluster. But by using a claim, application developers can interact with a much simpler and more intuitive CRD like  Cluster instead of  XCluster.

For simplicity, I have referenced the XNetwork composition name directly instead of the Network claim resource name. Crossplane creates the XNetwork resource and appends random characters to the claim name when naming it. As an additional step, you'll need to retrieve the actual XNetwork name from the Kubernetes API and use it here. While there are ways to automate this process, I’m keeping it simple here, let me know via comments if there are interest and I write more about how to automate that.

To create a claim, save the content below into a .yaml file. Please note the roleArn being referenced in this, that is the role I had mentioned earlier, AWS uses it to create other resources.

YAML
 
apiVersion: aws.platformref.crossplane.io/v1alpha1
kind: Cluster
metadata:
  name: demo-cluster
  namespace: default
spec:
  parameters:
    region: us-east-1
    roleArn: arn:aws:iam::914797696655:role/eks-crossplane-clsuter
    networkRef:
      name: crossplane-demo-network-jpv49 # <important> this is how EKS composition refers the network created earlier not the random character "jpv49" from XNetwork name


And apply using:

YAML
kubectl apply -f <your-file-name>.yaml


After this, you should see an EKS cluster in your AWS console, and ensure you are looking in the correct region. If there are any issues, look for error logs in the composite and managed resource. You could look at them using:

YAML
-- to get XCluster detail 
k get XCluster demo-cluster -o yaml  # look for reconciliation errors or messages, you will also find reference to managed resource

-- to look for status of a managed resource, example.  
k get Cluster.eks.aws.crossplane.io


As I mentioned before, below is a table where I attempt to provide another analogy for various components used in Crossplane:

component analogy

XRD

The interface, or blueprint for a product, defines what knobs users can turn

XR (XCluster)

A specific product instance with user-provided values

Composition

The function that implements all the details of the product

Claim

A customer-friendly interface for ordering the product, or an api payload.


Patch

I also want to explain an important concept we've used in our Composition: patching. You may have noticed the patches field in the .yaml blocks.

In Crossplane, a composite resource is the high-level abstraction we define — in our case, that's XCluster. Managed resources are the actual cloud resources Crossplane provisions on our behalf — for example, the AWS EKS Cluster, Nodegroup 

A patch in a Crossplane Composition is a way to copy or transform data from/to the composite resource (XCluster) to/from the managed resources (Cluster, NodeGroup, etc.).

Patching allows us to map values like region, roleArn, and names from the high-level composite to the actual underlying infrastructure — ensuring that developer inputs (or platform-defined parameters) flow all the way down to the cloud resources.

Conclusion 

Using Crossplane, you can build powerful abstractions that shield developers from the complexities of infrastructure, allowing them to focus on writing application code. These abstractions can also be made cloud-agnostic, enabling benefits like portability, cost optimization, resilience and redundancy, and greater standardization.

AWS Kubernetes Infrastructure as code

Opinions expressed by DZone contributors are their own.

Related

  • How to Build and Deploy an AI Agent on Kubernetes With AWS Bedrock, FastAPI and Helm
  • Demystifying Kubernetes on AWS: A Comparative Analysis of Deployment Options
  • How to Use Jenkins Effectively With ECS/EKS Cluster
  • Building a Platform Abstraction for AWS Networks Using Crossplane

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook