Create a Configuration

In the previous section we were able to create a PostgreSQL database because we had installed a configuration package that defined the PostgreSQLInstance type and a Composition of managed resources that mapped to it. Crossplane allows you to define your own composite resources (XRs) and compositions, then package them up to be easily distributed as OCI images. This allows you to construct a reproducible platform that exposes infrastructure APIs at your desired level of abstraction, and can be installed into any Crossplane cluster.

Create a Configuration Directory

We are going to build the same configuration package that we previously installed. It will consist of three files:

  • crossplane.yaml - Metadata about the configuration.
  • definition.yaml - The XRD.
  • composition.yaml - The Composition.

Crossplane can create a configuration from any directory with a valid crossplane.yaml metadata file at its root, and one or more XRDs or Compositions. The directory structure does not matter, as long as the crossplane.yaml file is at the root. Note that a configuration need not contain one XRD and one composition - it could include only an XRD, only a composition, several compositions, or any combination thereof.

Before we go any further, we must create a directory in which to build our configuration:

1mkdir crossplane-config
2cd crossplane-config

We’ll create the aforementioned three files in this directory, then build them into a package.

Note that definition.yaml and composition.yaml could be created directly in the Crossplane cluster without packaging them into a configuration. This can be useful for testing compositions before pushing them to a registry.

Create CompositeResourceDefinition

First we’ll create a CompositeResourceDefinition (XRD) to define the schema of our XPostgreSQLInstance and its PostgreSQLInstance resource claim.

 1apiVersion: apiextensions.crossplane.io/v1
 2kind: CompositeResourceDefinition
 3metadata:
 4  name: xpostgresqlinstances.database.example.org
 5spec:
 6  group: database.example.org
 7  names:
 8    kind: XPostgreSQLInstance
 9    plural: xpostgresqlinstances
10  claimNames:
11    kind: PostgreSQLInstance
12    plural: postgresqlinstances
13  connectionSecretKeys:
14    - username
15    - password
16    - endpoint
17    - port
18  versions:
19  - name: v1alpha1
20    served: true
21    referenceable: true
22    schema:
23      openAPIV3Schema:
24        type: object
25        properties:
26          spec:
27            type: object
28            properties:
29              parameters:
30                type: object
31                properties:
32                  storageGB:
33                    type: integer
34                required:
35                  - storageGB
36            required:
37              - parameters
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/definition.yaml

You might notice that the XRD we created specifies both “names” and “claim names”. This is because the composite resource it defines offers a composite resource claim (XRC).

Create Compositions

Now we’ll specify which managed resources our XPostgreSQLInstance XR and its claim could be composed of, and how they should be configured. We do this by defining a Composition that can satisfy the XR we defined above. In this case, our Composition will specify how to provision a public PostgreSQL instance on the chosen provider.

Note that this Composition will create an RDS instance using your default VPC, which may or may not allow connections from the internet depending on how it is configured. Select the AWS (New VPC) Composition if you wish to create an RDS instance that will allow traffic from the internet.

 1apiVersion: apiextensions.crossplane.io/v1
 2kind: Composition
 3metadata:
 4  name: xpostgresqlinstances.aws.database.example.org
 5  labels:
 6    provider: aws
 7    guide: quickstart
 8    vpc: default
 9spec:
10  writeConnectionSecretsToNamespace: crossplane-system
11  compositeTypeRef:
12    apiVersion: database.example.org/v1alpha1
13    kind: XPostgreSQLInstance
14  resources:
15    - name: rdsinstance
16      base:
17        apiVersion: database.aws.crossplane.io/v1beta1
18        kind: RDSInstance
19        spec:
20          forProvider:
21            region: us-east-1
22            dbInstanceClass: db.t2.small
23            masterUsername: masteruser
24            engine: postgres
25            engineVersion: "12"
26            skipFinalSnapshotBeforeDeletion: true
27            publiclyAccessible: true
28          writeConnectionSecretToRef:
29            namespace: crossplane-system
30      patches:
31        - fromFieldPath: "metadata.uid"
32          toFieldPath: "spec.writeConnectionSecretToRef.name"
33          transforms:
34            - type: string
35              string:
36                fmt: "%s-postgresql"
37        - fromFieldPath: "spec.parameters.storageGB"
38          toFieldPath: "spec.forProvider.allocatedStorage"
39      connectionDetails:
40        - fromConnectionSecretKey: username
41        - fromConnectionSecretKey: password
42        - fromConnectionSecretKey: endpoint
43        - fromConnectionSecretKey: port
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/aws/composition.yaml

