Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5195c67
Add stack-whatif commands.
kalbert312 Feb 20, 2026
c8e405c
Add no pretty print argument.
kalbert312 Feb 20, 2026
8330002
Add boilerplate for print deployment stack what-if results.
kalbert312 Feb 20, 2026
c2eb4f2
Implement legend output.
kalbert312 Feb 23, 2026
5fe8967
Progress on what-if formatting.
kalbert312 Feb 23, 2026
f63fa64
Add --no-color parameter.
kalbert312 Feb 23, 2026
48217fd
What-if output progress.
kalbert312 Feb 24, 2026
68f59b9
What-if resource change progress.
kalbert312 Feb 24, 2026
4a74275
What-if potential resource change progress.
kalbert312 Feb 25, 2026
dac4c03
What-if resource deletion summary progress.
kalbert312 Feb 25, 2026
9ef68ed
Add indents automatically.
kalbert312 Feb 25, 2026
29f898b
Type hints.
kalbert312 Feb 25, 2026
d188186
Diagnostics formatting progress.
kalbert312 Feb 25, 2026
8797f4e
Fix type logic.
kalbert312 Feb 25, 2026
a655102
Fix count.
kalbert312 Feb 25, 2026
8e696fb
Split into methods.
kalbert312 Feb 26, 2026
73db109
Extensible resource output support.
kalbert312 Feb 26, 2026
da7b890
Array children rendering fixes and misc refactors.
kalbert312 Feb 26, 2026
db27cd8
Array children rendering fixes and misc refactors.
kalbert312 Feb 26, 2026
1ed9b95
Remove redundant "change" label.
kalbert312 Feb 27, 2026
210f4bc
Add exclusions for wait command + rg. Is consistent with stack commands.
kalbert312 Feb 27, 2026
679df3a
Fix lint errors. Add resource class formatting method.
kalbert312 Feb 27, 2026
b5ed433
Update extensible resource display.
kalbert312 Feb 27, 2026
42c7914
Lint fixes.
kalbert312 Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions src/azure-cli/azure/cli/command_modules/resource/_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Color(Enum):
GREEN = "\033[38;5;77m"
PURPLE = "\033[38;5;141m"
BLUE = "\033[38;5;39m"
CYAN = "\033[38;5;51m"
GRAY = "\033[38;5;246m"
RED = "\033[38;5;203m"
DARK_YELLOW = "\033[38;5;136m"
Expand All @@ -26,11 +27,15 @@ def __init__(self, enable_color=True):
self._enable_color = enable_color
self._contents = []
self._colors = deque()
self._indents = []

def build(self):
return "".join(self._contents)

def append(self, value, color=None):
def append(self, value, color=None, no_indent=False):
if not no_indent and self._should_indent():
self._contents.append(''.join(self._indents))

if color:
self._push_color(color)

Expand All @@ -41,14 +46,55 @@ def append(self, value, color=None):

return self

def append_line(self, value="", color=None):
self.append(f"{str(value)}\n", color)

return self
def append_line(self, value="", color=None, no_indent=False):
self.append(value, color, no_indent)
return self.append("\n", no_indent=True)

def new_color_scope(self, color):
return self.ColorScope(self, color)

def insert(self, index, value="", color=None, no_indent=False):
if color and self._enable_color:
self._contents.insert(index, str(Color.RESET))

self._contents.insert(index, str(value))

if color and self._enable_color:
self._contents.insert(index, str(color))

if not no_indent and self._should_indent(index, True):
self._contents.insert(index, ''.join(self._indents))

return self

def insert_line(self, index, value="", color=None, no_indent=False):
self.insert(index, "\n", no_indent=no_indent)
return self.insert(index, value, color, no_indent)

def get_current_index(self):
return len(self._contents)

def push_indent(self, indent):
self._indents.append(indent)

def pop_indent(self):
self._indents.pop()

def ensure_num_new_lines(self, num_new_lines):
if len(self._contents) == 0:
self.append("\n" * num_new_lines)
return

last_entry = self._contents[-1]
existing_newlines = len(last_entry) - len(last_entry.rstrip('\n'))
remaining_newlines = num_new_lines - existing_newlines

if remaining_newlines > 0:
self._contents.append("\n" * remaining_newlines)

