---
title: Create a Configuration
weight: 4
---

{{<hint "important" >}}
This document has moved in recent versions of Crossplane documentation. 

Current Crossplane documentation versions introduce configurations as part of
the [Getting Started]({{<ref "/v1.11/getting-started" >}}) guides. 
{{</hint >}}

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:

```console
mkdir crossplane-config
cd 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.

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqlinstances.database.example.org
spec:
  group: database.example.org
  names:
    kind: XPostgreSQLInstance
    plural: xpostgresqlinstances
  claimNames:
    kind: PostgreSQLInstance
    plural: postgresqlinstances
  connectionSecretKeys:
    - username
    - password
    - endpoint
    - port
  versions:
  - name: v1alpha1
    served: true
    referenceable: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              parameters:
                type: object
                properties:
                  storageGB:
                    type: integer
                required:
                  - storageGB
            required:
              - parameters
```

```console
curl -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.

{{< tabs >}}
{{< tab "AWS (Default VPC)" >}}

> 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.

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xpostgresqlinstances.aws.database.example.org
  labels:
    provider: aws
    guide: quickstart
    vpc: default
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef:
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQLInstance
  resources:
    - name: rdsinstance
      base:
        apiVersion: database.aws.crossplane.io/v1beta1
        kind: RDSInstance
        spec:
          forProvider:
            region: us-east-1
            dbInstanceClass: db.t2.small
            masterUsername: masteruser
            engine: postgres
            engineVersion: "12"
            skipFinalSnapshotBeforeDeletion: true
            publiclyAccessible: true
          writeConnectionSecretToRef:
            namespace: crossplane-system
      patches:
        - fromFieldPath: "metadata.uid"
          toFieldPath: "spec.writeConnectionSecretToRef.name"
          transforms:
            - type: string
              string:
                fmt: "%s-postgresql"
        - fromFieldPath: "spec.parameters.storageGB"
          toFieldPath: "spec.forProvider.allocatedStorage"
      connectionDetails:
        - fromConnectionSecretKey: username
        - fromConnectionSecretKey: password
        - fromConnectionSecretKey: endpoint
        - fromConnectionSecretKey: port
```

```console
curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/aws/composition.yaml
```

{{< /tab >}}
{{< tab "AWS (New VPC)" >}}

> 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.

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: vpcpostgresqlinstances.aws.database.example.org
  labels:
    provider: aws
    guide: quickstart
    vpc: new
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef:
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQLInstance
  resources:
    - name: vpc
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: VPC
        spec:
          forProvider:
            region: us-east-1
            cidrBlock: 192.168.0.0/16
            enableDnsSupport: true
            enableDnsHostNames: true
    - name: subnet-a
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: Subnet
        metadata:
          labels:
            zone: us-east-1a
        spec:
          forProvider:
            region: us-east-1
            cidrBlock: 192.168.64.0/18
            vpcIdSelector:
              matchControllerRef: true
            availabilityZone: us-east-1a
    - name: subnet-b
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: Subnet
        metadata:
          labels:
            zone: us-east-1b
        spec:
          forProvider:
            region: us-east-1
            cidrBlock: 192.168.128.0/18
            vpcIdSelector:
              matchControllerRef: true
            availabilityZone: us-east-1b
    - name: subnet-c
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: Subnet
        metadata:
          labels:
            zone: us-east-1c
        spec:
          forProvider:
            region: us-east-1
            cidrBlock: 192.168.192.0/18
            vpcIdSelector:
              matchControllerRef: true
            availabilityZone: us-east-1c
    - name: dbsubnetgroup
      base:
        apiVersion: database.aws.crossplane.io/v1beta1
        kind: DBSubnetGroup
        spec:
          forProvider:
            region: us-east-1
            description: An excellent formation of subnetworks.
            subnetIdSelector:
              matchControllerRef: true
    - name: internetgateway
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: InternetGateway
        spec:
          forProvider:
            region: us-east-1
            vpcIdSelector:
              matchControllerRef: true
    - name: routetable
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: RouteTable
        spec:
          forProvider:
            region: us-east-1
            vpcIdSelector:
              matchControllerRef: true
            routes:
              - destinationCidrBlock: 0.0.0.0/0
                gatewayIdSelector:
                  matchControllerRef: true
            associations:
              - subnetIdSelector:
                  matchLabels:
                    zone: us-east-1a
              - subnetIdSelector:
                  matchLabels:
                    zone: us-east-1b
              - subnetIdSelector:
                  matchLabels:
                    zone: us-east-1c
    - name: securitygroup
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: SecurityGroup
        spec:
          forProvider:
            region: us-east-1
            vpcIdSelector:
              matchControllerRef: true
            groupName: crossplane-getting-started
            description: Allow access to PostgreSQL
            ingress:
              - fromPort: 5432
                toPort: 5432
                ipProtocol: tcp
                ipRanges:
                  - cidrIp: 0.0.0.0/0
                    description: Everywhere
    - name: rdsinstance
      base:
        apiVersion: database.aws.crossplane.io/v1beta1
        kind: RDSInstance
        spec:
          forProvider:
            region: us-east-1
            dbSubnetGroupNameSelector:
              matchControllerRef: true
            vpcSecurityGroupIDSelector:
              matchControllerRef: true
            dbInstanceClass: db.t2.small
            masterUsername: masteruser
            engine: postgres
            engineVersion: "12"
            skipFinalSnapshotBeforeDeletion: true
            publiclyAccessible: true
          writeConnectionSecretToRef:
            namespace: crossplane-system
      patches:
        - fromFieldPath: "metadata.uid"
          toFieldPath: "spec.writeConnectionSecretToRef.name"
          transforms:
            - type: string
              string:
                fmt: "%s-postgresql"
        - fromFieldPath: "spec.parameters.storageGB"
          toFieldPath: "spec.forProvider.allocatedStorage"
      connectionDetails:
        - fromConnectionSecretKey: username
        - fromConnectionSecretKey: password
        - fromConnectionSecretKey: endpoint
        - fromConnectionSecretKey: port
