- VCF Automation gives you three ways to feed a value into a deployment: request inputs, property groups, and secrets. They are not interchangeable, and picking the wrong one is how credentials end up in plain text.
- Property groups come in two flavours: Input (the requester sees and fills them) and Constant (silent metadata the requester cannot read or even know is there). You choose the type at creation and you cannot convert it later.
- A secret is a reusable encrypted value. Once saved it can never be read back, by anyone, ever. That is the point, and it is also the rotation trap.
- A project-scoped secret silently overrides an org-scoped secret of the same name. Great feature, miserable to debug if you do not know it exists.
- My rule: constant property groups for non-secret shared config, secrets for anything that authenticates, and your real system of record stays in a vault outside VCF Automation.
I still find this line in blueprints that teams clone from the catalog:
properties:
username: svc_deploy
password: Sup3rSecret! # hardcoded, in a shared template
The fix is not a stronger password. The fix is knowing which of VCF Automation's three configuration mechanisms a value belongs in. The product (the one formerly called Aria Automation, and before that vRealize Automation) gives you request inputs, property groups, and secrets. Each exists for a different reason, each has a different visibility and encryption story, and most of the messes I clean up come from someone using one where another was the right answer. [AUTHOR: add anecdote about a brownfield template audit]
Three places a value can live
When you author a VMware Cloud Template, every value in it came from somewhere. The requester typed it, you baked it into the template, or it was injected from a reusable object. VCF Automation 9 formalises those into three mechanisms, and the first design decision on any template is deciding, value by value, which mechanism owns it.
Inputs I covered when we built custom forms in Part 15, so this Part is about the other two: property groups, which solve the copy-paste problem, and secrets, which solve the plain-text-password problem. They get conflated constantly because both look like "reusable values you reference with a binding." The difference that matters is encryption and who can see the value.
Property groups: input versus constant
A property group is a named bundle of related properties you define once and reference from many templates. You find them in the Design area alongside Blueprints, Custom Resources and Resource Actions. When you create one, you pick a type, and this is the decision people get wrong: you cannot change or convert the type afterwards. Pick wrong and you delete and rebuild.
Input property groups
An input property group bundles settings the requester enters or selects, plus any read-only values the design needs to carry alongside them. Think of a "sizing" group that exposes T-shirt size, environment, and a backup tier as a single reusable block you drop onto every machine template. Instead of redefining the same five inputs on twenty blueprints, you define them once and reference the group. When you change the enum of allowed sizes, every template that uses the group inherits it.
Constant property groups
A constant property group silently applies known values. The docs describe it well: it is invisible metadata. The requester never sees it, never fills it, and does not even know it is present. This is where org standards live. Your internal package registry URL, the AD domain to join, the syslog target, the proxy, the NTP servers. Values that are not secret but should not be retyped by hand on every blueprint and should not be exposed in the request form either.
You reference a property group property with the propgroup binding. Flat values use dot notation, and nested values index in:
# flat reference
flavor: '${propgroup.org-build-standards.flavor}'
image: '${propgroup.org-build-standards.image}'
# nested reference
roleId: '${propgroup.os-details.linux[roleId]}'
Secrets: the write-only sink
A secret is a reusable, encrypted value that project users add to their designs. SSH keys, join passwords, API tokens, registry credentials. You create one as a project administrator under Administer, then Certificates, then Secrets, then New. The name must be unique with no spaces or special characters, because the name is what you bind to. You enter the value (obscured by default, with an eye icon to verify before saving), optionally describe it, and create it.
Here is the property that changes how you operate: once saved, a secret value is encrypted in the database and can never be re-exposed. Not to you, not to an admin, not through the API. There is no "show value" button hiding somewhere. This is correct security behaviour and it is also the single fact that trips up rotation, which I will get to.
You bind a secret in the template the same way the docs show it. Start typing ${secret. and the editor offers the secrets available to that project:
properties:
name: ourvm
image: mint20
flavor: small
remoteAccess:
authentication: publicPrivateKey
sshKey: '${secret.ourPublicKey}'
username: root
Note one thing the wizard does for you: a secret is already encrypted, so it does not get the extra request-time encryption the form applies to ordinary sensitive inputs. You bind it directly. No double wrapping.
Org scope, project scope, and who wins
You scope a secret to the entire organization or to specific projects (via Assign Projects). Two rules keep things sane: you cannot associate two secrets of the same name with the entire org, and you cannot associate one secret with the same project twice. The rule that bites: if a secret with a given name exists both at the project level and at the org level, the project secret takes precedence and the org secret is not even offered for selection in that project's templates.
This is genuinely useful for multi-tenancy: define a sensible org default, let a project override it with its own value, and the same template behaves correctly in both. The failure mode is a support ticket that reads "the deployment is using the wrong credential and I have no idea where it is coming from." You check the org secret, it looks right, and you waste an hour because nobody told you a project secret of the same name was quietly shadowing it. First thing I check on that ticket is project-scoped secrets, before anything else.
Wiring all three into one template
Here is the pattern I hand to teams. The requester picks a hostname (input). Build standards like image and flavor come from a constant property group so they are consistent and not on the form. The SSH key and the domain-join password are secrets. Nothing sensitive is typed, nothing standard is duplicated, and the form stays short.
formatVersion: 1
inputs:
hostname:
type: string
title: Server name
minLength: 3
resources:
appVM:
type: Cloud.vSphere.Machine
properties:
name: '${input.hostname}'
image: '${propgroup.org-build-standards.image}'
flavor: '${propgroup.org-build-standards.flavor}'
remoteAccess:
authentication: publicPrivateKey
username: svc_deploy
sshKey: '${secret.deploy_public_key}'
cloudConfig: |
#cloud-config
runcmd:
- /opt/app/join-domain.sh '${secret.ad_join_password}'
Expected result: the catalog form shows one field, Server name. The deployment provisions with the org-standard image and flavor, injects the public key, and runs the join script with the password pulled from the secret at provision time. Failure mode: if you typo the property group name, the request fails validation at submit with an unresolved propgroup binding rather than silently substituting blank, which is the behaviour you want. A missing secret behaves the same way, so test the template under the actual project that owns the secrets, not under your admin context where a differently scoped secret might mask the problem.
The comparison you actually need
| Mechanism | Requester sees it | Encrypted at rest | Best for |
|---|---|---|---|
| Input property group | Yes, on the form | No | Reusable user-facing inputs (size, environment) |
| Constant property group | No, silent | No (plain metadata) | Non-secret org standards (URLs, domain, NTP) |
| Secret | No | Yes, never readable | Credentials, keys, tokens |
Rotation, the thing nobody plans for
Because a secret can never be read back, VCF Automation is not a vault. It is a write-only sink that delivers a value into provisioning. That distinction decides your rotation design. When a credential rotates, you update the secret value in place. New deployments pick up the new value. Resources already provisioned were configured with the old value at their provision time and will not magically re-read the secret. If the rotated credential needs to reach existing machines, that is a day-2 action or a reconfigure workflow, not something the secret update does for you.
So keep the system of record outside the platform. A real vault (HashiCorp Vault, your PAM, a cloud secrets manager) holds the authoritative value and the rotation schedule. VCF Automation secrets are a downstream copy you refresh. If you want that refresh automated, drive it from a VCF Operations Orchestrator workflow against the VCF Automation API (Part 17 and Part 20 cover the workflow and token pieces). Manual updates are fine at small scale, but write down which secrets exist and who owns the upstream, because the platform will never tell you what a value is, only that one is set.
What I validate before going live
Before a template using these mechanisms hits the catalog: confirm the property group type is right, because you cannot convert it later. Test the deployment under the consuming project, not your admin context, so scoping resolves the way a real requester will see it. Check for name collisions between org and project secrets if you rely on overrides. Grep the template for any literal credential that should have been a secret. And confirm the requester only sees the inputs you intended, by previewing the request form, not by reading the YAML.
A worked example with real numbers
Say you run three tenant orgs, four projects each, and every project clones the same RHEL build template. Without property groups that is twelve copies of the same image, flavor, registry URL, syslog target and AD domain. Change the registry endpoint once and you are editing twelve templates and hoping you did not miss one. Move those five values into a constant property group called org-build-standards and the change is a single edit, inherited everywhere it is bound. The sizing input group maps small, medium and large to flavors, so the requester picks from a dropdown instead of facing four raw fields.
Now the credentials. One org-level secret, ad_join_password, covers the default domain. Two of the twelve projects join a different domain, so each gets a project-scoped ad_join_password that shadows the org value automatically, with no template change. Twelve templates, one binding, correct behaviour across all three orgs, and the only thing a requester ever types is a hostname.
What I'd Do
Default to constant property groups for shared non-secret config, input property groups for reusable form fields, and secrets for anything that authenticates. The one rule that prevents most incidents: a value that grants access never lives in a property group, no matter how convenient the hidden flag feels. Build your secrets as a delivery layer on top of a real vault, and write down what exists, because the platform will not tell you. Pick one template, move its hardcoded values into the right mechanism, and preview the request form to prove the requester sees only what you intended.
References
- Broadcom TechDocs: Managing Secrets in VCF Automation
- Broadcom TechDocs: Property Groups in VCF Automation
- Broadcom TechDocs: What's New in VCF Automation 9.1