def clear(self):
self._contents = []

def _push_color(self, color):
if not self._enable_color:
return
Expand All @@ -63,6 +109,10 @@ def _pop_color(self):
self._colors.pop()
self._contents.append(str(self._colors[-1] if self._colors else Color.RESET))

def _should_indent(self, index=-1, is_insert=False):
return len(self._indents) > 0 and (
not self._contents or self._contents[max(index - 1, 0) if is_insert else index].endswith("\n"))

# pylint: disable=protected-access
class ColorScope:
def __init__(self, color_string_builder, color):
Expand Down
96 changes: 96 additions & 0 deletions src/azure-cli/azure/cli/command_modules/resource/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -2460,6 +2460,12 @@
long-summary: Deployment stacks are defined in ARM as the type Microsoft.Resources/deploymentStacks.
"""

helps['stack-whatif'] = """
type: group
short-summary: A deployment stack What-If is a preview of an operation to be performed on a new or existing deployment stack.
long-summary: Deployment stacks What-Ifs are defined in ARM as the type Microsoft.Resources/deploymentStacksWhatIfResults.
"""

helps['stack mg'] = """
type: group
short-summary: Manage Deployment Stacks at management group.
Expand Down Expand Up @@ -2547,6 +2553,34 @@
text: az stack mg delete --id /providers/Microsoft.Management/managementGroups/myMg/providers/Microsoft.Resources/deploymentStacks/StackName --management-group-id myMg --action-on-unmanage deleteAll
"""

helps['stack-whatif mg'] = """
type: group
short-summary: Manage Deployment Stacks What-Ifs at management group.
"""

# TODO(kylealbert): params in examples
helps['stack-whatif mg create'] = """
type: command
short-summary: Preview a deployment stack operation at management group scope.
examples:
- name: Perform a what-if on a deployment stack using template file and detach all resources on unmanage.
text: az stack-whatif mg create --name StackName --management-group-id myMg --template-file simpleTemplate.json --location westus2 --description description --deny-settings-mode None --action-on-unmanage detachAll
- name: Perform a what-if on a deployment stack with parameter file and delete resources on unmanage.
text: az stack-whatif mg create --name StackName --management-group-id myMg --action-on-unmanage deleteResources --template-file simpleTemplate.json --parameters simpleTemplateParams.json --location westus2 --description description --deny-settings-mode None
- name: Perform a what-if on a deployment stack with template spec.
text: az stack-whatif mg create --name StackName --management-group-id myMg --template-spec TemplateSpecResourceIDWithVersion --location westus2 --description description --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack using bicep file and delete all resources on unmanage.
text: az stack-whatif mg create --name StackName --management-group-id myMg --action-on-unmanage deleteAll --template-file simple.bicep --location westus2 --description description --deny-settings-mode None
- name: Perform a what-if on a deployment stack using parameters from key/value pairs.
text: az stack-whatif mg create --name StackName --management-group-id myMg --template-file simpleTemplate.json --location westus --description description --parameters simpleTemplateParams.json value1=foo value2=bar --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, using a parameter file, a remote parameter file, and selectively overriding key/value pairs.
text: az stack-whatif mg create --name StackName --management-group-id myMg --template-file azuredeploy.json --parameters @params.json --parameters https://mysite/params.json --parameters MyValue=This MyArray=@array.json --location westus --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, using deny settings.
text: az stack-whatif mg create --name StackName --management-group-id myMg --template-file azuredeploy.json --deny-settings-mode denyDelete --deny-settings-excluded-actions Microsoft.Compute/virtualMachines/write --deny-settings-excluded-principals "test1 test2" --location westus --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, apply deny settings to child scope.
text: az stack-whatif mg create --name StackName --management-group-id myMg --template-file azuredeploy.json --deny-settings-mode denyDelete --deny-settings-excluded-actions Microsoft.Compute/virtualMachines/write --deny-settings-apply-to-child-scopes --location westus --action-on-unmanage deleteResources
"""

helps['stack sub'] = """
type: group
short-summary: Manage Deployment Stacks at subscription.
Expand Down Expand Up @@ -2642,6 +2676,38 @@
text: az stack sub delete --id /subscriptions/111111111111/providers/Microsoft.Resources/deploymentStacks/StackName --action-on-unmanage detachAll
"""

helps['stack-whatif sub'] = """
type: group
short-summary: Manage Deployment Stacks What-Ifs at subscription.
"""

# TODO(kylealbert): params in examples
helps['stack-whatif sub create'] = """
type: command
short-summary: Preview a deployment stack operation at subscription scope.
examples:
- name: Perform a what-if on a deployment stack using template file and detach all resources on unmanage.
text: az stack-whatif sub create --name StackName --template-file simpleTemplate.json --location westus2 --description description --deny-settings-mode None --action-on-unmanage detachAll
- name: Perform a what-if on a deployment stack with parameter file and delete resources on unmanage.
text: az stack-whatif sub create --name StackName --action-on-unmanage deleteResources --template-file simpleTemplate.json --parameters simpleTemplateParams.json --location westus2 --description description --deny-settings-mode None
- name: Perform a what-if on a deployment stack with template spec.
text: az stack-whatif sub create --name StackName --template-spec TemplateSpecResourceIDWithVersion --location westus2 --description description --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack using bicep file and delete all resources on unmanage.
text: az stack-whatif sub create --name StackName --action-on-unmanage deleteAll --template-file simple.bicep --location westus2 --description description --deny-settings-mode None
- name: Perform a what-if on a deployment stack at a different subscription.
text: az stack-whatif sub create --name StackName --template-file simpleTemplate.json --location westus2 --description description --subscription subscriptionId --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack and deploy at the resource group scope.
text: az stack-whatif sub create --name StackName --template-file simpleTemplate.json --location westus --deployment-resource-group ResourceGroup --description description --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack using parameters from key/value pairs.
text: az stack-whatif sub create --name StackName --template-file simpleTemplate.json --location westus --description description --parameters simpleTemplateParams.json value1=foo value2=bar --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, using a parameter file, a remote parameter file, and selectively overriding key/value pairs.
text: az stack-whatif sub create --name StackName --template-file azuredeploy.json --parameters @params.json --parameters https://mysite/params.json --parameters MyValue=This MyArray=@array.json --location westus --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, using deny settings.
text: az stack-whatif sub create --name StackName --template-file azuredeploy.json --deny-settings-mode denyDelete --deny-settings-excluded-actions Microsoft.Compute/virtualMachines/write --deny-settings-excluded-principals "test1 test2" --location westus --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, apply deny settings to child scopes.
text: az stack-whatif sub create --name StackName --template-file azuredeploy.json --deny-settings-mode denyDelete --deny-settings-excluded-actions Microsoft.Compute/virtualMachines/write --deny-settings-apply-to-child-scopes --location westus --action-on-unmanage deleteResources
"""

helps['stack group'] = """
type: group
short-summary: Manage Deployment Stacks at resource group.
Expand Down Expand Up @@ -2733,6 +2799,36 @@
text: az stack group delete --id /subscriptions/111111111111/resourceGroups/ResourceGroup/providers/Microsoft.Resources/deploymentStacks/StackName --action-on-unmanage detachAll
"""

helps['stack-whatif group'] = """
type: group
short-summary: Manage Deployment Stacks What-Ifs at resource group.
"""

# TODO(kylealbert): params in examples
helps['stack-whatif group create'] = """
type: command
short-summary: Preview a deployment stack operation at resource group scope.
examples:
- name: Perform a what-if on a deployment stack using template file and delete resources on unmanage.
text: az stack-whatif group create --name StackName --resource-group ResourceGroup --action-on-unmanage deleteResources --template-file simpleTemplate.json --description description --deny-settings-mode None
- name: Perform a what-if on a deployment stack with parameter file and detach all resources on unmanage.
text: az stack-whatif group create --name StackName --resource-group ResourceGroup --action-on-unmanage detachAll --template-file simpleTemplate.json --parameters simpleTemplateParams.json --description description --deny-settings-mode None
- name: Perform a what-if on a deployment stack with template spec and delete all resources on unmanage.
text: az stack-whatif group create --name StackName --resource-group ResourceGroup --action-on-unmanage deleteAll --template-spec TemplateSpecResourceIDWithVersion --description description --deny-settings-mode None
- name: Perform a what-if on a deployment stack using bicep file.
text: az stack-whatif group create --name StackName --resource-group ResourceGroup --template-file simple.bicep --description description --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack at a different subscription.
text: az stack-whatif group create --name StackName --resource-group ResourceGroup --template-file simpleTemplate.json --description description --subscription subscriptionId --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack using parameters from key/value pairs.
text: az stack-whatif group create --name StackName --template-file simpleTemplate.json --resource-group ResourceGroup --description description --parameters simpleTemplateParams.json value1=foo value2=bar --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, using a parameter file, a remote parameter file, and selectively overriding key/value pairs.
text: az stack-whatif group create --name StackName --template-file azuredeploy.json --parameters @params.json --parameters https://mysite/params.json --parameters MyValue=This MyArray=@array.json --resource-group ResourceGroup --deny-settings-mode None --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, using deny settings.
text: az stack-whatif group create --name StackName --resource-group ResourceGroup --template-file azuredeploy.json --deny-settings-mode denyDelete --deny-settings-excluded-actions Microsoft.Compute/virtualMachines/write --deny-settings-excluded-principals "test1 test2" --action-on-unmanage deleteResources
- name: Perform a what-if on a deployment stack from a local template, apply deny setting to child scopes.
text: az stack-whatif group create --name StackName --resource-group ResourceGroup --template-file azuredeploy.json --deny-settings-mode denyDelete --deny-settings-excluded-actions Microsoft.Compute/virtualMachines/write --deny-settings-apply-to-child-scopes --action-on-unmanage deleteResources
"""

helps['bicep generate-params'] = """
type: command
short-summary: Generate parameters file for a Bicep file.
Expand Down
10 changes: 8 additions & 2 deletions src/azure-cli/azure/cli/command_modules/resource/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ def load_arguments(self, _):
min_api='2019-07-01')
deployment_what_if_no_pretty_print_type = CLIArgumentType(options_list=['--no-pretty-print'], action='store_true',
help='Disable pretty-print for What-If results. When set, the output format type will be used.')
deployment_what_if_no_color_type = CLIArgumentType(options_list=['--no-color'], action='store_true',
help='Disable color in pretty-printed what-if results.')
deployment_what_if_confirmation_type = CLIArgumentType(options_list=['--confirm-with-what-if', '-c'], action='store_true',
help='Instruct the command to run deployment What-If before executing the deployment. It then prompts you to acknowledge resource changes before it continues.',
min_api='2019-07-01')
Expand Down Expand Up @@ -621,8 +623,7 @@ def load_arguments(self, _):
with self.argument_context('stack mg') as c:
c.argument('management_group_id', arg_type=management_group_id_type, help='The management group id to create stack at.')