Note: this Composition for AWS also includes several networking managed resources that are required to provision a publicly available PostgreSQL instance. Composition enables scenarios such as this, as well as far more complex ones. See the [composition] documentation for more information.

  1apiVersion: apiextensions.crossplane.io/v1
  2kind: Composition
  3metadata:
  4  name: vpcpostgresqlinstances.aws.database.example.org
  5  labels:
  6    provider: aws
  7    guide: quickstart
  8    vpc: new
  9spec:
 10  writeConnectionSecretsToNamespace: crossplane-system
 11  compositeTypeRef:
 12    apiVersion: database.example.org/v1alpha1
 13    kind: XPostgreSQLInstance
 14  resources:
 15    - name: vpc
 16      base:
 17        apiVersion: ec2.aws.crossplane.io/v1beta1
 18        kind: VPC
 19        spec:
 20          forProvider:
 21            region: us-east-1
 22            cidrBlock: 192.168.0.0/16
 23            enableDnsSupport: true
 24            enableDnsHostNames: true
 25    - name: subnet-a
 26      base:
 27        apiVersion: ec2.aws.crossplane.io/v1beta1
 28        kind: Subnet
 29        metadata:
 30          labels:
 31            zone: us-east-1a
 32        spec:
 33          forProvider:
 34            region: us-east-1
 35            cidrBlock: 192.168.64.0/18
 36            vpcIdSelector:
 37              matchControllerRef: true
 38            availabilityZone: us-east-1a
 39    - name: subnet-b
 40      base:
 41        apiVersion: ec2.aws.crossplane.io/v1beta1
 42        kind: Subnet
 43        metadata:
 44          labels:
 45            zone: us-east-1b
 46        spec:
 47          forProvider:
 48            region: us-east-1
 49            cidrBlock: 192.168.128.0/18
 50            vpcIdSelector:
 51              matchControllerRef: true
 52            availabilityZone: us-east-1b
 53    - name: subnet-c
 54      base:
 55        apiVersion: ec2.aws.crossplane.io/v1beta1
 56        kind: Subnet
 57        metadata:
 58          labels:
 59            zone: us-east-1c
 60        spec:
 61          forProvider:
 62            region: us-east-1
 63            cidrBlock: 192.168.192.0/18
 64            vpcIdSelector:
 65              matchControllerRef: true
 66            availabilityZone: us-east-1c
 67    - name: dbsubnetgroup
 68      base:
 69        apiVersion: database.aws.crossplane.io/v1beta1
 70        kind: DBSubnetGroup
 71        spec:
 72          forProvider:
 73            region: us-east-1
 74            description: An excellent formation of subnetworks.
 75            subnetIdSelector:
 76              matchControllerRef: true
 77    - name: internetgateway
 78      base:
 79        apiVersion: ec2.aws.crossplane.io/v1beta1
 80        kind: InternetGateway
 81        spec:
 82          forProvider:
 83            region: us-east-1
 84            vpcIdSelector:
 85              matchControllerRef: true
 86    - name: routetable
 87      base:
 88        apiVersion: ec2.aws.crossplane.io/v1beta1
 89        kind: RouteTable
 90        spec:
 91          forProvider:
 92            region: us-east-1
 93            vpcIdSelector:
 94              matchControllerRef: true
 95            routes:
 96              - destinationCidrBlock: 0.0.0.0/0
 97                gatewayIdSelector:
 98                  matchControllerRef: true
 99            associations:
100              - subnetIdSelector:
101                  matchLabels:
102                    zone: us-east-1a
103              - subnetIdSelector:
104                  matchLabels:
105                    zone: us-east-1b
106              - subnetIdSelector:
107                  matchLabels:
108                    zone: us-east-1c
109    - name: securitygroup
110      base:
111        apiVersion: ec2.aws.crossplane.io/v1beta1
112        kind: SecurityGroup
113        spec:
114          forProvider:
115            region: us-east-1
116            vpcIdSelector:
117              matchControllerRef: true
118            groupName: crossplane-getting-started
119            description: Allow access to PostgreSQL
120            ingress:
121              - fromPort: 5432
122                toPort: 5432
123                ipProtocol: tcp
124                ipRanges:
125                  - cidrIp: 0.0.0.0/0
126                    description: Everywhere
127    - name: rdsinstance
128      base:
129        apiVersion: database.aws.crossplane.io/v1beta1
130        kind: RDSInstance
131        spec:
132          forProvider:
133            region: us-east-1
134            dbSubnetGroupNameSelector:
135              matchControllerRef: true
136            vpcSecurityGroupIDSelector:
137              matchControllerRef: true
138            dbInstanceClass: db.t2.small
139            masterUsername: masteruser
140            engine: postgres
141            engineVersion: "12"
142            skipFinalSnapshotBeforeDeletion: true
143            publiclyAccessible: true
144          writeConnectionSecretToRef:
145            namespace: crossplane-system
146      patches:
147        - fromFieldPath: "metadata.uid"
148          toFieldPath: "spec.writeConnectionSecretToRef.name"
149          transforms:
150            - type: string
151              string:
152                fmt: "%s-postgresql"
153        - fromFieldPath: "spec.parameters.storageGB"
154          toFieldPath: "spec.forProvider.allocatedStorage"
155      connectionDetails:
156        - fromConnectionSecretKey: username
157        - fromConnectionSecretKey: password
158        - fromConnectionSecretKey: endpoint
159        - fromConnectionSecretKey: port
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/aws-with-vpc/composition.yaml
 1apiVersion: apiextensions.crossplane.io/v1
 2kind: Composition
 3metadata:
 4  name: xpostgresqlinstances.gcp.database.example.org
 5  labels:
 6    provider: gcp
 7    guide: quickstart
 8spec:
 9  writeConnectionSecretsToNamespace: crossplane-system
