Tech ONTAP Blogs
Tech ONTAP Blogs
The recently introduced resource transformations in NetApp Trident Protect enable you to modify a resource as it is being restored. This is useful when the restored version needs to differ from the original — for example, changing a virtual machine's IP address when restoring it to a different network or scaling down a deployment.
We can use the following operations to modify resources:
In this blog post, we’ll walk you through a restore scenarios using resource transformations to scale up an application during restore as an example how to use resource transformations.
We also show you how to create resource transformation rules.
We demonstrate the use of Trident Protect resource transformations on a K8s cluster which has NetApp Trident and Trident Protect version 26.06 already installed and configured. Resource transformations are supported starting with Trident Protect 26.06.
As sample application, we use a simple Alpine deployment in the namespace pu-alpine, protected with a Trident Protect snapshot as application pu-alpine.
$ kubectl get all,pvc,volumesnapshots -n pu-alpine
NAME READY STATUS RESTARTS AGE
pod/alpine-85cb86d5fb-2wjd7 1/1 Running 0 2d1h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/alpine 1/1 1 1 2d1h
NAME DESIRED CURRENT READY AGE
replicaset.apps/alpine-85cb86d5fb 1 1 1 2d1h
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/alpinedata Bound pvc-a6fbd817-a80c-4c94-b77f-463c25c06269 50Gi RWX azure-netapp-files-standard <unset> 2d1h
NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE
volumesnapshot.snapshot.storage.k8s.io/snapshot-b9012d00-d547-4b81-8679-0b6b81d0bf69-pvc-a6fbd817-a80c-4c94-b77f-463c25c06269 true alpinedata 50Gi csi-trident-snapclass snapcontent-35611620-c5d5-4206-babb-5d4abee49516 41h 41h
$ tridentctl-protect get application -n pu-alpine
+-----------+------------+-------+-------+
| NAME | NAMESPACES | STATE | AGE |
+-----------+------------+-------+-------+
| pu-alpine | pu-alpine | Ready | 1d17h |
+-----------+------------+-------+-------+
The Trident Protect CLI tridentctl-protect has two options to specify the transformation rules, either in human-readable or in JSON format. As always, you can also use Trident Protect custom resources (CRs) to restore applications and specify the transformation rules in the CR. In this document we focus on the CLI, look at the documentation to learn more about all the details of resource transformations.
Let’s start with the JSON format option of the CLI and restore the sample application into the namespace pu-alpine-restore on the same cluster, scaling up the alpine deployment to two pods during the restore. The --dry-run option of the CLI allows us to look at the corresponding snapshotrestore CR before execution:
$ tridentctl-protect create snapshotrestore --snapshot pu-alpine/pu-alpine-lacj30 --namespace-mapping pu-alpine:pu-alpine-restore --destination-app-name pu-alpine-restore --transformation_json '{"resource":{"group":"apps","version":"v1","kind":"Deployment","name":"alpine"},"operations":[{"op":"replace","path":"/spec/replicas","value":2}]}' -n pu-alpine-restore --dry-run
apiVersion: protect.trident.netapp.io/v1
kind: SnapshotRestore
metadata:
name: pu-alpine-4nxlsu
namespace: pu-alpine-restore
spec:
appArchivePath: pu-alpine_64172278-e753-4707-88e6-0e1be7c020aa/snapshots/20260707084659_pu-alpine-lacj30_3e24bd6d-cb46-49f8-b663-1033c38787be
appVaultRef: demo
cleanUpAdditionalExecHooks: true
cleanUpArchivedExecHooks: false
destinationApplicationName: pu-alpine-restore
namespaceMapping:
- destination: pu-alpine-restore
source: pu-alpine
resourceFilter: {}
runArchivedExecHooks: true
skipApplicationCreation: false
transformations:
- operations:
- op: replace
path: /spec/replicas
value: 2
resource:
group: apps
kind: Deployment
name: alpine
version: v1
status:
postRestoreExecHooksRunResults: null
state: ""
Now let’s execute the restore.
$ tridentctl-protect create snapshotrestore --snapshot pu-alpine/pu-alpine-lacj30 --namespace-mapping pu-alpine:pu-alpine-restore --destination-app-name pu-alpine-restore --transformation_json '{"resource":{"group":"apps","version":"v1","kind":"Deployment","name":"alpine"},"operations":[{"op":"replace","path":"/spec/replicas","value":2}]}' -n pu-alpine-restore
SnapshotRestore "pu-alpine-st92s8" created.
The restore finishes in some minutes and we can confirm that the restored Alpine application was indeed scaled up.
$ kubectl get all,pvc,volumesnapshots -n pu-alpine-restore
NAME READY STATUS RESTARTS AGE
pod/alpine-85cb86d5fb-dm62k 1/1 Running 0 66s
pod/alpine-85cb86d5fb-j56lp 1/1 Running 0 66s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/alpine 2/2 2 2 66s
NAME DESIRED CURRENT READY AGE
replicaset.apps/alpine-85cb86d5fb 2 2 2 67s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/alpinedata Bound pvc-befdc220-2095-4c93-915f-377af6358a0d 50Gi RWX azure-netapp-files-standard <unset> 71s
In human-readable format, the Trident Protect CLI command looks like this:
$ tridentctl-protect create snapshotrestore --snapshot pu-alpine/pu-alpine-lacj30 --namespace-mapping pu-alpine:pu-alpine-restore --destination-app-name pu-alpine-restore --transformation 'apps,v1,Deployment,alpine:replace{"path":"/spec/replicas","value":2}' -n pu-alpine-restore
Instead of specifying the transformation rules in each restore operation, we can create transformation rules at the namespace level (ResourceTransformationRule CRD) or cluster level (ResourceTransformationClusterRule CRD) and simply reference the rule we want to apply during the restore using the --transformationsRuleRef or --transformationsClusterRuleRef flags of the tridentctl-protect command.
Let’s look at transformation rules at the namespace level first.
As a transformation rule at the namespace level needs to exist in the target namespace of the restore, we need to create the namespace for the restore first.
$ kubectl create ns pu-alpine-restore
namespace/pu-alpine-restore created
Now we can create our transformation rule in the newly created namespace by applying this manifest:
$ cat scale-up-alpine-deployment-to-two_ResourceTransformationRule.yaml
apiVersion: protect.trident.netapp.io/v1
kind: ResourceTransformationRule
metadata:
name: scale-up-alpine-deployment-to-two
spec:
transformations:
- resource:
group: apps
kind: Deployment
version: v1
name: alpine
operations:
- op: replace
path: /spec/replicas
value: 2
$ kubectl -n pu-alpine-restore apply -f scale-up-alpine-deployment-to-two_ResourceTransformationRule.yaml
resourcetransformationrule.protect.trident.netapp.io/scale-up-alpine-deployment-to-two created
$ kubectl -n pu-alpine-restore get resourcetransformationrules
NAME AGE
scale-up-alpine-deployment-to-two 20s
The CLI command and the snapshotrestore CR are now much simpler, only referencing the transformation rule with the --transformationsRuleRef flag:
$ tridentctl-protect create snapshotrestore --snapshot pu-alpine/pu-alpine-4frnyj --namespace-mapping pu-alpine:pu-alpine-restore --destination-app-name pu-alpine-restore --transformationsRuleRef scale-up-alpine-deployment-to-two -n pu-alpine-restore --dry-run
apiVersion: protect.trident.netapp.io/v1
kind: SnapshotRestore
metadata:
name: pu-alpine-e4zgfc
namespace: pu-alpine-restore
spec:
appArchivePath: pu-alpine_2b9d9467-5697-4066-8acf-3c569b6627a9/snapshots/20260707161646_pu-alpine-4frnyj_b9012d00-d547-4b81-8679-0b6b81d0bf69
appVaultRef: demo
cleanUpAdditionalExecHooks: true
cleanUpArchivedExecHooks: false
destinationApplicationName: pu-alpine-restore
namespaceMapping:
- destination: pu-alpine-restore
source: pu-alpine
resourceFilter: {}
runArchivedExecHooks: true
skipApplicationCreation: false
transformationsRef:
kind: ResourceTransformationRule
name: scale-up-alpine-deployment-to-two
status:
postRestoreExecHooksRunResults: null
state: ""
Now let’s see how we can leverage ResourceTransformatioClusterRules to modify resources during restores. Below is a manifest for a ResourceTransformatioClusterRule that scales any deployment to two. As the resource name (like group and version) is an optional argument, we don’t specify it in the ResourceTransformatioClusterRule, making it more generic – but keep in mind that any deployment will be re-scaled by applying this rule.
$ cat scale-up-deployment-to-two_ResourceTransformationClusterRule.yaml
apiVersion: protect.trident.netapp.io/v1
kind: ResourceTransformationClusterRule
metadata:
name: scale-up-deployment-to-two
spec:
transformations:
- resource:
group: apps
kind: Deployment
version: v1
operations:
- op: replace
path: /spec/replicas
value: 2
We apply this manifest to create the cluster rule:
$ kubectl apply -f scale-up-deployment-to-two_ResourceTransformationClusterRule.yaml
resourcetransformationclusterrule.protect.trident.netapp.io/scale-up-deployment-to-two created
$ kubectl get ResourceTransformationClusterRule
NAME AGE
scale-up-deployment-to-two 16s
With the --transformationsClusterRuleRef flag of tridentctl-protect, we can now reference the cluster rule during the restore from snapshot easily:
$ tridentctl-protect create snapshotrestore --snapshot pu-alpine/pu-alpine-4frnyj --namespace-mapping pu-alpine:pu-alpine-restore --destination-app-name pu-alpine-restore --transformationsClusterRuleRef scale-up-deployment-to-two -n pu-alpine-restore
SnapshotRestore "pu-alpine-ywvuzx" created.
Checking the restore namespace, we confirm that the alpine deployment was scaled up to two pods as expected:
$ kubectl get all,pvc,volumesnapshots -n pu-alpine-restore
NAME READY STATUS RESTARTS AGE
pod/alpine-85cb86d5fb-5qjm6 1/1 Running 0 5m28s
pod/alpine-85cb86d5fb-phkqh 1/1 Running 0 5m28s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/alpine 2/2 2 2 5m29s
NAME DESIRED CURRENT READY AGE
replicaset.apps/alpine-85cb86d5fb 2 2 2 5m29s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/alpinedata Bound pvc-6318a214-c800-4fa8-95fe-c32edf74f288 50Gi RWX azure-netapp-files-standard <unset> 5m33
When doing cross-cluster restores, the ResourceTransformationClusterRules must exist in the target cluster!
Before the introduction of resource transformation in Trident Protect, operations like rescaling an application or changing its image registry during restore were only possible by using post-restore hook scripts executed in helper pods, as outlined in these two blog post:
You’ll agree that using resource transformation for such operations is much easier and less error prone.
NetApp Trident Protect's resource transformations feature simplifies the restoration of Kubernetes applications by enabling modifications to resources during the restore process. For instance, scaling a deployment or changing a virtual machine's IP address can now be achieved directly through transformation rules. Supported operations include add, replace, remove, and more, offering granular control over resource adjustments.
Key highlights include:
Before this feature, resource modifications required post-restore scripts, which were prone to errors and operational overhead. Resource transformations now offer a more streamlined and efficient alternative, improving disaster recovery workflows.
This blog post walks through practical scenarios, such as scaling a deployment during restore, and demonstrates how to create and apply transformation rules using both CLI commands and manifests. Whether you're restoring applications within the same cluster or across clusters, resource transformations ensure a seamless and automated recovery experience.