Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## Upcoming Release
#### Az.Resources
* Improved deployment stack What-If output to show configuration details for resource creates and deletes, including potential changes.
- Displays top-level fields such as location, kind, SKU, and tags followed by properties, while omitting redundant resource header fields.
- Indented resource details beneath their headings and aligned inline JSON formatting with Azure CLI output.
- Aligns with [Azure/azure-cli#34156](https://github.com/Azure/azure-cli/pull/34156).

## 16.4.0 - October 2026
#### Az.Compute 11.10.0
* Added '-ScheduleProfileStart' and '-MinimumCommitmentDayCount' parameters to 'New-AzCapacityReservation' to create Future Capacity Reservations, and surfaced the read-only 'ScheduleProfile' (including 'ModifiableUntil') and instance view 'ReservationStateInfo' on 'Get-AzCapacityReservation' output.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ public class DeploymentStackWhatIfFormatter
{
private const int IndentSize = 2;

private static readonly HashSet<string> ExcludedResourceTopLevelProperties = new HashSet<string>(StringComparer.Ordinal)
{
"apiVersion",
"extension",
"id",
"identifiers",
"name",
"resourceGroup",
"type"
};

private static readonly string[] AllWhatIfTopLevelChangeTypes = new[]
{
"Create",
Expand Down Expand Up @@ -540,6 +551,7 @@ private static int GetPotentialChangeTypePriority(string changeType)
private void FormatResourceChange(PSDeploymentStackWhatIfResourceChange resourceChange)
{
FormatResourceHeadingLine(resourceChange);
this.builder.PushIndent(new string(' ', IndentSize));

if (resourceChange.ManagementStatusChange != null)
{
Expand All @@ -551,9 +563,9 @@ private void FormatResourceChange(PSDeploymentStackWhatIfResourceChange resource
FormatPrimitiveChange(resourceChange.DenyStatusChange, "Deny Status");
}

bool formattedCreatedProperties = FormatCreatedResourceProperties(resourceChange);
bool hasResourceConfiguration = FormatResourceConfiguration(resourceChange);

if (!formattedCreatedProperties && resourceChange.ResourceConfigurationChanges?.Delta != null)
if (!hasResourceConfiguration && resourceChange.ResourceConfigurationChanges?.Delta != null)
{
foreach (var delta in resourceChange.ResourceConfigurationChanges.Delta)
{
Expand All @@ -572,6 +584,8 @@ private void FormatResourceChange(PSDeploymentStackWhatIfResourceChange resource
}
}
}

this.builder.PopIndent();
}

private static bool ShouldSuppressResourcePropertyDelta(PSDeploymentStackWhatIfPropertyChange delta)
Expand Down Expand Up @@ -604,28 +618,37 @@ private static bool ShouldSuppressResourcePropertyDelta(PSDeploymentStackWhatIfP
string.Equals(delta.ChangeType, "Modify", StringComparison.OrdinalIgnoreCase));
}

private bool FormatCreatedResourceProperties(PSDeploymentStackWhatIfResourceChange resourceChange)
private bool FormatResourceConfiguration(PSDeploymentStackWhatIfResourceChange resourceChange)
{
if (!string.Equals(resourceChange.ChangeType, "Create", StringComparison.OrdinalIgnoreCase) ||
!(resourceChange.ResourceConfigurationChanges?.After is JObject after))
JObject configuration = null;
if (string.Equals(resourceChange.ChangeType, "Create", StringComparison.OrdinalIgnoreCase))
{
return false;
configuration = resourceChange.ResourceConfigurationChanges?.After as JObject;
}
else if (string.Equals(resourceChange.ChangeType, "Delete", StringComparison.OrdinalIgnoreCase))
{
configuration = resourceChange.ResourceConfigurationChanges?.Before as JObject;
}

JToken properties = after.GetValue("properties", StringComparison.OrdinalIgnoreCase);
if (properties == null || properties.Type == JTokenType.Null)
if (configuration == null || !configuration.HasValues)
{
return false;
}

return FormatPrimitiveChange(
new PSDeploymentStackWhatIfPropertyChange
var properties = configuration.Properties()
.Where(property => !ExcludedResourceTopLevelProperties.Contains(property.Name))
.OrderBy(property => property.Name == "properties")
.ThenBy(property => property.Name, StringComparer.Ordinal);

foreach (var property in properties)
{
if (!IsNullValue(property.Value))
{
Path = "properties",
ChangeType = "Create",
After = properties
},
"properties");
FormatPropertyValue(resourceChange.ChangeType, property.Name, property.Value.ToString(Formatting.Indented));
}
}

return true;
}

private void FormatResourceHeadingLine(PSDeploymentStackWhatIfResourceChange resourceChange)
Expand Down Expand Up @@ -816,7 +839,6 @@ private bool FormatPrimitiveChange(object change, string path)
changeType = Equals(before, after) ? "NoEffect" : "Modify";
}

var (symbol, color) = GetChangeTypeFormatting(changeType);
string formattedValue;

if (string.Equals(changeType, "Modify", StringComparison.OrdinalIgnoreCase))
Expand All @@ -829,15 +851,22 @@ private bool FormatPrimitiveChange(object change, string path)
formattedValue = FormatValue(value);
}

return FormatPropertyValue(changeType, path, formattedValue);
}

private bool FormatPropertyValue(string changeType, string path, string formattedValue)
{
var (symbol, color) = GetChangeTypeFormatting(changeType);
this.builder.Append(symbol, color).Append(" ");
this.builder.Append(path).Append(":");

if (formattedValue.IndexOfAny(new[] { '\r', '\n' }) >= 0)
{
this.builder.AppendLine();
string[] lines = formattedValue.Replace("\r\n", "\n").Split('\n');
this.builder.Append(" ").Append(lines[0], color).AppendLine();
this.builder.PushIndent(new string(' ', IndentSize));

foreach (string line in formattedValue.Replace("\r\n", "\n").Split('\n'))
foreach (string line in lines.Skip(1))
{
this.builder.Append(line, color).AppendLine();
}
Expand Down
Loading
Loading