```

```console
curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/aws-with-vpc/composition.yaml
```

{{< /tab >}}
{{< tab "GCP" >}}

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xpostgresqlinstances.gcp.database.example.org
  labels:
    provider: gcp
    guide: quickstart
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef:
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQLInstance
  resources:
    - name: cloudsqlinstance
      base:
        apiVersion: database.gcp.crossplane.io/v1beta1
        kind: CloudSQLInstance
        spec:
          forProvider:
            databaseVersion: POSTGRES_12
            region: us-central1
            settings:
              tier: db-custom-1-3840
              dataDiskType: PD_SSD
              ipConfiguration:
                ipv4Enabled: true
                authorizedNetworks:
                  - value: "0.0.0.0/0"
          writeConnectionSecretToRef:
            namespace: crossplane-system
      patches:
        - fromFieldPath: "metadata.uid"
          toFieldPath: "spec.writeConnectionSecretToRef.name"
          transforms:
            - type: string
              string:
                fmt: "%s-postgresql"
        - fromFieldPath: "spec.parameters.storageGB"
          toFieldPath: "spec.forProvider.settings.dataDiskSizeGb"
      connectionDetails:
        - fromConnectionSecretKey: username
        - fromConnectionSecretKey: password
        - fromConnectionSecretKey: endpoint
        - type: FromValue
          name: port
          value: "5432"
```

```console
curl -OL https://raw.githubusercontent.com/crossplane/docs/master/content/v1.10/snippets/package/gcp/composition.yaml
```

{{< /tab >}}
{{< tab "Azure" >}}

