From e173a15b9a19818885b55bd972b3d922efe988e2 Mon Sep 17 00:00:00 2001 From: deggja Date: Sun, 12 Jan 2025 00:17:53 +0100 Subject: [PATCH 1/2] blog: initial code for composition function blog Signed-off-by: deggja --- 1. Crossplane Composition Functions/README.md | 0 .../providers/azure-providers.yml | 38 +++++ .../providers/workload-identity.yml | 28 ++++ .../resources/composition-functions.yml | 0 .../resources/composition.yml | 137 ++++++++++++++++++ .../resources/resources.yml | 67 +++++++++ 6 files changed, 270 insertions(+) create mode 100644 1. Crossplane Composition Functions/README.md create mode 100644 1. Crossplane Composition Functions/providers/azure-providers.yml create mode 100644 1. Crossplane Composition Functions/providers/workload-identity.yml create mode 100644 1. Crossplane Composition Functions/resources/composition-functions.yml create mode 100644 1. Crossplane Composition Functions/resources/composition.yml create mode 100644 1. Crossplane Composition Functions/resources/resources.yml diff --git a/1. Crossplane Composition Functions/README.md b/1. Crossplane Composition Functions/README.md new file mode 100644 index 0000000..e69de29 diff --git a/1. Crossplane Composition Functions/providers/azure-providers.yml b/1. Crossplane Composition Functions/providers/azure-providers.yml new file mode 100644 index 0000000..707de62 --- /dev/null +++ b/1. Crossplane Composition Functions/providers/azure-providers.yml @@ -0,0 +1,38 @@ +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-family-azure +spec: + package: xpkg.upbound.io/upbound/provider-family-azure:v1.11.0 + runtimeConfigRef: + name: workload-identity-config +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-azure-managedidentity +spec: + package: xpkg.upbound.io/upbound/provider-azure-managedidentity:v1.11.0 + skipDependencyResolution: true + runtimeConfigRef: + name: workload-identity-config +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-azure-authorization +spec: + package: xpkg.upbound.io/upbound/provider-azure-authorization:v1.11.0 + skipDependencyResolution: true + runtimeConfigRef: + name: workload-identity-config +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-azure-storage +spec: + package: xpkg.upbound.io/upbound/provider-azure-storage:v1.11.0 + skipDependencyResolution: true + runtimeConfigRef: + name: workload-identity-config \ No newline at end of file diff --git a/1. Crossplane Composition Functions/providers/workload-identity.yml b/1. Crossplane Composition Functions/providers/workload-identity.yml new file mode 100644 index 0000000..117ed23 --- /dev/null +++ b/1. Crossplane Composition Functions/providers/workload-identity.yml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + azure.workload.identity/client-id: client-id + azure.workload.identity/tenant-id: tenant-id + name: workload-identity-crossplane + namespace: crossplane-system +--- +apiVersion: pkg.crossplane.io/v1beta1 +kind: DeploymentRuntimeConfig +metadata: + name: workload-identity-config + namespace: crossplane-system +spec: + deploymentTemplate: + spec: + selector: {} + template: + metadata: + labels: + azure.workload.identity/use: "true" + spec: + serviceAccountName: workload-identity-crossplane + containers: + - name: package-runtime + args: + - --enable-management-policies \ No newline at end of file diff --git a/1. Crossplane Composition Functions/resources/composition-functions.yml b/1. Crossplane Composition Functions/resources/composition-functions.yml new file mode 100644 index 0000000..e69de29 diff --git a/1. Crossplane Composition Functions/resources/composition.yml b/1. Crossplane Composition Functions/resources/composition.yml new file mode 100644 index 0000000..6812b98 --- /dev/null +++ b/1. Crossplane Composition Functions/resources/composition.yml @@ -0,0 +1,137 @@ +### XRD ### +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: azureapps.azure.example.org +spec: + group: crossplane.io + names: + kind: zureApp + plural: azureapps + claimNames: + kind: AzureAppClaim + plural: azureappsclaims + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + parameters: + type: object + properties: + resourceGroupName: + type: string + location: + type: string + managedIdentityName: + type: string + subscriptionID: + type: string + storageAccountName: + type: string + scope: + type: string + roleDefinitionName: + type: string + status: + type: object + properties: + principalId: + type: string + description: "Stores the principal ID from the Azure Managed Identity" +--- +### Composition ### +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: azure-apps +spec: + compositeTypeRef: + apiVersion: crossplane.io + kind: AzureApp + resources: + - name: resource-group + base: + apiVersion: azure.upbound.io/v1beta1 + kind: ResourceGroup + spec: + forProvider: + location: West Europe + patches: + - fromFieldPath: "spec.parameters.resourceGroupName" + toFieldPath: "spec.forProvider.name" + - fromFieldPath: "spec.parameters.location" + toFieldPath: "spec.forProvider.location" + - name: storage-account + base: + apiVersion: storage.azure.upbound.io/v1beta1 + kind: Account + spec: + forProvider: + accountKind: StorageV2 + accountReplicationType: LRS + accountTier: Standard + isHnsEnabled: true + patches: + - fromFieldPath: "spec.parameters.resourceGroupName" + toFieldPath: "spec.forProvider.resourceGroupName" + - fromFieldPath: "spec.parameters.location" + toFieldPath: "spec.forProvider.location" + - fromFieldPath: "spec.parameters.storageAccountName" + toFieldPath: "metadata.name" + - name: managed-identity + base: + apiVersion: managedidentity.azure.upbound.io/v1beta1 + kind: UserAssignedIdentity + spec: + forProvider: {} + patches: + - fromFieldPath: "spec.parameters.resourceGroupName" + toFieldPath: "spec.forProvider.resourceGroupName" + - fromFieldPath: "spec.parameters.location" + toFieldPath: "spec.forProvider.location" + - fromFieldPath: "spec.parameters.managedIdentityName" + toFieldPath: "spec.forProvider.name" + - name: federated-identity-credential + base: + apiVersion: managedidentity.azure.upbound.io/v1beta1 + kind: FederatedIdentityCredential + spec: + forProvider: + issuer: "https://issuer" + audience: + - "api://AzureADTokenExchange" + subject: "foo" + patches: + - fromFieldPath: "spec.parameters.resourceGroupName" + toFieldPath: "spec.forProvider.resourceGroupName" + - type: FromCompositeFieldPath + fromFieldPath: "spec.parameters.managedIdentityName" + toFieldPath: "metadata.name" + - type: PatchSet + patchSetName: "set-parent" + patches: + - type: FromResourceFieldPath + fromResource: managed-identity + fromFieldPath: "status.atProvider.id" + toFieldPath: "spec.forProvider.parentId" + - name: role-assignment + base: + apiVersion: authorization.azure.upbound.io/v1beta1 + kind: RoleAssignment + spec: + forProvider: + principalId: principal_id # this is unknown + roleDefinitionName: Reader + scope: "/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts/example" + patches: + - type: FromResourceFieldPath + fromResource: managed-identity + fromFieldPath: "status.atProvider.principalId" + toFieldPath: "spec.forProvider.principalId" \ No newline at end of file diff --git a/1. Crossplane Composition Functions/resources/resources.yml b/1. Crossplane Composition Functions/resources/resources.yml new file mode 100644 index 0000000..85ab91f --- /dev/null +++ b/1. Crossplane Composition Functions/resources/resources.yml @@ -0,0 +1,67 @@ +apiVersion: azure.upbound.io/v1beta1 +kind: ResourceGroup +metadata: + name: resource-group + labels: + testing.upbound.io/example-name: resource-group +spec: + forProvider: + location: West Europe + tags: + provisioner: crossplane +--- +apiVersion: storage.azure.upbound.io/v1beta1 +kind: Account +metadata: + name: storage-account +spec: + forProvider: + accountKind: StorageV2 + accountReplicationType: LRS + accountTier: Standard + isHnsEnabled: true + location: West Europe + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: resource-group +--- +apiVersion: authorization.azure.upbound.io/v1beta1 +kind: RoleAssignment +metadata: + name: role-assignment +spec: + forProvider: + principalId: principal_id # this is unknown + roleDefinitionName: Reader + scope: "/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts/example" +--- +apiVersion: managedidentity.azure.upbound.io/v1beta1 +kind: UserAssignedIdentity +metadata: + name: managed-identity + labels: + testing.upbound.io/example-name: managed-identity +spec: + forProvider: + location: West Europe + name: exmplemanagidentity + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: managidentity-rg +--- +apiVersion: managedidentity.azure.upbound.io/v1beta1 +kind: FederatedIdentityCredential +metadata: + name: federated-credential +spec: + forProvider: + audience: + - "api://AzureADTokenExchange" + issuer: https://issuer + parentIdSelector: + matchLabels: + testing.upbound.io/example-name: managed-identity + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: resource-group + subject: system:serviceaccount:namespace:application \ No newline at end of file From 4d719e662548685f66de4128300ed0c94f06f22a Mon Sep 17 00:00:00 2001 From: deggja Date: Sun, 12 Jan 2025 14:46:47 +0100 Subject: [PATCH 2/2] blog: update code Signed-off-by: deggja --- .../resources/composition-function.yml | 184 ++++++++++++++++++ .../resources/composition-functions.yml | 0 .../resources/composition.yml | 125 +++++++----- .../resources/functions.yml | 17 ++ 4 files changed, 276 insertions(+), 50 deletions(-) create mode 100644 1. Crossplane Composition Functions/resources/composition-function.yml delete mode 100644 1. Crossplane Composition Functions/resources/composition-functions.yml create mode 100644 1. Crossplane Composition Functions/resources/functions.yml diff --git a/1. Crossplane Composition Functions/resources/composition-function.yml b/1. Crossplane Composition Functions/resources/composition-function.yml new file mode 100644 index 0000000..5e2f2a4 --- /dev/null +++ b/1. Crossplane Composition Functions/resources/composition-function.yml @@ -0,0 +1,184 @@ +### XRD ### +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: azureapps.crossplane.io +spec: + group: crossplane.io + names: + kind: AzureApp + plural: azureapps + claimNames: + kind: AzureAppClaim + plural: azureappclaims + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + parameters: + type: object + properties: + resourceGroup: + type: string + subscription: + type: string + tenant: + type: string + managedIdentity: + type: string + storageAccount: + type: string + roles: + type: array + items: + type: string + status: + type: object + properties: + principalId: + type: string +--- +### Composition ### +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example +spec: + compositeTypeRef: + apiVersion: crossplane.io/v1alpha1 + kind: AzureApp + mode: Pipeline # We add this so Crossplane knows we are going to be using Functions + pipeline: + - step: create-resources + functionRef: + name: function-patch-and-transform + input: + apiVersion: pt.fn.crossplane.io/v1beta1 + kind: Resources + resources: + - name: resource-group + base: + apiVersion: azure.upbound.io/v1beta1 + kind: ResourceGroup + metadata: + labels: + testing.upbound.io/example-name: resource-group + spec: + forProvider: + location: West Europe + patches: + - fromFieldPath: "spec.parameters.resourceGroup" + toFieldPath: "spec.forProvider.name" + - name: storage-account + base: + apiVersion: storage.azure.upbound.io/v1beta1 + kind: Account + spec: + forProvider: + accountKind: StorageV2 + accountReplicationType: LRS + accountTier: Standard + isHnsEnabled: true + location: West Europe + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: resource-group + patches: + - fromFieldPath: "spec.parameters.storageAccount" + toFieldPath: "metadata.name" + - name: managed-identity + base: + apiVersion: managedidentity.azure.upbound.io/v1beta1 + kind: UserAssignedIdentity + metadata: + labels: + testing.upbound.io/example-name: managed-identity + spec: + forProvider: + location: West Europe + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: resource-group + patches: + - fromFieldPath: "spec.parameters.managedIdentity" + toFieldPath: "spec.forProvider.name" + - type: ToCompositeFieldPath + fromFieldPath: "status.atProvider.principalId" + toFieldPath: "status.principalId" + - name: federated-credential + base: + apiVersion: managedidentity.azure.upbound.io/v1beta1 + kind: FederatedIdentityCredential + spec: + forProvider: + issuer: "https://issuer" + audience: + - "api://AzureADTokenExchange" + subject: "system:serviceaccount:namespace:application" + parentIdSelector: + matchLabels: + testing.upbound.io/example-name: managed-identity + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: resource-group + patches: + - type: FromCompositeFieldPath + fromFieldPath: "spec.parameters.managedIdentity" + toFieldPath: "metadata.name" + - step: create-role-assignments + functionRef: + name: function-go-templating + input: + apiVersion: gotemplating.fn.crossplane.io/v1beta1 + kind: GoTemplate + source: Inline + inline: + template: | + {{- $subscription := .observed.composite.resource.spec.parameters.subscription -}} + {{- $resourceGroup := .observed.composite.resource.spec.parameters.resourceGroup -}} + {{- $roles := $composite.spec.parameters.roles -}} + {{- $principalId := $composite.status.principalId -}} + {{ range $index, $roleName := $roles }} + --- + apiVersion: authorization.azure.upbound.io/v1beta1 + kind: RoleAssignment + metadata: + name: roleassignment-{{ $index }} + annotations: + gotemplating.fn.crossplane.io/composition-resource-name: roleassignment-{{ $index }} + spec: + forProvider: + name: roleassignment-{{ $index }} + scope: {{ /subscriptions/{{ $subscription }}/resourceGroups/{{ $resourceGroup }}/providers/Microsoft.Storage/storageAccounts/example }} + roleDefinitionName: "{{ $roleName }}" + principalId: "{{ $principalId }}" + providerConfigRef: + name: workload-identity-provider-config + {{- end }} +--- +### XR ### +apiVersion: crossplane.io +kind: AzureApp +metadata: + name: example +spec: + compositionRef: + name: azure-apps + parameters: + resourceGroup: resource-group + subscription: 12345678-aaaa-bbbb-cccc-123456789012 + managedIdentity: managed-identity + storageAccount: example + roles: + - Reader + - Contributor + - Storage Blob Data Reader + - Storage Table Data Contributor + - Role Based Access Control Administrator \ No newline at end of file diff --git a/1. Crossplane Composition Functions/resources/composition-functions.yml b/1. Crossplane Composition Functions/resources/composition-functions.yml deleted file mode 100644 index e69de29..0000000 diff --git a/1. Crossplane Composition Functions/resources/composition.yml b/1. Crossplane Composition Functions/resources/composition.yml index 6812b98..2427403 100644 --- a/1. Crossplane Composition Functions/resources/composition.yml +++ b/1. Crossplane Composition Functions/resources/composition.yml @@ -2,15 +2,15 @@ apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition metadata: - name: azureapps.azure.example.org + name: azureapps.crossplane.io spec: group: crossplane.io names: - kind: zureApp + kind: AzureApp plural: azureapps claimNames: kind: AzureAppClaim - plural: azureappsclaims + plural: azureappclaims versions: - name: v1alpha1 served: true @@ -25,49 +25,47 @@ spec: parameters: type: object properties: - resourceGroupName: - type: string - location: + resourceGroup: type: string - managedIdentityName: + subscription: type: string - subscriptionID: + tenant: type: string - storageAccountName: + managedIdentity: type: string - scope: + storageAccount: type: string - roleDefinitionName: + role: type: string - status: - type: object - properties: - principalId: - type: string - description: "Stores the principal ID from the Azure Managed Identity" + status: + type: object + properties: + principalId: + type: string --- ### Composition ### apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: - name: azure-apps + name: example spec: compositeTypeRef: - apiVersion: crossplane.io + apiVersion: crossplane.io/v1alpha1 kind: AzureApp resources: - name: resource-group base: apiVersion: azure.upbound.io/v1beta1 kind: ResourceGroup + metadata: + labels: + testing.upbound.io/example-name: resource-group spec: forProvider: location: West Europe patches: - - fromFieldPath: "spec.parameters.resourceGroupName" + - fromFieldPath: "spec.parameters.resourceGroup" toFieldPath: "spec.forProvider.name" - - fromFieldPath: "spec.parameters.location" - toFieldPath: "spec.forProvider.location" - name: storage-account base: apiVersion: storage.azure.upbound.io/v1beta1 @@ -78,27 +76,33 @@ spec: accountReplicationType: LRS accountTier: Standard isHnsEnabled: true + location: West Europe + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: resource-group patches: - - fromFieldPath: "spec.parameters.resourceGroupName" - toFieldPath: "spec.forProvider.resourceGroupName" - - fromFieldPath: "spec.parameters.location" - toFieldPath: "spec.forProvider.location" - - fromFieldPath: "spec.parameters.storageAccountName" + - fromFieldPath: "spec.parameters.storageAccount" toFieldPath: "metadata.name" - name: managed-identity base: apiVersion: managedidentity.azure.upbound.io/v1beta1 kind: UserAssignedIdentity + metadata: + labels: + testing.upbound.io/example-name: managed-identity spec: - forProvider: {} + forProvider: + location: West Europe + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: resource-group patches: - - fromFieldPath: "spec.parameters.resourceGroupName" - toFieldPath: "spec.forProvider.resourceGroupName" - - fromFieldPath: "spec.parameters.location" - toFieldPath: "spec.forProvider.location" - - fromFieldPath: "spec.parameters.managedIdentityName" + - fromFieldPath: "spec.parameters.managedIdentity" toFieldPath: "spec.forProvider.name" - - name: federated-identity-credential + - type: ToCompositeFieldPath + fromFieldPath: "status.atProvider.principalId" + toFieldPath: "status.principalId" + - name: federated-credential base: apiVersion: managedidentity.azure.upbound.io/v1beta1 kind: FederatedIdentityCredential @@ -107,31 +111,52 @@ spec: issuer: "https://issuer" audience: - "api://AzureADTokenExchange" - subject: "foo" + subject: "system:serviceaccount:namespace:application" + parentIdSelector: + matchLabels: + testing.upbound.io/example-name: managed-identity + resourceGroupNameSelector: + matchLabels: + testing.upbound.io/example-name: resource-group patches: - - fromFieldPath: "spec.parameters.resourceGroupName" - toFieldPath: "spec.forProvider.resourceGroupName" - type: FromCompositeFieldPath - fromFieldPath: "spec.parameters.managedIdentityName" + fromFieldPath: "spec.parameters.managedIdentity" toFieldPath: "metadata.name" - - type: PatchSet - patchSetName: "set-parent" - patches: - - type: FromResourceFieldPath - fromResource: managed-identity - fromFieldPath: "status.atProvider.id" - toFieldPath: "spec.forProvider.parentId" - name: role-assignment base: apiVersion: authorization.azure.upbound.io/v1beta1 kind: RoleAssignment spec: forProvider: - principalId: principal_id # this is unknown + principalId: "" # this was previously unknown, but now it is patched in from the status.principalId field in the Composite Resource roleDefinitionName: Reader scope: "/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts/example" patches: - - type: FromResourceFieldPath - fromResource: managed-identity - fromFieldPath: "status.atProvider.principalId" - toFieldPath: "spec.forProvider.principalId" \ No newline at end of file + - fromFieldPath: "status.principalId" + toFieldPath: "spec.forProvider.principalId" + - fromFieldPath: "spec.parameters.role" + toFieldPath: "spec.forProvider.roleDefinitionName" + - type: CombineFromComposite + combine: + variables: + - fromFieldPath: "spec.parameters.subscription" + - fromFieldPath: "spec.parameters.resourceGroup" + - fromFieldPath: "spec.parameters.storageAccount" + strategy: string + string: + fmt: "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/%s" +--- +### XR ### +apiVersion: crossplane.io +kind: AzureApp +metadata: + name: example +spec: + compositionRef: + name: azure-apps + parameters: + resourceGroup: resource-group + subscription: 12345678-aaaa-bbbb-cccc-123456789012 + managedIdentity: managed-identity + storageAccount: example + role: Reader \ No newline at end of file diff --git a/1. Crossplane Composition Functions/resources/functions.yml b/1. Crossplane Composition Functions/resources/functions.yml new file mode 100644 index 0000000..6e2716c --- /dev/null +++ b/1. Crossplane Composition Functions/resources/functions.yml @@ -0,0 +1,17 @@ +apiVersion: pkg.crossplane.io/v1 +kind: Function +metadata: + name: function-patch-and-transform +spec: + package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.8.0 + runtimeConfigRef: + name: functions-config +--- +apiVersion: pkg.crossplane.io/v1 +kind: Function +metadata: + name: function-go-templating +spec: + package: xpkg.upbound.io/crossplane-contrib/function-go-templating:v0.9.0 + runtimeConfigRef: + name: functions-config \ No newline at end of file