From 472ac592a1d17e4e40f653b986296d47d75d5fdd Mon Sep 17 00:00:00 2001 From: Patrick Hermann Date: Sat, 24 Jan 2026 16:06:28 +0000 Subject: [PATCH] feat: feat/rework-vspherevm-opentofu --- .../apps/postgres-db/examples/provider.yaml | 2 +- .../terraform/harbor-project/README.md | 6 +- .../harbor-project/apis/composition.yaml | 2 +- .../terraform/harbor-project/crossplane.yaml | 2 +- .../vsphere-vm-ansible/apis/composition.yaml | 727 +++++++++--------- .../vsphere-vm-ansible/apis/definition.yaml | 24 +- configurations/terraform/vsphere-vm/README.md | 132 +++- .../vsphere-vm/apis/composition.yaml | 271 ++++--- .../terraform/vsphere-vm/apis/defintion.yaml | 22 +- .../terraform/vsphere-vm/crossplane.yaml | 83 +- .../terraform/vsphere-vm/examples/claim.yaml | 14 +- ...nfig.yaml => cluster-provider-config.yaml} | 8 +- .../examples/deployment-runtime-config.yaml | 17 + .../vsphere-vm/examples/functions.yaml | 14 + .../vsphere-vm/examples/provider.yaml | 9 + 15 files changed, 754 insertions(+), 579 deletions(-) rename configurations/terraform/vsphere-vm/examples/{provider-config.yaml => cluster-provider-config.yaml} (56%) create mode 100644 configurations/terraform/vsphere-vm/examples/deployment-runtime-config.yaml create mode 100644 configurations/terraform/vsphere-vm/examples/functions.yaml create mode 100644 configurations/terraform/vsphere-vm/examples/provider.yaml diff --git a/configurations/apps/postgres-db/examples/provider.yaml b/configurations/apps/postgres-db/examples/provider.yaml index f53d494..d7ce9d1 100644 --- a/configurations/apps/postgres-db/examples/provider.yaml +++ b/configurations/apps/postgres-db/examples/provider.yaml @@ -4,4 +4,4 @@ kind: Provider metadata: name: provider-sql spec: - package: xpkg.upbound.io/crossplane-contrib/provider-sql:v0.13.0 \ No newline at end of file + package: xpkg.upbound.io/crossplane-contrib/provider-sql:v0.13.0 diff --git a/configurations/terraform/harbor-project/README.md b/configurations/terraform/harbor-project/README.md index 34f31b4..2da9586 100644 --- a/configurations/terraform/harbor-project/README.md +++ b/configurations/terraform/harbor-project/README.md @@ -51,7 +51,7 @@ spec: configuration: | terraform { backend "kubernetes" { - secret_suffix = "providerconfig-default" + secret_suffix = "providerconfig-default" # pragma: allowlist secret namespace = "crossplane-system" in_cluster_config = true } @@ -84,7 +84,7 @@ type: Opaque stringData: credentials.tfvars: | harbor_username = "admin" - harbor_password = "your-password-here" + harbor_password = "your-password-here" # pragma: allowlist secret EOF ``` @@ -140,7 +140,7 @@ The secret must contain a `credentials.tfvars` key with HCL-formatted variables: ```hcl harbor_username = "admin" -harbor_password = "your-password" +harbor_password = "your-password" # pragma: allowlist secret ``` ## Debugging diff --git a/configurations/terraform/harbor-project/apis/composition.yaml b/configurations/terraform/harbor-project/apis/composition.yaml index 590ce49..f8d0b63 100644 --- a/configurations/terraform/harbor-project/apis/composition.yaml +++ b/configurations/terraform/harbor-project/apis/composition.yaml @@ -16,7 +16,7 @@ spec: kind: GoTemplate source: Inline inline: - template: | + template: | # pragma: allowlist secret --- apiVersion: opentofu.m.upbound.io/v1beta1 kind: Workspace diff --git a/configurations/terraform/harbor-project/crossplane.yaml b/configurations/terraform/harbor-project/crossplane.yaml index f169185..7398bd5 100644 --- a/configurations/terraform/harbor-project/crossplane.yaml +++ b/configurations/terraform/harbor-project/crossplane.yaml @@ -10,7 +10,7 @@ metadata: meta.crossplane.io/description: | Harbor project management abstraction for Crossplane using OpenTofu provider. Automatically provisions Harbor projects with vulnerability scanning and storage quotas. - meta.crossplane.io/readme: | + meta.crossplane.io/readme: | # pragma: allowlist secret # HarborProject Configuration A Crossplane configuration for managing Harbor projects using the OpenTofu provider with the Harbor Terraform provider. diff --git a/configurations/terraform/vsphere-vm-ansible/apis/composition.yaml b/configurations/terraform/vsphere-vm-ansible/apis/composition.yaml index 4888268..90ff5ac 100644 --- a/configurations/terraform/vsphere-vm-ansible/apis/composition.yaml +++ b/configurations/terraform/vsphere-vm-ansible/apis/composition.yaml @@ -1,379 +1,392 @@ - +--- apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: - creationTimestamp: "2024-12-08T07:49:55Z" - labels: - crossplane.io/xrd: xvspherevmansibles.resources.stuttgart-things.com name: vsphere-vm-ansible + labels: + crossplane.io/xrd: vspherevmansibles.resources.stuttgart-things.com spec: compositeTypeRef: apiVersion: resources.stuttgart-things.com/v1alpha1 - kind: XVsphereVmAnsible + kind: VsphereVmAnsible mode: Pipeline pipeline: - - step: create-vsphere-vm - functionRef: - name: function-patch-and-transform - input: - apiVersion: pt.fn.crossplane.io/v1beta1 - environment: null - kind: Resources - patchSets: [] - resources: - - base: - apiVersion: resources.stuttgart-things.com/v1alpha1 - kind: XVsphereVM - name: vsphere-vm - patches: - - fromFieldPath: metadata.name - toFieldPath: metadata.name - type: FromCompositeFieldPath - - fromFieldPath: metadata.namespace - toFieldPath: metadata.namespace - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.count - toFieldPath: spec.vm.count - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.name - toFieldPath: spec.vm.name - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.cpu - toFieldPath: spec.vm.cpu - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.ram - toFieldPath: spec.vm.ram - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.disk - toFieldPath: spec.vm.disk - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.firmware - toFieldPath: spec.vm.firmware - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.folderPath - toFieldPath: spec.vm.folderPath - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.datacenter - toFieldPath: spec.vm.datacenter - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.datastore - toFieldPath: spec.vm.datastore - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.resourcePool - toFieldPath: spec.vm.resourcePool - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.network - toFieldPath: spec.vm.network - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.template - toFieldPath: spec.vm.template - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.bootstrap - toFieldPath: spec.vm.bootstrap - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.annotation - toFieldPath: spec.vm.annotation - type: FromCompositeFieldPath - - fromFieldPath: spec.vm.unverifiedSsl - toFieldPath: spec.vm.unverifiedSsl - type: FromCompositeFieldPath - - fromFieldPath: spec.tfvars.secretName - toFieldPath: spec.tfvars.secretName - type: FromCompositeFieldPath - - fromFieldPath: spec.tfvars.secretNamespace - toFieldPath: spec.tfvars.secretNamespace - type: FromCompositeFieldPath - - fromFieldPath: spec.tfvars.secretKey - toFieldPath: spec.tfvars.secretKey - type: FromCompositeFieldPath - - fromFieldPath: spec.connectionSecret.name - toFieldPath: spec.connectionSecret.name - type: FromCompositeFieldPath - - fromFieldPath: spec.connectionSecret.namespace - toFieldPath: spec.connectionSecret.namespace - type: FromCompositeFieldPath - - fromFieldPath: spec.providerRef.name - toFieldPath: spec.providerRef.name - type: FromCompositeFieldPath - - fromFieldPath: status.share.ip - policy: - fromFieldPath: Required - toFieldPath: status.share.ip - transforms: - - string: - join: - separator: ',' - type: Join - type: string - - string: - trim: '[' - type: TrimPrefix - type: string - - string: - trim: ']' - type: TrimSuffix - type: string - - string: - fmt: all+["%s"] - type: Format - type: string - type: ToCompositeFieldPath - - fromFieldPath: status.share.ip - policy: - fromFieldPath: Required - toFieldPath: status.share.ips - transforms: - - string: - join: - separator: '-' - type: Join - type: string - type: ToCompositeFieldPath - - step: create-ansible-inventory - functionRef: - name: function-patch-and-transform - input: - apiVersion: pt.fn.crossplane.io/v1beta1 - environment: null - kind: Resources - patchSets: [] - resources: - - name: create-ansible-inventory - base: - apiVersion: tf.upbound.io/v1beta1 - kind: Workspace - metadata: - name: create-ansible-inventory - annotations: - crossplane.io/external-name: create-ansible-inventory - spec: - forProvider: - source: Inline - vars: - - key: ipAddrs - value: pat - module: | - provider "random" {} + - step: create-vsphere-vm + functionRef: + name: function-patch-and-transform + input: + apiVersion: pt.fn.crossplane.io/v1beta1 + kind: Resources + resources: + - name: vsphere-vm + base: + apiVersion: resources.stuttgart-things.com/v1alpha1 + kind: VsphereVM + patches: + - type: FromCompositeFieldPath + fromFieldPath: metadata.name + toFieldPath: metadata.name + - type: FromCompositeFieldPath + fromFieldPath: metadata.namespace + toFieldPath: metadata.namespace + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.count + toFieldPath: spec.vm.count + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.name + toFieldPath: spec.vm.name + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.cpu + toFieldPath: spec.vm.cpu + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.ram + toFieldPath: spec.vm.ram + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.disk + toFieldPath: spec.vm.disk + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.firmware + toFieldPath: spec.vm.firmware + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.folderPath + toFieldPath: spec.vm.folderPath + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.datacenter + toFieldPath: spec.vm.datacenter + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.datastore + toFieldPath: spec.vm.datastore + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.resourcePool + toFieldPath: spec.vm.resourcePool + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.network + toFieldPath: spec.vm.network + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.template + toFieldPath: spec.vm.template + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.bootstrap + toFieldPath: spec.vm.bootstrap + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.annotation + toFieldPath: spec.vm.annotation + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.unverifiedSsl + toFieldPath: spec.vm.unverifiedSsl + - type: FromCompositeFieldPath + fromFieldPath: spec.tfvars.secretName + toFieldPath: spec.tfvars.secretName + - type: FromCompositeFieldPath + fromFieldPath: spec.tfvars.secretKey + toFieldPath: spec.tfvars.secretKey + - type: FromCompositeFieldPath + fromFieldPath: spec.connectionSecret.name + toFieldPath: spec.connectionSecret.name + - type: FromCompositeFieldPath + fromFieldPath: spec.providerRef.name + toFieldPath: spec.providerRef.name + - type: FromCompositeFieldPath + fromFieldPath: spec.providerRef.kind + toFieldPath: spec.providerRef.kind + - type: ToCompositeFieldPath + fromFieldPath: status.share.ip + toFieldPath: status.share.ip + policy: + fromFieldPath: Required + transforms: + - type: string + string: + type: Join + join: + separator: ',' + - type: string + string: + type: TrimPrefix + trim: '[' + - type: string + string: + type: TrimSuffix + trim: ']' + - type: string + string: + type: Format + fmt: all+["%s"] + - type: ToCompositeFieldPath + fromFieldPath: status.share.ip + toFieldPath: status.share.ips + policy: + fromFieldPath: Required + transforms: + - type: string + string: + type: Join + join: + separator: '-' + - step: create-ansible-inventory + functionRef: + name: function-patch-and-transform + input: + apiVersion: pt.fn.crossplane.io/v1beta1 + kind: Resources + resources: + - name: create-ansible-inventory + base: + apiVersion: opentofu.m.upbound.io/v1beta1 + kind: Workspace + metadata: + annotations: + crossplane.io/external-name: create-ansible-inventory + spec: + providerConfigRef: + name: default + kind: ClusterProviderConfig + forProvider: + source: Inline + vars: + - key: ipAddrs + value: pat + module: | + provider "random" {} - locals { - ip_addresses = split("-", var.ipAddrs) - } + locals { + ip_addresses = split("-", var.ipAddrs) + } - resource "random_shuffle" "shuffled_ips" { - input = local.ip_addresses - result_count = length(local.ip_addresses) # Shuffle all IPs - } + resource "random_shuffle" "shuffled_ips" { + input = local.ip_addresses + result_count = length(local.ip_addresses) # Shuffle all IPs + } - output "ips" { - value = var.ipAddrs - } + output "ips" { + value = var.ipAddrs + } - output "ansible_inventory" { - value = <CONFIGURE IN-CLUSTER PROVIDER +## Overview + +This configuration creates vSphere virtual machines through Crossplane using the Terraform provider. It provides a Kubernetes-native way to manage VM lifecycle in vSphere environments. + +## Prerequisites + +| Component | Version | +|-----------|---------| +| Crossplane | >= v1.14.1 | +| provider-helm | >= v0.19.0 | +| provider-kubernetes | >= v0.14.1 | +| provider-terraform | (required) | + +## Installation ```bash -kubectl apply -f - < +## Configuration + +### Create TFVars Secret -
CREATE TFVARS AS SECRET +The Terraform provider requires vSphere credentials stored as a Kubernetes secret: ```bash -# CREATE SECRET -kubectl create secret generic vsphere-tfvars --from-literal=terraform.tfvars="$(cat < +## Claim Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `vm.name` | string | yes | - | VM name | +| `vm.count` | string | no | `"1"` | Number of VMs to create | +| `vm.cpu` | string | yes | `"4"` | Number of vCPUs | +| `vm.ram` | string | yes | `"4096"` | Memory in MB | +| `vm.disk` | string | yes | `"64"` | Disk size in GB | +| `vm.firmware` | string | no | `"bios"` | Firmware type (`bios` or `efi`) | +| `vm.folderPath` | string | yes | - | vSphere folder path | +| `vm.datacenter` | string | yes | - | vSphere datacenter | +| `vm.datastore` | string | yes | - | vSphere datastore | +| `vm.resourcePool` | string | yes | - | vSphere resource pool | +| `vm.network` | string | yes | - | vSphere network | +| `vm.template` | string | yes | - | VM template name | +| `vm.bootstrap` | string | no | `'["echo STUTTGART-THINGS"]'` | Bootstrap commands (JSON array) | +| `vm.annotation` | string | no | `VSPHERE-VM BUILD...` | VM annotation | +| `vm.unverifiedSsl` | string | no | `"true"` | Skip SSL verification | +| `tfvars.secretName` | string | yes | - | Name of tfvars secret | +| `tfvars.secretKey` | string | no | `terraform.tfvars` | Key in the secret | +| `connectionSecret.name` | string | yes | - | Output connection secret name | +| `providerRef.name` | string | yes | - | Provider config reference | +| `providerRef.kind` | string | no | `ClusterProviderConfig` | `ProviderConfig` or `ClusterProviderConfig` | + +## Usage Example + +```yaml +apiVersion: resources.stuttgart-things.com/v1alpha1 +kind: VsphereVM +metadata: + name: my-vm +spec: + providerRef: + name: default + kind: ClusterProviderConfig + vm: + name: my-vm + count: "1" + cpu: "4" + ram: "4096" + disk: "64" + firmware: bios + folderPath: stuttgart-things/testing + datacenter: /LabUL + datastore: /LabUL/datastore/UL-ESX-SAS-01 + resourcePool: /LabUL/host/Cluster-V6.5/Resources + network: /LabUL/network/LAB-10.31.103 + template: sthings-u24 + tfvars: + secretName: vsphere-tfvars + secretKey: terraform.tfvars + connectionSecret: + name: my-vm-connection +``` + +## Development + +### Render Composition Locally + +```bash +crossplane render examples/claim.yaml apis/composition.yaml examples/functions.yaml \ + --include-function-results +``` + +### Trace Resource + +```bash +crossplane beta trace vspherevm my-vm -n default +``` + +## License -## VSPHERE-VM CLAIM EXAMPLE +Apache-2.0 diff --git a/configurations/terraform/vsphere-vm/apis/composition.yaml b/configurations/terraform/vsphere-vm/apis/composition.yaml index 88a3b9a..1c801b6 100644 --- a/configurations/terraform/vsphere-vm/apis/composition.yaml +++ b/configurations/terraform/vsphere-vm/apis/composition.yaml @@ -4,147 +4,136 @@ kind: Composition metadata: name: vsphere-vm labels: - crossplane.io/xrd: xvspherevms.resources.stuttgart-things.com + crossplane.io/xrd: vspherevms.resources.stuttgart-things.com spec: compositeTypeRef: apiVersion: resources.stuttgart-things.com/v1alpha1 - kind: XVsphereVM - resources: - - name: vsphere-vm - base: - kind: Workspace - apiVersion: tf.upbound.io/v1beta1 - metadata: - annotations: - crossplane.io/external-name: vspherevm - spec: - providerConfigRef: - name: terraform-default - writeConnectionSecretToRef: - name: vsphere-vm-test - namespace: crossplane-system - forProvider: - source: Remote - module: git::https://github.com/stuttgart-things/vsphere-vm.git?ref=v1.7.5-2.7.0 - vars: - - key: vm_count - type: integer - value: "1" - - key: vsphere_vm_name - type: string - - key: vm_memory - type: integer - value: "4096" - - key: vm_disk_size - type: integer - value: "64" - - key: vm_num_cpus - type: integer - value: "4" - - key: firmware - type: string - value: bios - - key: vsphere_vm_folder_path - type: string - - key: vsphere_datacenter - type: string - - key: vsphere_datastore - type: string - - key: vsphere_resource_pool - type: string - - key: vsphere_network - type: string - - key: vsphere_vm_template - type: string - - key: bootstrap - type: string - value: '["echo STUTTGART-THINGS"]' - - key: annotation - type: string - value: VSPHERE-VM BUILD w/ CROSSPLANE FOR STUTTGART-THINGS - - key: unverified_ssl - type: string - value: "true" - varFiles: - - source: SecretKey - secretKeyRef: - namespace: default - name: vsphere-tfvars - key: terraform.tfvars - patches: - - type: CombineFromComposite - combine: - variables: - - fromFieldPath: spec.vm.name - strategy: string - string: - fmt: "vspherevm-%s" - toFieldPath: "metadata.annotations[crossplane.io/external-name]" - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.count - toFieldPath: spec.forProvider.vars[0].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.name - toFieldPath: spec.forProvider.vars[1].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.ram - toFieldPath: spec.forProvider.vars[2].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.disk - toFieldPath: spec.forProvider.vars[3].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.cpu - toFieldPath: spec.forProvider.vars[4].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.firmware - toFieldPath: spec.forProvider.vars[5].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.folderPath - toFieldPath: spec.forProvider.vars[6].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.datacenter - toFieldPath: spec.forProvider.vars[7].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.datastore - toFieldPath: spec.forProvider.vars[8].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.resourcePool - toFieldPath: spec.forProvider.vars[9].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.network - toFieldPath: spec.forProvider.vars[10].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.template - toFieldPath: spec.forProvider.vars[11].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.bootstrap - toFieldPath: spec.forProvider.vars[12].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.annotation - toFieldPath: spec.forProvider.vars[13].value - - type: FromCompositeFieldPath - fromFieldPath: spec.vm.unverifiedSsl - toFieldPath: spec.forProvider.vars[14].value - - type: FromCompositeFieldPath - fromFieldPath: spec.tfvars.secretName - toFieldPath: spec.forProvider.varFiles[0].secretKeyRef.name - - type: FromCompositeFieldPath - fromFieldPath: spec.tfvars.secretNamespace - toFieldPath: spec.forProvider.varFiles[0].secretKeyRef.namespace - - type: FromCompositeFieldPath - fromFieldPath: spec.tfvars.secretKey - toFieldPath: spec.forProvider.varFiles[0].secretKeyRef.key - - type: FromCompositeFieldPath - fromFieldPath: spec.connectionSecret.name - toFieldPath: spec.writeConnectionSecretToRef.name - - type: FromCompositeFieldPath - fromFieldPath: spec.connectionSecret.namespace - toFieldPath: spec.writeConnectionSecretToRef.namespace - - type: FromCompositeFieldPath - fromFieldPath: spec.providerRef.name - toFieldPath: spec.providerConfigRef.name - - type: ToCompositeFieldPath - fromFieldPath: status.atProvider.outputs.ip - toFieldPath: status.share.ip - policy: - fromFieldPath: Optional + kind: VsphereVM + mode: Pipeline + pipeline: + - step: patch-and-transform + functionRef: + name: function-patch-and-transform + input: + apiVersion: pt.fn.crossplane.io/v1beta1 + kind: Resources + resources: + - name: vsphere-vm + base: + kind: Workspace + apiVersion: opentofu.m.upbound.io/v1beta1 + metadata: + annotations: + crossplane.io/external-name: vspherevm + spec: + providerConfigRef: + name: default + kind: ClusterProviderConfig + writeConnectionSecretToRef: + name: vsphere-vm-test + forProvider: + source: Remote + module: git::https://github.com/stuttgart-things/vsphere-vm.git?ref=v1.7.5-2.7.0 + vars: + - key: vm_count + value: "1" + - key: vsphere_vm_name + - key: vm_memory + value: "4096" + - key: vm_disk_size + value: "64" + - key: vm_num_cpus + value: "4" + - key: firmware + value: bios + - key: vsphere_vm_folder_path + - key: vsphere_datacenter + - key: vsphere_datastore + - key: vsphere_resource_pool + - key: vsphere_network + - key: vsphere_vm_template + - key: bootstrap + value: '["echo STUTTGART-THINGS"]' + - key: annotation + value: VSPHERE-VM BUILD w/ CROSSPLANE FOR STUTTGART-THINGS + - key: unverified_ssl + value: "true" + varFiles: + - source: SecretKey + secretKeyRef: + name: vsphere-tfvars + key: terraform.tfvars + patches: + - type: CombineFromComposite + combine: + variables: + - fromFieldPath: spec.vm.name + strategy: string + string: + fmt: "vspherevm-%s" + toFieldPath: "metadata.annotations[crossplane.io/external-name]" + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.count + toFieldPath: spec.forProvider.vars[0].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.name + toFieldPath: spec.forProvider.vars[1].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.ram + toFieldPath: spec.forProvider.vars[2].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.disk + toFieldPath: spec.forProvider.vars[3].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.cpu + toFieldPath: spec.forProvider.vars[4].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.firmware + toFieldPath: spec.forProvider.vars[5].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.folderPath + toFieldPath: spec.forProvider.vars[6].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.datacenter + toFieldPath: spec.forProvider.vars[7].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.datastore + toFieldPath: spec.forProvider.vars[8].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.resourcePool + toFieldPath: spec.forProvider.vars[9].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.network + toFieldPath: spec.forProvider.vars[10].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.template + toFieldPath: spec.forProvider.vars[11].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.bootstrap + toFieldPath: spec.forProvider.vars[12].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.annotation + toFieldPath: spec.forProvider.vars[13].value + - type: FromCompositeFieldPath + fromFieldPath: spec.vm.unverifiedSsl + toFieldPath: spec.forProvider.vars[14].value + - type: FromCompositeFieldPath + fromFieldPath: spec.tfvars.secretName + toFieldPath: spec.forProvider.varFiles[0].secretKeyRef.name + - type: FromCompositeFieldPath + fromFieldPath: spec.tfvars.secretKey + toFieldPath: spec.forProvider.varFiles[0].secretKeyRef.key + - type: FromCompositeFieldPath + fromFieldPath: spec.connectionSecret.name + toFieldPath: spec.writeConnectionSecretToRef.name + - type: FromCompositeFieldPath + fromFieldPath: spec.providerRef.name + toFieldPath: spec.providerConfigRef.name + - type: FromCompositeFieldPath + fromFieldPath: spec.providerRef.kind + toFieldPath: spec.providerConfigRef.kind + - type: ToCompositeFieldPath + fromFieldPath: status.atProvider.outputs.ip + toFieldPath: status.share.ip + policy: + fromFieldPath: Optional diff --git a/configurations/terraform/vsphere-vm/apis/defintion.yaml b/configurations/terraform/vsphere-vm/apis/defintion.yaml index 5d8fdff..c4243c6 100644 --- a/configurations/terraform/vsphere-vm/apis/defintion.yaml +++ b/configurations/terraform/vsphere-vm/apis/defintion.yaml @@ -1,16 +1,16 @@ --- -apiVersion: apiextensions.crossplane.io/v1 +apiVersion: apiextensions.crossplane.io/v2 kind: CompositeResourceDefinition metadata: - name: xvspherevms.resources.stuttgart-things.com + name: vspherevms.resources.stuttgart-things.com spec: group: resources.stuttgart-things.com + defaultCompositeDeletePolicy: Foreground + scope: Namespaced names: - kind: XVsphereVM - plural: xvspherevms - claimNames: kind: VsphereVM plural: vspherevms + singular: vspherevm versions: - name: v1alpha1 served: true @@ -79,9 +79,6 @@ spec: properties: secretName: type: string - secretNamespace: - type: string - default: default secretKey: type: string default: terraform.tfvars @@ -92,9 +89,6 @@ spec: properties: name: type: string - namespace: - type: string - default: default required: - name providerRef: @@ -102,6 +96,12 @@ spec: properties: name: type: string + kind: + type: string + default: ClusterProviderConfig + enum: + - ProviderConfig + - ClusterProviderConfig required: - name required: diff --git a/configurations/terraform/vsphere-vm/crossplane.yaml b/configurations/terraform/vsphere-vm/crossplane.yaml index aef7f55..2bda705 100644 --- a/configurations/terraform/vsphere-vm/crossplane.yaml +++ b/configurations/terraform/vsphere-vm/crossplane.yaml @@ -1,21 +1,86 @@ ---- apiVersion: meta.pkg.crossplane.io/v1 kind: Configuration metadata: name: vsphere-vm annotations: - meta.crossplane.io/maintainer: patrick.hermann@sva.de + meta.crossplane.io/maintainer: Stuttgart-Things meta.crossplane.io/source: github.com/stuttgart-things/crossplane meta.crossplane.io/license: Apache-2.0 meta.crossplane.io/description: | - creates vsphere-vm with crossplane based on terraform (provider) + Combined Crossplane configuration for provisioning vSphere virtual machines + using the Terraform provider. + + This configuration provides a declarative interface for VM provisioning: + - vSphere VM creation via Terraform provider + - Helm-based resource management + - Kubernetes resource orchestration + + The resulting VSphereVM resource provides a simple, declarative + interface for VM provisioning on VMware vSphere infrastructure. + meta.crossplane.io/readme: | - creates vsphere-vm with crossplane based on terraform (provider) + # vSphere VM Configuration + + This Crossplane configuration provides the **VSphereVM** composite + resource. It leverages the Terraform provider to create and manage + virtual machines on VMware vSphere infrastructure. + + ## What this configuration does + + When a `VSphereVM` claim is created, this configuration: + + 1. Provisions a virtual machine on vSphere using Terraform + 2. Manages VM lifecycle (create, update, delete) + 3. Aggregates readiness and status information on the parent resource + + ## Provided API + + ```yaml + apiVersion: resources.stuttgart-things.com/v1alpha1 + kind: VSphereVM + ``` + + ## Minimal Example + + ```yaml + apiVersion: resources.stuttgart-things.com/v1alpha1 + kind: VSphereVM + metadata: + name: my-vsphere-vm + spec: + providerConfigRef: terraform-provider + vm: + name: my-vm + datacenter: dc1 + cluster: cluster1 + datastore: datastore1 + network: VM Network + template: ubuntu-template + ``` + + ## Design Notes + + - Uses **Terraform provider** for vSphere resource management + - Uses **Pipeline compositions** + - Uses **function-patch-and-transform** + - Integrates with Helm and Kubernetes providers for auxiliary resources + + ## Requirements + + - Crossplane v1.14+ + - Terraform provider configured with vSphere credentials + - Helm provider configured + - Kubernetes provider configured + spec: crossplane: - version: ">=v1.14.1-0" + version: ">=v2.1.3" + dependsOn: - - provider: xpkg.upbound.io/crossplane-contrib/provider-helm - version: ">=v0.19.0" - - provider: xpkg.upbound.io/crossplane-contrib/provider-kubernetes - version: ">=v0.14.1" + # Required for vSphere VM provisioning via OpenTofu + - provider: xpkg.upbound.io/upbound/provider-opentofu + version: ">=v0.3.0" + + # Required for Pipeline compositions + - function: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform + version: ">=v0.9.3" diff --git a/configurations/terraform/vsphere-vm/examples/claim.yaml b/configurations/terraform/vsphere-vm/examples/claim.yaml index e782bb4..084aecb 100644 --- a/configurations/terraform/vsphere-vm/examples/claim.yaml +++ b/configurations/terraform/vsphere-vm/examples/claim.yaml @@ -2,14 +2,14 @@ apiVersion: resources.stuttgart-things.com/v1alpha1 kind: VsphereVM metadata: - name: xplane-kind - namespace: crossplane-system + name: opentofu-test1 spec: providerRef: - name: vsphere-vm + name: default + kind: ClusterProviderConfig vm: count: "1" - name: xplane-kind + name: opentofu-test1 cpu: "8" ram: "4096" disk: "64" @@ -25,10 +25,6 @@ spec: unverifiedSsl: "true" tfvars: secretName: vsphere-tfvars # pragma: allowlist secret - secretNamespace: crossplane-system # pragma: allowlist secret secretKey: terraform.tfvars # pragma: allowlist secret connectionSecret: - name: xplane-kind - namespace: crossplane-system - compositionRef: - name: vsphere-vm + name: opentofu-test1 diff --git a/configurations/terraform/vsphere-vm/examples/provider-config.yaml b/configurations/terraform/vsphere-vm/examples/cluster-provider-config.yaml similarity index 56% rename from configurations/terraform/vsphere-vm/examples/provider-config.yaml rename to configurations/terraform/vsphere-vm/examples/cluster-provider-config.yaml index 703e6b4..9c33b2d 100644 --- a/configurations/terraform/vsphere-vm/examples/provider-config.yaml +++ b/configurations/terraform/vsphere-vm/examples/cluster-provider-config.yaml @@ -1,13 +1,13 @@ --- -apiVersion: tf.upbound.io/v1beta1 -kind: ProviderConfig +apiVersion: opentofu.m.upbound.io/v1beta1 +kind: ClusterProviderConfig metadata: - name: vsphere-vm + name: default spec: configuration: | terraform { backend "kubernetes" { - secret_suffix = "vsphere-vm-tfstate" + secret_suffix = "providerconfig-default" namespace = "crossplane-system" in_cluster_config = true } diff --git a/configurations/terraform/vsphere-vm/examples/deployment-runtime-config.yaml b/configurations/terraform/vsphere-vm/examples/deployment-runtime-config.yaml new file mode 100644 index 0000000..0ee5c3c --- /dev/null +++ b/configurations/terraform/vsphere-vm/examples/deployment-runtime-config.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: pkg.crossplane.io/v1beta1 +kind: DeploymentRuntimeConfig +metadata: + name: opentofu +spec: + deploymentTemplate: + spec: + selector: {} + template: + spec: + containers: + - name: package-runtime + args: + - -d + - --poll=5m + - --max-reconcile-rate=10 diff --git a/configurations/terraform/vsphere-vm/examples/functions.yaml b/configurations/terraform/vsphere-vm/examples/functions.yaml new file mode 100644 index 0000000..456ec2f --- /dev/null +++ b/configurations/terraform/vsphere-vm/examples/functions.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: pkg.crossplane.io/v1beta1 +kind: Function +metadata: + name: function-go-templating +spec: + package: xpkg.crossplane.io/crossplane-contrib/function-go-templating:v0.11.3 +--- +apiVersion: pkg.crossplane.io/v1beta1 +kind: Function +metadata: + name: function-patch-and-transform +spec: + package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.9.3 diff --git a/configurations/terraform/vsphere-vm/examples/provider.yaml b/configurations/terraform/vsphere-vm/examples/provider.yaml new file mode 100644 index 0000000..29d9dd6 --- /dev/null +++ b/configurations/terraform/vsphere-vm/examples/provider.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-opentofu +spec: + package: xpkg.upbound.io/upbound/provider-opentofu:v1.0.3 + runtimeConfigRef: + name: opentofu