It is short tutorial how to deploy PostreSQL server on Azure using Crossplane.

k8s and crossplane

We need k8s cluster to run Crossplane. I’m using microk8s here on Ubuntu 24.10.

Install microk8s:

sudo snap install microk8s --classic
microk8s status --wait-ready
sudo usermod -a -G microk8s q84fh
newgrp microk8s
mkdir -p "${HOME}/.kube"
microk8s config > "${HOME}/.kube/config"
sudo chown -R q84fh ~/.kube
kubectl get nodes

Then we need Crossplane CLI (previously it was named crank)

Install Crossplane CLI:

curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh
# you may alternativly move it to ~/bin/ and include it in your $PATH)
sudo mv crossplane /usr/local/bin 
crossplane --help

Install Crossplane

kubectl create namespace crossplane-system
microk8s helm repo add crossplane-stable https://charts.crossplane.io/stable
microk8s helm repo update
microk8s helm install crossplane --namespace crossplane-system crossplane-stable/crossplane
kubectl get pods -n crossplane-system
crossplane version

Azure

To manage Azure resources, you need to install the Azure provider and configure Crossplane to use your Azure account.

Create a YAML manifest for the Azure provider:

azure.yaml:

# by default logs are quiet, we would like to have them verbose for learning
apiVersion: pkg.crossplane.io/v1alpha1
kind: ControllerConfig
metadata:
  name: debug-config
spec:
  args:
    - --debug
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: default
spec:
  package: xpkg.upbound.io/crossplane-contrib/provider-azure:v0.20.1
  controllerConfigRef:
    name: debug-config
---
apiVersion: azure.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: azure-creds
      key: creds

Create azure credentials (I’m using jq here to check JSON syntax on the fly, it is not really needed):

az login;
az ad sp create-for-rbac --sdk-auth --role Owner --scopes /subscriptions/[YOUR SUBSCRIBTION ID] | jq > azure-creds.json;
kubectl create secret generic azure-creds -n crossplane-system --from-file=creds=./azure-creds.json;
kubectl apply -f azure.yaml;
kubectl get provider;

Now Crossplane should be able to talk with your Azure subscribtion:

Verify by creating resource group:

example.yaml:

apiVersion: azure.crossplane.io/v1alpha3
kind: ResourceGroup
metadata:
  name: example-rg
spec:
  location: WestUS

Let’s apply it and check in Azure console if it was created:

kubectl apply -f example.yaml
watch crossplane beta trace ResourceGroup example-rg -o wide -n crossplane-system

Extend example.yaml to include PostreSQL server:

apiVersion: azure.crossplane.io/v1alpha3
kind: ResourceGroup
metadata:
  name: example-rg
spec:
  location: WestUS
---
apiVersion: database.azure.crossplane.io/v1beta1
kind: PostgreSQLServer
metadata:
  name: example-sqlserver
spec:
  forProvider:
    administratorLogin: q84fh
    location: WestUS
    resourceGroupNameRef:
      name: example-rg
    sslEnforcement: Enabled
    sku:
      tier: GeneralPurpose
      capacity: 2
      family: Gen5 
    storageProfile:
      backupRetentionDays: 7
      geoRedundantBackup: Disabled
      storageAutogrow: Disabled
      storageMB: 5120
    version: "9.6"
  publishConnectionDetailsTo:
    name: postgresql-creds
    namespace: crossplane-system

And thats it!

Teardown

Let’s remove what we’ve created:

kubectl delete -f example.yaml;
# check if it is all gone in Azure console
sudo snap remove microk8s --purge;