--- title: Azure Quickstart Part 2 weight: 120 tocHidden: true aliases: - /master/getting-started/provider-azure-part-3 --- {{< hint "important" >}} This guide is part 2 of a series. [**Part 1**]({{}}) covers to installing Crossplane and connect your Kubernetes cluster to Azure. {{< /hint >}} This guide walks you through building and accessing a custom API with Crossplane. ## Prerequisites * Complete [quickstart part 1]({{}}) connecting Kubernetes to Azure. * an Azure account with permissions to create an Azure Virtual Machine, Resource Group and Virtual Networking. {{}} 1. Add the Crossplane Helm repository and install Crossplane ```shell helm repo add \ crossplane-stable https://charts.crossplane.io/stable helm repo update && helm install crossplane \ crossplane-stable/crossplane \ --namespace crossplane-system \ --create-namespace ``` 2. When the Crossplane pods finish installing and are ready, apply the Azure Provider ```yaml {label="provider",copy-lines="all"} cat <}} ```console az ad sp create-for-rbac \ --sdk-auth \ --role Owner \ --scopes /subscriptions/$@$@ ``` {{}} 4. Create a Kubernetes secret from the Azure JSON file. ```shell {label="kube-create-secret",copy-lines="all"} kubectl create secret \ generic azure-secret \ -n crossplane-system \ --from-file=creds=./azure-credentials.json ``` 5. Create a _ProviderConfig_ ```yaml {label="providerconfig",copy-lines="all"} cat <}} ## Create a custom API Crossplane allows you to build your own custom APIs for your users, abstracting away details about the cloud provider and their resources. You can make your API as complex or simple as you wish. The custom API is a Kubernetes object. Here is an example custom API. ```yaml {label="exAPI"} apiVersion: compute.example.com/v1alpha1 kind: VirtualMachine metadata: name: my-vm spec: location: "US" ``` Like any Kubernetes object the API has a {{}}version{{}}, {{}}kind{{}} and {{}}spec{{}}. ### Define a group and version To create your own API start by defining an [API group](https://kubernetes.io/docs/reference/using-api/#api-groups) and [version](https://kubernetes.io/docs/reference/using-api/#api-versioning). The _group_ can be any value, but common convention is to map to a fully qualified domain name. The version shows how mature or stable the API is and increments when changing, adding or removing fields in the API. Crossplane doesn't require specific versions or a specific version naming convention, but following [Kubernetes API versioning guidelines](https://kubernetes.io/docs/reference/using-api/#api-versioning) is strongly recommended. * `v1alpha1` - A new API that may change at any time. * `v1beta1` - An existing API that's considered stable. Breaking changes are strongly discouraged. * `v1` - A stable API that doesn't have breaking changes. This guide uses the group {{}}compute.example.com{{}}. Because this is the first version of the API, this guide uses the version {{}}v1alpha1{{}}. ```yaml {label="version",copy-lines="none"} apiVersion: compute.example.com/v1alpha1 ``` ### Define a kind The API group is a logical collection of related APIs. In a group are individual kinds representing different resources. For example a `compute` group may have a `VirtualMachine` and `BareMetal` kinds. The `kind` can be anything, but it must be [UpperCamelCased](https://kubernetes.io/docs/contribute/style/style-guide/#use-upper-camel-case-for-api-objects). This API's kind is {{}}VirtualMachine{{}} ```yaml {label="kind",copy-lines="none"} apiVersion: compute.example.com/v1alpha1 kind: VirtualMachine ``` ### Define a spec The most important part of an API is the schema. The schema defines the inputs accepted from users. This API allows users to provide a {{}}location{{}} of where to run their cloud resources. All other resource settings can't be configurable by the users. This allows Crossplane to enforce any policies and standards without worrying about user errors. ```yaml {label="spec",copy-lines="none"} apiVersion: compute.example.com/v1alpha1 kind: VirtualMachine spec: location: "US" ``` ### Apply the API Crossplane uses {{}}Composite Resource Definitions{{}} (also called an `XRD`) to install your custom API in Kubernetes. The XRD {{}}spec{{}} contains all the information about the API including the {{}}group{{}}, {{}}version{{}}, {{}}kind{{}} and {{}}schema{{}}. The XRD's {{}}name{{}} must be the combination of the {{}}plural{{}} and {{}}group{{}}. The {{}}schema{{}} uses the {{}}OpenAPIv3{{}} specification to define the API {{}}spec{{}}. The API defines a {{}}location{{}} that must be {{}}oneOf{{}} either {{}}EU{{}} or {{}}US{{}}. Apply this XRD to create the custom API in your Kubernetes cluster. ```yaml {label="xrd",copy-lines="all"} cat <}}claimNames{{}} allows users to access this API either at the cluster level with the {{}}VirtualMachine{{}} endpoint or in a namespace with the {{}}VirtualMachineClaim{{}} endpoint. The namespace scoped API is a Crossplane _Claim_. {{}} For more details on the fields and options of Composite Resource Definitions read the [XRD documentation]({{}}). {{< /hint >}} View the installed XRD with `kubectl get xrd`. ```shell {copy-lines="1"} kubectl get xrd NAME ESTABLISHED OFFERED AGE virtualmachines.compute.example.com True True 43s ``` View the new custom API endpoints with `kubectl api-resources | grep VirtualMachine` ```shell {copy-lines="1",label="apiRes"} kubectl api-resources | grep VirtualMachine virtualmachineclaims compute.example.com/v1alpha1 true VirtualMachineClaim virtualmachines compute.example.com/v1alpha1 false VirtualMachine ``` ## Create a deployment template When users access the custom API Crossplane takes their inputs and combines them with a template describing what infrastructure to deploy. Crossplane calls this template a _Composition_. The {{}}Composition{{}} defines all the cloud resources to deploy. Each entry in the template is a full resource definitions, defining all the resource settings and metadata like labels and annotations. This template creates an Azure {{}}LinuxVirtualMachine{{}} {{}}NetworkInterface{{}}, {{}}Subnet{{}} {{}}VirtualNetwork{{}} and {{}}ResourceGroup{{}}. This Composition takes the user's {{}}location{{}} input and uses it as the {{}}location{{}} used in the individual resource. {{}} This Composition uses an array of resource templates. You can patch each template with data copied from the custom API. Crossplane calls this a _Patch and Transform_ Composition. You don't have to use Patch and Transform. Crossplane supports a variety of alternatives, including Go Templating and CUE. You can also write a function in Go or Python to template your resources. Read the [Composition documentation]({{}}) for more information on configuring Compositions and all the available options. {{< /hint >}} Apply this Composition to your cluster. ```yaml {label="comp",copy-lines="all"} cat <}}compositeTypeRef{{}} defines which custom APIs can use this template to create resources. A Composition uses a pipeline of _composition functions_ to define the cloud resources to deploy. This template uses {{}}function-patch-and-transform{{}}. You must install the function before you can use it in a Composition. Apply this Function to install `function-patch-and-transform`: ```yaml {label="install"} cat <}} Read the [Composition documentation]({{}}) for more information on configuring Compositions and all the available options. Read the [Patch and Transform function documentation]({{}}) for more information on how it uses patches to map user inputs to Composition resource templates. {{< /hint >}} View the Composition with `kubectl get composition` ```shell {copy-lines="1"} kubectl get composition NAME XR-KIND XR-APIVERSION AGE crossplane-quickstart-vm-with-network XVirtualMachine custom-api.example.org/v1alpha1 77s ``` ## Install the Azure virtual machine provider Part 1 only installed the Azure Virtual Network Provider. To deploying virtual machines requires the Azure Compute provider as well. Add the new Provider to the cluster. ```yaml cat <}}VirtualMachine{{}} object to create the cloud resources. ```yaml {copy-lines="all",label="xr"} cat <}} It may take up to five minutes for the resources to provision. {{< /hint >}} ```shell {copy-lines="1"} kubectl get VirtualMachine NAME SYNCED READY COMPOSITION AGE my-vm True True crossplane-quickstart-vm-with-network 3m3s ``` This object is a Crossplane _composite resource_ (also called an `XR`). It's a single object representing the collection of resources created from the Composition template. View the individual resources with `kubectl get managed` ```shell {copy-lines="1"} kubectl get managed NAME READY SYNCED EXTERNAL-NAME AGE resourcegroup.azure.upbound.io/my-vm-7jb4n True True my-vm-7jb4n 3m43s NAME READY SYNCED EXTERNAL-NAME AGE linuxvirtualmachine.compute.azure.upbound.io/my-vm-5h7p4 True True my-vm-5h7p4 3m43s NAME READY SYNCED EXTERNAL-NAME AGE networkinterface.network.azure.upbound.io/my-vm-j7fpx True True my-vm-j7fpx 3m43s NAME READY SYNCED EXTERNAL-NAME AGE subnet.network.azure.upbound.io/my-vm-b2dqt True True my-vm-b2dqt 3m43s NAME READY SYNCED EXTERNAL-NAME AGE virtualnetwork.network.azure.upbound.io/my-vm-pd2sw True True my-vm-pd2sw 3m43s ``` Accessing the API created all five resources defined in the template and linked them together. Look at a specific resource to see it's created in the location used in the API. ```yaml {copy-lines="1"} kubectl describe linuxvirtualmachine | grep Location Location: Sweden Central Location: swedencentral ``` Delete the resources with `kubectl delete VirtualMachine`. ```shell {copy-lines="1"} kubectl delete VirtualMachine my-vm virtualmachine.compute.example.com "my-vm" deleted ``` Verify Crossplane deleted the resources with `kubectl get managed` {{}} It may take up to 5 minutes to delete the resources. {{< /hint >}} ```shell {copy-lines="1"} kubectl get managed No resources found ``` ## Using the API with namespaces Accessing the API `VirtualMachine` happens at the cluster scope. Most organizations isolate their users into namespaces. A Crossplane _Claim_ is the custom API in a namespace. Creating a _Claim_ is just like accessing the custom API endpoint, but with the {{}}kind{{}} from the custom API's `claimNames`. Create a new namespace to test create a Claim in. ```shell kubectl create namespace crossplane-test ``` Then create a Claim in the `crossplane-test` namespace. ```yaml {label="claim",copy-lines="all"} cat <}} It may take up to 5 minutes to delete the resources. {{< /hint >}} Verify Crossplane deleted the composite resource with `kubectl get composite`. ```shell {copy-lines="1"} kubectl get composite No resources found ``` Verify Crossplane deleted the managed resources with `kubectl get managed`. ```shell {copy-lines="1"} kubectl get managed No resources found ``` ## Next steps * Explore Azure resources that Crossplane can configure in the [Provider CRD reference](https://github.com/crossplane-contrib/provider-upjet-azure/tree/main/package/crds). * Join the [Crossplane Slack](https://slack.crossplane.io/) and connect with Crossplane users and contributors. * Read more about the [Crossplane concepts]({{}}) to find out what else you can do with Crossplane.