10  compositeTypeRef:
11    apiVersion: database.example.org/v1alpha1
12    kind: XPostgreSQLInstance
13  resources:
14    - name: cloudsqlinstance
15      base:
16        apiVersion: database.gcp.crossplane.io/v1beta1
17        kind: CloudSQLInstance
18        spec:
19          forProvider:
20            databaseVersion: POSTGRES_12
21            region: us-central1
22            settings:
23              tier: db-custom-1-3840
24              dataDiskType: PD_SSD
25              ipConfiguration:
26                ipv4Enabled: true
27                authorizedNetworks:
28                  - value: "0.0.0.0/0"
29          writeConnectionSecretToRef:
30            namespace: crossplane-system
31      patches:
32        - fromFieldPath: "metadata.uid"
33          toFieldPath: "spec.writeConnectionSecretToRef.name"
34          transforms:
35            - type: string
36              string:
37                fmt: "%s-postgresql"
38        - fromFieldPath: "spec.parameters.storageGB"
39          toFieldPath: "spec.forProvider.settings.dataDiskSizeGb"
40      connectionDetails:
41        - fromConnectionSecretKey: username
42        - fromConnectionSecretKey: password
43        - fromConnectionSecretKey: endpoint
44        - type: FromValue
45          name: port
46          value: "5432"
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/gcp/composition.yaml

Note: the Composition for Azure also includes a ResourceGroup and PostgreSQLServerFirewallRule that are required to provision a publicly available PostgreSQL instance on Azure. Composition enables scenarios such as this, as well as far more complex ones. See the [composition] documentation for more information.

 1apiVersion: apiextensions.crossplane.io/v1
 2kind: Composition
 3metadata:
 4  name: xpostgresqlinstances.azure.database.example.org
 5  labels:
 6    provider: azure
 7    guide: quickstart
 8spec:
 9  writeConnectionSecretsToNamespace: crossplane-system
10  compositeTypeRef:
11    apiVersion: database.example.org/v1alpha1
12    kind: XPostgreSQLInstance
13  resources:
14    - name: resourcegroup
15      base:
16        apiVersion: azure.crossplane.io/v1alpha3
17        kind: ResourceGroup
18        spec:
19          location: West US 2
20    - name: postgresqlserver
21      base:
22        apiVersion: database.azure.crossplane.io/v1beta1
23        kind: PostgreSQLServer
24        spec:
25          forProvider:
26            administratorLogin: myadmin
27            resourceGroupNameSelector:
28              matchControllerRef: true
29            location: West US 2
30            sslEnforcement: Disabled
31            version: "9.6"
32            sku:
33              tier: GeneralPurpose
34              capacity: 2
35              family: Gen5
36          writeConnectionSecretToRef:
37            namespace: crossplane-system
38      patches:
39        - fromFieldPath: "metadata.uid"
40          toFieldPath: "spec.writeConnectionSecretToRef.name"
41          transforms:
42            - type: string
43              string:
44                fmt: "%s-postgresql"
45        - fromFieldPath: "spec.parameters.storageGB"
46          toFieldPath: "spec.forProvider.storageProfile.storageMB"
47          transforms:
48            - type: math
49              math:
50                multiply: 1024
51      connectionDetails:
52        - fromConnectionSecretKey: username
53        - fromConnectionSecretKey: password
54        - fromConnectionSecretKey: endpoint
55        - type: FromValue
56          name: port
57          value: "5432"
58    - name: firewallrule
59      base:
60        apiVersion: database.azure.crossplane.io/v1alpha3
61        kind: PostgreSQLServerFirewallRule
62        spec:
63          forProvider:
64            serverNameSelector:
65              matchControllerRef: true
66            resourceGroupNameSelector:
67              matchControllerRef: true
68            properties:
69              startIpAddress: 0.0.0.0
70              endIpAddress: 255.255.255.254
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/azure/composition.yaml

