Install Crossplane on AWS with the native provider

crossplane kubernetes aws s3 native provider

4 min read | by Jordi Prats

Crossplane is an open source Kubernetes add-on that lets you create cloud resources using Kubernetes objects (CRDs). It's installation it's straightforward, but once we have it installed the key it to properly configure it's providers. Here we are going to use the crossplane's native AWS provider

First, we need to install crossplane, we can do so using helm

kubectl create namespace crossplane-system

helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update

helm install crossplane --namespace crossplane-system crossplane-stable/crossplane

Then we'll have to create an IRSA role with a trust relationship for the entire crossplane namespace (so we can have multiple providers). That would look like this:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::123456789876:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/1B45E2E0B2D55D1E1BC9FA13D02A31CD"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringLike": {
                    "oidc.eks.eu-west-1.amazonaws.com/id/BC91B45B2E0D1F02AD55D1EA13DE231C:sub": "system:serviceaccount:crossplane-system:*"
                }
            }
        }
    ]
}

Once we have the role ready, we'll need it's ARN to configure the AWS provider using IRSA we'll have to create the following objects (please notice I'm setting an specific version):

apiVersion: pkg.crossplane.io/v1alpha1
kind: ControllerConfig
metadata:
  name: aws-config
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789876:role/crossplane
spec:
  podSecurityContext:
    fsGroup: 2000
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: crossplane/provider-aws:v0.24.1
  controllerConfigRef:
    name: aws-config

Once we can see the provider's Pod running:

$ kubectl get pods 
NAME                                             READY   STATUS    RESTARTS   AGE
crossplane-fbf66ff7c7-t8t6n                      1/1     Running   0          3d3h
crossplane-rbac-manager-4bc757b79-nz7bb          1/1     Running   0          3d3h
provider-aws-f78664a342f1-575cccfd-shnxc         1/1     Running   0          1m47s

We can now also add the following object:

apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: aws-provider
spec:
  credentials:
    source: InjectedIdentity

Once applied we can check that it will have create a ServiceAccount named after the provider. We can check that it has the eks.amazonaws.com/role-arn annotations to be able to use IRSA:

$ kubectl get sa
NAME                            SECRETS   AGE
crossplane                      1         16d
default                         1         16d
provider-aws-f78664a342f1       1         19m
rbac-manager                    1         16d
$ kubectl get sa provider-aws-f78664a342f1 -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789876:role/crossplane
(...)

With these objects in place we have now the AWS provider ready to be used. To test it out we can create an S3 bucket using the object Bucket belonging to the s3.aws.crossplane.io/v1beta1 API.

Since we might have several AWS accounts configured on the cluster, providerConfigRef must match with the name of the ProviderConfig object we have just created; if not specified it looks for an ProviderConfig object named default):

apiVersion: s3.aws.crossplane.io/v1beta1
kind: Bucket
metadata:
  name: pet2cattle-xplane-test
spec:
  providerConfigRef:
    name: aws-provider
  forProvider:
    locationConstraint: 'eu-west-1'
    acl: private

Once we apply the object we can check it's events and status (for example with kubectl describe) to check whether it have been created:

$ kubectl apply -f s3bucket.yaml 
bucket.s3.aws.crossplane.io/xplane-native-aws created
$ kubectl describe bucket.s3.aws.crossplane.io/xplane-native-aws
Name:         xplane-native-aws
Namespace:    
Labels:       <none>
Annotations:  crossplane.io/external-create-pending: 2022-02-25T23:24:54Z
              crossplane.io/external-create-succeeded: 2022-02-25T23:24:55Z
              crossplane.io/external-name: xplane-native-aws
API Version:  s3.aws.crossplane.io/v1beta1
Kind:         Bucket
Metadata:
(...)
Spec:
  Deletion Policy:  Delete
  For Provider:
    Acl:                  private
    Location Constraint:  eu-west-1
    Payment Configuration:
      Payer:  BucketOwner
  Provider Config Ref:
    Name:  aws-provider
Status:
  At Provider:
    Arn:  arn:aws:s3:::xplane-native-aws
  Conditions:
    Last Transition Time:  2022-02-25T23:24:55Z
    Reason:                Available
    Status:                True
    Type:                  Ready
    Last Transition Time:  2022-02-25T23:24:55Z
    Reason:                ReconcileSuccess
    Status:                True
    Type:                  Synced
Events:
  Type    Reason                   Age   From                                 Message
  ----    ------                   ----  ----                                 -------
  Normal  CreatedExternalResource  45s   managed/bucket.s3.aws.crossplane.io  Successfully requested creation of external resource

Finally, we can check it with the AWS Console or AWS cli to verify that the bucket have been created:

$ aws s3 ls | grep xpl
2022-02-25 23:25:57 xplane-native-aws

Posted on 28/02/2022