Skip to content

Commit 3907935

Browse files
authored
Fixed symbolic references with Azure.Deployment.AdminUsername Azure#3146 (Azure#3160)
1 parent f42d4a9 commit 3907935

File tree

7 files changed

+3855
-19
lines changed

7 files changed

+3855
-19
lines changed

docs/CHANGELOG-v1.md

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
3232
- Bug fixes:
3333
- Fixed evaluation of `Azure.NSG.LateralTraversal` with empty string properties by @BernieWhite.
3434
[#3130](https://github.com/Azure/PSRule.Rules.Azure/issues/3130)
35+
- Fixed evaluation of `Azure.Deployment.AdminUsername` with symbolic references by @BernieWhite.
36+
[#3146](https://github.com/Azure/PSRule.Rules.Azure/issues/3146)
3537

3638
## v1.40.0-B0029 (pre-release)
3739

src/PSRule.Rules.Azure/Runtime/Helper.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ public static string CompressExpression(string expression)
5555
/// </summary>
5656
public static bool HasLiteralValue(string expression)
5757
{
58-
return !IsTemplateExpression(expression) ||
59-
TokenStreamValidator.HasLiteralValue(ExpressionParser.Parse(expression));
58+
return !string.IsNullOrEmpty(expression) && (
59+
!IsTemplateExpression(expression) ||
60+
TokenStreamValidator.HasLiteralValue(ExpressionParser.Parse(expression))
61+
);
6062
}
6163

6264
/// <summary>

src/PSRule.Rules.Azure/rules/Azure.Deployment.Rule.ps1

+13-4
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,17 @@ function global:RecurseDeploymentSensitive {
123123
[PSObject]$Deployment
124124
)
125125
process {
126+
Write-Debug "Deployment is: $($Deployment.name)";
126127
$propertyNames = $Configuration.GetStringValues('AZURE_DEPLOYMENT_SENSITIVE_PROPERTY_NAMES');
128+
129+
# Resources could be an object or an array. Check if it is an object and enumerate properties instead.
127130
$resources = @($Deployment.properties.template.resources);
131+
if ($Deployment.properties.template.resources -is [System.Management.Automation.PSObject]) {
132+
$resources = @($Deployment.properties.template.resources.PSObject.Properties.GetEnumerator() | ForEach-Object {
133+
$_.Value
134+
});
135+
}
136+
128137
if ($resources.Length -eq 0) {
129138
return $Assert.Pass();
130139
}
@@ -140,7 +149,7 @@ function global:RecurseDeploymentSensitive {
140149
$Assert.Pass();
141150
}
142151
else {
143-
Write-Debug "Found property name: $propertyName";
152+
Write-Debug "Found property name: $propertyName, value: $found";
144153
foreach ($value in $found) {
145154
$Assert.Create(![PSRule.Rules.Azure.Runtime.Helper]::HasLiteralValue($value), $LocalizedData.LiteralSensitiveProperty, $propertyName);
146155
}
@@ -167,9 +176,9 @@ function global:RecursivePropertiesSecretEvaluation {
167176
[Bool]$ShouldUseSecret = $True
168177
)
169178
process {
170-
$PropertyName = $Property.psObject.properties.Name
171-
foreach ($NestedProperty in $Property.PSObject.Properties.Value.PSObject.Properties ) {
172-
if($NestedProperty.MemberType -eq 'NoteProperty'){
179+
$PropertyName = $Property.PSObject.properties.Name
180+
foreach ($NestedProperty in $Property.PSObject.Properties.Value.PSObject.Properties) {
181+
if($NestedProperty.MemberType -eq 'NoteProperty') {
173182
RecursivePropertiesSecretEvaluation -Resource $Resource -SecureParameters $SecureParameters -Property $NestedProperty -ShouldUseSecret $ShouldUseSecret
174183
} else {
175184
CheckPropertyUsesSecureParameter -Resource $Resource -SecureParameters $SecureParameters -PropertyPath "properties.$($PropertyName)" -ShouldUseSecret $ShouldUseSecret

tests/PSRule.Rules.Azure.Tests/Azure.Deployment.Tests.ps1

+34-3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,28 @@ Describe 'Azure.Deployment' -Tag 'Deployment' {
7676
$targetNames | Should -BeIn 'secret_good', 'streaming_jobs_good', 'reference_good';
7777
}
7878
}
79+
80+
Context 'With Template' {
81+
BeforeAll {
82+
$templatePath = Join-Path -Path $here -ChildPath 'deployment.tests.json';
83+
$outputFile = Join-Path -Path $rootPath -ChildPath out/tests/Resources.Deployment.json;
84+
Export-AzRuleTemplateData -TemplateFile $templatePath -OutputPath $outputFile;
85+
$result = Invoke-PSRule -Module PSRule.Rules.Azure -InputPath $outputFile -Outcome All -WarningAction Ignore -ErrorAction Stop;
86+
}
87+
88+
It 'Azure.Deployment.Name' {
89+
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.Deployment.Name' };
90+
91+
# Fail
92+
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
93+
$ruleResult | Should -BeNullOrEmpty;
94+
95+
# Pass
96+
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
97+
$ruleResult | Should -Not -BeNullOrEmpty;
98+
$ruleResult.Length | Should -Be 2;
99+
}
100+
}
79101
}
80102

81103
Describe 'Azure.Deployment' -Tag 'Deployment' {
@@ -162,9 +184,18 @@ Describe 'Azure.Deployment.AdminUsername' -Tag 'Deployment' {
162184
$ruleResult | Should -Not -BeNullOrEmpty;
163185
$ruleResult.Length | Should -Be 3;
164186
}
187+
}
165188

166-
It 'Azure.Deployment.Name' {
167-
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.Deployment.Name' };
189+
Context 'With Bicep with symbolic names' {
190+
BeforeAll {
191+
$templatePath = Join-Path -Path $here -ChildPath 'Bicep/SymbolicNameTestCases/Tests.Bicep.5.json';
192+
$outputFile = Join-Path -Path $rootPath -ChildPath out/tests/Resources.Deployment.json;
193+
Export-AzRuleTemplateData -TemplateFile $templatePath -OutputPath $outputFile;
194+
$result = Invoke-PSRule -Module PSRule.Rules.Azure -InputPath $outputFile -Outcome All -WarningAction Ignore -ErrorAction Stop;
195+
}
196+
197+
It 'Azure.Deployment.AdminUsername' {
198+
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.Deployment.AdminUsername' };
168199

169200
# Fail
170201
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
@@ -173,7 +204,7 @@ Describe 'Azure.Deployment.AdminUsername' -Tag 'Deployment' {
173204
# Pass
174205
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
175206
$ruleResult | Should -Not -BeNullOrEmpty;
176-
$ruleResult.Length | Should -Be 2;
207+
$ruleResult.Length | Should -Be 10;
177208
}
178209
}
179210
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// Test case for https://github.com/Azure/PSRule.Rules.Azure/issues/3146
5+
// Contributed by @riosengineer
6+
7+
param rgName string = 'rg-test'
8+
param location string = 'eastus'
9+
param sqlServerName string = 's1'
10+
param sqlAdministrators object = {
11+
azureADOnlyAuthentication: true
12+
login: 'group1'
13+
sid: 'sid'
14+
principalType: 'Group'
15+
}
16+
param sqlDatabase object = {
17+
name: 'sqldb'
18+
tier: 'Basic'
19+
sku: 'Basic'
20+
maxSizeBytes: 2147483648
21+
collation: 'SQL_Latin1_General_CP1_CI_AS'
22+
}
23+
24+
// Azure SQL DB
25+
module sqlDb 'br/public:avm/res/sql/server:0.8.0' = {
26+
name: 'abc'
27+
scope: resourceGroup(rgName)
28+
params: {
29+
name: sqlServerName
30+
location: location
31+
minimalTlsVersion: '1.2'
32+
managedIdentities: {
33+
systemAssigned: true
34+
}
35+
privateEndpoints: [
36+
{
37+
name: 'pe'
38+
customNetworkInterfaceName: 'pe-sql-nic'
39+
subnetResourceId: ''
40+
privateDnsZoneGroup: {
41+
privateDnsZoneGroupConfigs: [
42+
{
43+
privateDnsZoneResourceId: ''
44+
}
45+
]
46+
}
47+
}
48+
]
49+
publicNetworkAccess: 'Disabled'
50+
securityAlertPolicies: [
51+
{
52+
name: 'Default'
53+
state: 'Enabled'
54+
emailAccountAdmins: true
55+
}
56+
]
57+
administrators: {
58+
azureADOnlyAuthentication: sqlAdministrators.azureADOnlyAuthentication
59+
login: sqlAdministrators.login
60+
sid: sqlAdministrators.sid
61+
principalType: sqlAdministrators.principalType
62+
}
63+
databases: [
64+
{
65+
name: sqlDatabase.name
66+
collation: sqlDatabase.collation
67+
maxSizeBytes: sqlDatabase.maxSizeBytes
68+
skuTier: sqlDatabase.tier
69+
skuName: sqlDatabase.sku
70+
}
71+
]
72+
}
73+
}

0 commit comments

Comments
 (0)