# TODO(kylealbert): add "stack-whatif"
for resource_type in ['stack']:
for resource_type in ['stack', 'stack-whatif']:
for scope in ['group', 'sub', 'mg']:
for action in ['create', 'validate', 'delete', 'show', 'list', 'export']:
if resource_type == 'stack-whatif' and (action == 'validate' or action == 'export'):
Expand Down Expand Up @@ -662,6 +663,8 @@ def load_arguments(self, _):
elif resource_type == 'stack-whatif':
c.argument('stack_id', arg_type=stacks_whatif_stack_id_type)
c.argument('retention_interval', arg_type=stacks_whatif_retention_interval_type)
c.argument('no_pretty_print', arg_type=deployment_what_if_no_pretty_print_type)
c.argument('no_color', arg_type=deployment_what_if_no_color_type)
if action == 'create' and resource_type == 'stack':
c.argument('yes', help='Do not prompt for confirmation')
elif action == 'delete':
Expand All @@ -680,6 +683,9 @@ def load_arguments(self, _):
c.argument('subscription', arg_type=subscription_type)
if scope == 'group':
c.argument('resource_group', arg_type=resource_group_name_type, help='The resource group where the deployment stack exists')
if resource_type == 'stack-whatif':
c.argument('no_pretty_print', arg_type=deployment_what_if_no_pretty_print_type)
c.argument('no_color', arg_type=deployment_what_if_no_color_type)
elif action == 'list':
if scope == 'sub':
continue # only uses global arguments
Expand Down
Loading
Loading