Build and Push The Configuration

Finally, we’ll author our metadata file then build and push our configuration so that Crossplane users may install it.

Note that Crossplane pushes packages to an OCI registry - currently Docker Hub by default. You may need to run docker login before you are able to push a package.

 1apiVersion: meta.pkg.crossplane.io/v1
 2kind: Configuration
 3metadata:
 4  name: getting-started-with-aws
 5  annotations:
 6    guide: quickstart
 7    provider: aws
 8    vpc: default
 9spec:
10  crossplane:
11    version: ">=v1.4.0-0"
12  dependsOn:
13    - provider: xpkg.upbound.io/crossplane-contrib/provider-aws
14      version: ">=v0.18.2"
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/aws/crossplane.yaml
2
3kubectl crossplane build configuration

You should see a file in your working directory with a .xpkg extension. The Crossplane CLI will automatically tag and push it to the registry of your choosing in the next step if it is the only .xpkg in the directory. Otherwise you may specify a specific package by using the -f flag.

1# Set this to the Docker Hub username or OCI registry you wish to use.
2REG=my-package-repo
3kubectl crossplane push configuration ${REG}/getting-started-with-aws:v1.10.2

Note that the Crossplane CLI will not follow symbolic links for files in the root package directory.

 1apiVersion: meta.pkg.crossplane.io/v1
 2kind: Configuration
 3metadata:
 4  name: getting-started-with-aws-with-vpc
 5  annotations:
 6    guide: quickstart
 7    provider: aws
 8    vpc: new
 9spec:
10  crossplane:
11    version: ">=v1.4.0-0"
12  dependsOn:
13    - provider: xpkg.upbound.io/crossplane-contrib/provider-aws
14      version: ">=v0.18.2"
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/aws-with-vpc/crossplane.yaml
2
3kubectl crossplane build configuration

You should see a file in your working directory with a .xpkg extension. The Crossplane CLI will automatically tag and push it to the registry of your choosing in the next step if it is the only .xpkg in the directory. Otherwise you may specify a specific package by using the -f flag.

1# Set this to the Docker Hub username or OCI registry you wish to use.
2REG=my-package-repo
3kubectl crossplane push configuration ${REG}/getting-started-with-aws-with-vpc:v1.10.2

Note that the Crossplane CLI will not follow symbolic links for files in the root package directory.

 1apiVersion: meta.pkg.crossplane.io/v1
 2kind: Configuration
 3metadata:
 4  name: getting-started-with-gcp
 5  annotations:
 6    guide: quickstart
 7    provider: gcp
 8spec:
 9  crossplane:
10    version: ">=v1.4.0-0"
11  dependsOn:
12    - provider: xpkg.upbound.io/crossplane-contrib/provider-gcp
13      version: ">=v0.13.0"
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/gcp/crossplane.yaml
2
3kubectl crossplane build configuration

You should see a file in your working directory with a .xpkg extension. The Crossplane CLI will automatically tag and push it to the registry of your choosing in the next step if it is the only .xpkg in the directory. Otherwise you may specify a specific package by using the -f flag.

1# Set this to the Docker Hub username or OCI registry you wish to use.
2REG=my-package-repo
3kubectl crossplane push configuration ${REG}/getting-started-with-gcp:v1.10.2

Note that the Crossplane CLI will not follow symbolic links for files in the root package directory.

 1apiVersion: meta.pkg.crossplane.io/v1
 2kind: Configuration
 3metadata:
 4  name: getting-started-with-azure
 5  annotations:
 6    guide: quickstart
 7    provider: azure
 8spec:
 9  crossplane:
10    version: ">=v1.4.0-0"
11  dependsOn:
12    - provider: xpkg.upbound.io/crossplane-contrib/provider-azure
13      version: ">=v0.13.0"
1curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/azure/crossplane.yaml
2
3kubectl crossplane build configuration

You should see a file in your working directory with a .xpkg extension. The Crossplane CLI will automatically tag and push it to the registry of your choosing in the next step if it is the only .xpkg in the directory. Otherwise you may specify a specific package by using the -f flag.

1# Set this to the Docker Hub username or OCI registry you wish to use.
2REG=my-package-repo
3kubectl crossplane push configuration ${REG}/getting-started-with-azure:v1.10.2

Note that the Crossplane CLI will not follow symbolic links for files in the root package directory.

That’s it! You’ve now built and pushed your package. Take a look at the Crossplane packages documentation for more information about installing and working with packages, or read about other Crossplane concepts.

Clean Up

To clean up, you can simply delete your package directory:

1cd ..
2rm -rf crossplane-config