> 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.

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xpostgresqlinstances.azure.database.example.org
  labels:
    provider: azure
    guide: quickstart
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef:
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQLInstance
  resources:
    - name: resourcegroup
      base:
        apiVersion: azure.crossplane.io/v1alpha3
        kind: ResourceGroup
        spec:
          location: West US 2
    - name: postgresqlserver
      base:
        apiVersion: database.azure.crossplane.io/v1beta1
        kind: PostgreSQLServer
        spec:
          forProvider:
            administratorLogin: myadmin
            resourceGroupNameSelector:
              matchControllerRef: true
            location: West US 2
            sslEnforcement: Disabled
            version: "9.6"
            sku:
              tier: GeneralPurpose
              capacity: 2
              family: Gen5
          writeConnectionSecretToRef:
            namespace: crossplane-system
      patches:
        - fromFieldPath: "metadata.uid"
          toFieldPath: "spec.writeConnectionSecretToRef.name"
          transforms:
            - type: string
              string:
                fmt: "%s-postgresql"
        - fromFieldPath: "spec.parameters.storageGB"
          toFieldPath: "spec.forProvider.storageProfile.storageMB"
          transforms:
            - type: math
              math:
                multiply: 1024
      connectionDetails:
        - fromConnectionSecretKey: username
        - fromConnectionSecretKey: password
        - fromConnectionSecretKey: endpoint
        - type: FromValue
          name: port
          value: "5432"
    - name: firewallrule
      base:
        apiVersion: database.azure.crossplane.io/v1alpha3
        kind: PostgreSQLServerFirewallRule
        spec:
          forProvider:
            serverNameSelector:
              matchControllerRef: true
            resourceGroupNameSelector:
              matchControllerRef: true
            properties:
              startIpAddress: 0.0.0.0
              endIpAddress: 255.255.255.254
```

```console
curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/azure/composition.yaml
```

{{< /tab >}}
{{< /tabs >}}

## 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.

{{< tabs >}}
{{< tab "AWS (Default VPC)" >}}

```yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Configuration
metadata:
  name: getting-started-with-aws
  annotations:
    guide: quickstart
    provider: aws
    vpc: default
spec:
  crossplane:
    version: ">=v1.4.0-0"
  dependsOn:
    - provider: xpkg.upbound.io/crossplane-contrib/provider-aws
      version: ">=v0.18.2"
```

```console
curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/aws/crossplane.yaml

kubectl 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.

```console
# Set this to the Docker Hub username or OCI registry you wish to use.
REG=my-package-repo
kubectl 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.

{{< /tab >}}
{{< tab "AWS (New VPC)" >}}

```yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Configuration
metadata:
  name: getting-started-with-aws-with-vpc
  annotations:
    guide: quickstart
    provider: aws
    vpc: new
spec:
  crossplane:
    version: ">=v1.4.0-0"
  dependsOn:
    - provider: xpkg.upbound.io/crossplane-contrib/provider-aws
      version: ">=v0.18.2"
```

```console
curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/aws-with-vpc/crossplane.yaml

kubectl 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.

```console
# Set this to the Docker Hub username or OCI registry you wish to use.
REG=my-package-repo
kubectl 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.

{{< /tab >}}
{{< tab "GCP" >}}

```yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Configuration
metadata:
  name: getting-started-with-gcp
  annotations:
    guide: quickstart
    provider: gcp
spec:
  crossplane:
    version: ">=v1.4.0-0"
  dependsOn:
    - provider: xpkg.upbound.io/crossplane-contrib/provider-gcp
      version: ">=v0.13.0"
```

```console
curl -OL https://raw.githubusercontent.com/crossplane/docs/master/content/v1.10/snippets/package/gcp/crossplane.yaml

kubectl 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.

```console
# Set this to the Docker Hub username or OCI registry you wish to use.
REG=my-package-repo
kubectl 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.

{{< /tab >}}
{{< tab "Azure" >}}

```yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Configuration
metadata:
  name: getting-started-with-azure
  annotations:
    guide: quickstart
    provider: azure
spec:
  crossplane:
    version: ">=v1.4.0-0"
  dependsOn:
    - provider: xpkg.upbound.io/crossplane-contrib/provider-azure
      version: ">=v0.13.0"
```

```console
curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/package/azure/crossplane.yaml

kubectl 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.

```console
# Set this to the Docker Hub username or OCI registry you wish to use.
REG=my-package-repo
kubectl 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.

{{< /tab >}}
{{< /tabs >}}

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:

```console
cd ..
rm -rf crossplane-config
```

<!-- Named Links -->

[previous section]: {{<ref "provision-infrastructure" >}}
[composed]: {{<ref "../concepts/composition" >}}
[composition]: {{<ref "../concepts/composition" >}}
[Docker Hub]: https://hub.docker.com/
[packages]: {{<ref "../concepts/packages" >}}
[concepts]: {{<ref "../concepts" >}}
