You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue tracks the implementation of the azdo pipelines variable-group variable create command.
Command Description
Add a variable to an existing variable group. The Azure CLI validates duplicate keys, optionally prompts for secret values, and persists the change via update_variable_group (source). azdo should provide the same behavior with explicit secret handling, isReadOnly support, and safer output.
Important REST constraint:
Azure DevOps REST never returns secret variable values. Secret values are write-only: the command may send them to the API, but must not expect to read them back. Human-readable output must redact secret values (e.g., ***), and JSON output must never contain a secret value.
--name string (required): Variable key to create (case-insensitive uniqueness enforced).
--value string: Literal value. Required unless --secret is set and --prompt-value is used.
--secret: Create as secret (VariableValue.IsSecret=true). Secret values are write-only and must not be echoed.
--read-only bool: Set VariableValue.IsReadOnly (default false).
--prompt-value: Only valid with --secret. If --value is omitted, read from environment or prompt securely.
JSON export flags (--json, --jq, --template) registered via util.AddJSONFlags.
Value resolution rules (deterministic)
Non-secret variables:
Require --value (no prompting).
Secret variables (--secret):
If --value is provided: use it (but never echo/log it).
Else if --prompt-value is set:
Try env var AZDO_PIPELINES_SECRET_<NAME> (same pattern as azdo pipelines variable-group create --secret ...).
If missing and prompting is available, prompt securely.
If prompting is not available, return an error stating a value is required.
Else (no --value and no --prompt-value): return a validation error (“secret value required”).
Behavior
Parse [ORGANIZATION/]PROJECT/VARIABLE_GROUP_ID_OR_NAME using util.ParseProjectTargetWithDefaultOrganization; wrap parse errors with util.FlagErrorWrap.
Resolve the variable group ID-or-name using the shared helper shared.ResolveVariableGroup (do not re-implement ID/name resolution).
Fetch the variable group; error if it does not exist.
Reject Azure Key Vault variable groups:
If the group type indicates Azure Key Vault, reject creating variables (these groups are managed by Key Vault configuration rather than arbitrary variables).
Reject duplicate keys using case-insensitive comparison (error if a variable already exists).
Add the variable by inserting a new taskagent.VariableValue entry into the group’s variables map and call TaskAgent.UpdateVariableGroup.
Stop progress before output.
Output (single-object command):
Default output is a Go text template summarizing the created variable (name, group, secret/read-only flags). Never print a secret value; print *** when the created variable is secret.
Keep masking/redaction consistent with internal/cmd/pipelines/variablegroup/variable/list/list.go.
JSON output:
Emit the updated SDK variable group model returned by UpdateVariableGroup.
util.AddJSONFlags must list fields that match the SDK JSON tags (field-level contract). Suggested list:
Wire command: internal/cmd/pipelines/variablegroup/variable/variable.go must AddCommand(create.NewCmd(ctx)) and be reachable from azdo pipelines variable-group variable.
Parse scope: util.ParseProjectTargetWithDefaultOrganization(ctx, targetArg); wrap parse errors with util.FlagErrorWrap.
Client: Task Agent via ctx.ClientFactory().TaskAgent(ctx.Context(), scope.Organization).
Resolve group: shared.ResolveVariableGroup(ctx, taskClient, scope.Project, scope.Target) where ctx is the injected util.CmdContext passed into NewCmd/run.
Progress: ios.StartProgressIndicator(); defer ios.StopProgressIndicator() and stop before printing.
Create algorithm:
Reject Key Vault groups.
Reject duplicate key (case-insensitive).
Resolve value per rules (secret via env/prompt only when --prompt-value).
Insert into variables map and call TaskAgent.UpdateVariableGroup.
Enforce secret write-only semantics; never print or rely on server returning secret values.
Output:
Template: create internal/cmd/pipelines/variablegroup/variable/create/create.tpl and render it (single-object output).
JSON: emit the updated variable group SDK model; ensure util.AddJSONFlags list matches the SDK JSON tags used in output.
Tests:
Add unit tests at internal/cmd/pipelines/variablegroup/variable/create/create_test.go.
Hermetic mocks: Task Agent client + prompter.
Table-driven cases: non-secret with value, secret with explicit value, secret via env, secret via prompt, duplicate key error, group not found, Key Vault group rejection, secret value redaction in output/JSON.
Command Wiring
Implement the command in internal/cmd/pipelines/variablegroup/variable/create/create.go with NewCmd(ctx util.CmdContext) *cobra.Command.
Register it from internal/cmd/pipelines/variablegroup/variable/variable.go so azdo pipelines variable-group variable create is exposed.
SDK / Client Requirements
Requires the Task Agent client (ClientFactory().TaskAgent(...)) to update variable groups. Ensure the client exists; if not, follow "Handling Missing Azure DevOps SDK Clients" in AGENTS.md.
This issue tracks the implementation of the
azdo pipelines variable-group variable createcommand.Command Description
Add a variable to an existing variable group. The Azure CLI validates duplicate keys, optionally prompts for secret values, and persists the change via
update_variable_group(source).azdoshould provide the same behavior with explicit secret handling,isReadOnlysupport, and safer output.Important REST constraint:
***), and JSON output must never contain a secret value.azdoCommand SignatureFlags:
--name string(required): Variable key to create (case-insensitive uniqueness enforced).--value string: Literal value. Required unless--secretis set and--prompt-valueis used.--secret: Create as secret (VariableValue.IsSecret=true). Secret values are write-only and must not be echoed.--read-only bool: SetVariableValue.IsReadOnly(default false).--prompt-value: Only valid with--secret. If--valueis omitted, read from environment or prompt securely.--json,--jq,--template) registered viautil.AddJSONFlags.Value resolution rules (deterministic)
--value(no prompting).--secret):--valueis provided: use it (but never echo/log it).--prompt-valueis set:AZDO_PIPELINES_SECRET_<NAME>(same pattern asazdo pipelines variable-group create --secret ...).--valueand no--prompt-value): return a validation error (“secret value required”).Behavior
[ORGANIZATION/]PROJECT/VARIABLE_GROUP_ID_OR_NAMEusingutil.ParseProjectTargetWithDefaultOrganization; wrap parse errors withutil.FlagErrorWrap.shared.ResolveVariableGroup(do not re-implement ID/name resolution).taskagent.VariableValueentry into the group’s variables map and callTaskAgent.UpdateVariableGroup.***when the created variable is secret.internal/cmd/pipelines/variablegroup/variable/list/list.go.UpdateVariableGroup.util.AddJSONFlagsmust list fields that match the SDK JSON tags (field-level contract). Suggested list:id,name,type,description,variables,variableGroupProjectReferences,providerData,createdBy,createdOn,modifiedBy,modifiedOnImplementation Notes (filled checklist)
internal/cmd/pipelines/variablegroup/variable/create/create.go(type opts struct, NewCmd(ctx), run(ctx, opts)).internal/cmd/pipelines/variablegroup/variable/variable.gomustAddCommand(create.NewCmd(ctx))and be reachable fromazdo pipelines variable-group variable.util.ParseProjectTargetWithDefaultOrganization(ctx, targetArg); wrap parse errors withutil.FlagErrorWrap.ctx.ClientFactory().TaskAgent(ctx.Context(), scope.Organization).shared.ResolveVariableGroup(ctx, taskClient, scope.Project, scope.Target)wherectxis the injectedutil.CmdContextpassed intoNewCmd/run.ios.StartProgressIndicator(); defer ios.StopProgressIndicator()and stop before printing.--prompt-value).TaskAgent.UpdateVariableGroup.internal/cmd/pipelines/variablegroup/variable/create/create.tpland render it (single-object output).util.AddJSONFlagslist matches the SDK JSON tags used in output.internal/cmd/pipelines/variablegroup/variable/create/create_test.go.Command Wiring
internal/cmd/pipelines/variablegroup/variable/create/create.gowithNewCmd(ctx util.CmdContext) *cobra.Command.internal/cmd/pipelines/variablegroup/variable/variable.gosoazdo pipelines variable-group variable createis exposed.SDK / Client Requirements
ClientFactory().TaskAgent(...)) to update variable groups. Ensure the client exists; if not, follow "Handling Missing Azure DevOps SDK Clients" inAGENTS.md.References
variable_group_variable_add