Home GitOps Infrastructure as Code using Crossplane
Post
Cancel

GitOps Infrastructure as Code using Crossplane

Introduction

This document outlines on how we can use crossplane in integration with argo cd to create resources in cloud. This document will not dive deep into the what and how crossplane works or the argocd. Here, I am going to outline the steps the would help create resources using infrastructure as code with crossplane

Pre requisites

Crossplane using kube api, hence we we would need a kubernetes cluster. Follow tis document if you want to setup a local kubernetes cluster Setup Kubernetes cluster with Lightweight K3S

Argo CD installation

First lets setup or agro cd for gitops implementation of our infrastructure creation. the following steps are similar to what was outline in the argocd offical documentation. For more information, visti Getting started with Argocd

First, lets create a namespace,

1
kubectl create namespace argocd

install the required CRDs

1
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

Also, install the argocd cli using brew which will help in obtaining the initial login password. Now, in order test the deployment of the UI, we can do a port forward to and access the application locally at http://localhost:8080

1
kubectl port-forward svc/argocd-server -n argocd 8080:443

once, we are sure that this application is setup properly and is working, we will move with ahead and create an ingress for the traefik so, we can access the argocd dashboard at a custom domain with self signed certificates from Lets Encrypt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: argocd
  namespace: argocd
  annotations:
    kubernetes.io/ingress.class: <traefik_instance_name>
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`<custom_domain_based_on_traefik_configuration>`)
      kind: Rule
      services:
        - name: argocd-server
          port: 443
    - match: Host(`<custom_domain_based_on_traefik_configuration>`)
      kind: Rule
      services:
        - name: argocd-server
          port: 443
      middlewares:
        - name: <middleware_if_any>
  tls:
    secretName: <cert_manager_kuberters_secret_name>

Now, we can do a kubectl apply -f ingress.yaml which will allow us to access the argocd dashboard at the specified custom domain.

NOTE

In order for this to work, make sure that the cert-manager certificate, which is a kubernetes certificate, is available or copied to the newly created namespace

Crossplane

For this demonstration, I will be using Azure but we can also use crossplane to create resources in AWS and GCP. For more information refer the documentation: Getting started with Crossplane.

In order to install Crossplane, We will be utilizing the helm chart that crossplane offers, there are also several alternatives available to install crossplane mentioned in the documentation

1
2
3
4
5
6
# add crossplane helm repo
helm repo add \
crossplane-stable https://charts.crossplane.io/stable

# udpate install helm repos
helm repo update

verify the installation

1
kubectl get pods -n crossplane-system

this should output something like

1
2
3
NAME                                                  READY   STATUS    RESTARTS   AGE
crossplane-5755f8bcb6-246nn                           1/1     Running   0          173m
crossplane-rbac-manager-b98b48664-wrp2g               1/1     Running   0          173m

with this installation, we have basically created new kubernets api endpoints, which can be verified using

1
kubectl api-resources  | grep crossplane

which should output something like below,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
compositeresourcedefinitions        xrd,xrds           apiextensions.crossplane.io/v1                         false        CompositeResourceDefinition
compositionrevisions                comprev            apiextensions.crossplane.io/v1                         false        CompositionRevision
compositions                        comp               apiextensions.crossplane.io/v1                         false        Composition
environmentconfigs                  envcfg             apiextensions.crossplane.io/v1beta1                    false        EnvironmentConfig
usages                                                 apiextensions.crossplane.io/v1beta1                    false        Usage
configurationrevisions                                 pkg.crossplane.io/v1                                   false        ConfigurationRevision
configurations                                         pkg.crossplane.io/v1                                   false        Configuration
controllerconfigs                                      pkg.crossplane.io/v1alpha1                             false        ControllerConfig
deploymentruntimeconfigs                               pkg.crossplane.io/v1beta1                              false        DeploymentRuntimeConfig
functionrevisions                                      pkg.crossplane.io/v1                                   false        FunctionRevision
functions                                              pkg.crossplane.io/v1                                   false        Function
imageconfigs                                           pkg.crossplane.io/v1beta1                              false        ImageConfig
locks                                                  pkg.crossplane.io/v1beta1                              false        Lock
providerrevisions                                      pkg.crossplane.io/v1                                   false        ProviderRevision
providers                                              pkg.crossplane.io/v1                                   false        Provider
storeconfigs

Now, that we have verified the installation, we need to install CRDs required for access cloud providers api. These providers will help crossplane with creating resources using the kube api’s. For Azure, my provider.yaml file looks something like this

1
2
3
4
5
6
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-family-azure
spec:
  package: xpkg.upbound.io/upbound/provider-family-azure:v1

With this manifest, I am basically installing all the CRDs required for crossplane to communicate with azure. But there are other smaller and more focused provider that will work for any specific type of azure resources. For example, if we just want to create some virtual networks, we can use provider-azure-networks, which can be installed using the below configuration

1
2
3
4
5
6
7
8
cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-azure-network
spec:
  package: xpkg.crossplane.io/crossplane-contrib/provider-azure-network:v1.11.2
EOF

Moving on to the authentication piece, in order for crossplane to create resources, it should be able to authenticate with the environment, for this we also have couple of options like federated credentials or secrets. for simiplicity we are going to use a client secret.

1
2
3
4
5
az ad sp create-for-rbac \
--sdk-auth \
--role Owner \
--display-name <name_of_app_regirstration> \
--scopes /subscriptions/<subscription_id> > azure.json

For more information of differnet authentication methods, visit the crossplane documentation Authentications for Crossplane

The above CLI command will create a service principal with owner permissions on the specified scope and will also generate client_id, tenant_id and client_secret and store them locally in a json file. Now, this file is highly sensitive, make sure to keep it safe. Once we have this file, we are going to convert this to a kubernetes secret, which we will later use in all our crossplane configurations to create managed resources.

1
2
3
4
kubectl create secret \
generic azure-secret \
-n crossplane-system \
--from-file=creds=./azure.json

Now that we have create this secrets, we can reference this in a providerConfig.yaml file, which is basically points crossplane to which secrets to use for differnet resources. My providerConfig, looks something like this,

1
2
3
4
5
6
7
8
9
10
11
apiVersion: azure.upbound.io/v1beta1
metadata:
  name: <metadata_name>
kind: ProviderConfig
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: <secret_name>
      key: <secret_key>

Since we are trying a gitops approach, we also have to have an argocd application that is monitoring this repo for any changes and to apply them in our environment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: <application_name>
  namespace: <argocd_namespace>
spec:
  project: default
  source:
    repoURL: <gitlab/github_url>
    path: <folder_path>
    targetRevision: <branch>
  destination:
    server: <kubernetes_server_url> ## for default, leave it as is.
    namespace: <crossplane_namespace>
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

and apply this file.

Now that we have all pieces on the management side of things to create a managed resources using crossplane, I am going to create a resource group, using the follwoing yaml. Now push this file to gitlab repo

1
2
3
4
5
6
7
8
9
10
kind: ResourceGroup
metadata:
  name: <resource_group_name>
spec:
  forProvider:
    location: <location>
    tags:
      <tag_key>: <tag_value>
  providerConfigRef:
    name: <providerConfig_ref>

When we push this file to the git repo, the argo cd application will look at the changes made to this repo and will try to apply those changes. Remeber, argocd needs to have a reference, so, first we have to apply the resource group yaml file manually before leveraging argocd.

Reference

For more information on crossplane intergration with different providers, visit crossplane

This post is licensed under CC BY 4.0 by the author.