Skip to content

Commit 69d4385

Browse files
authored
Merge pull request #784 from Azure/pallavisahadminvaluesupdate
Adding new test for admin password and admin usernames
2 parents 7bcf6f6 + 7c41237 commit 69d4385

File tree

8 files changed

+223
-57
lines changed

8 files changed

+223
-57
lines changed

.github/workflows/run-unit-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010
steps:
1111
- name: Check out repository
12-
uses: actions/checkout@v2
12+
uses: actions/checkout@v4
1313
- name: InstallPester
1414
id: InstallPester
1515
shell: pwsh
@@ -38,7 +38,7 @@ jobs:
3838
Write-Host "::debug:: RunPester $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')"
3939
& './arm-ttk/GitHubWorkflow/Steps/RunPester.ps1' @Parameters
4040
- name: PublishTestResults
41-
uses: actions/upload-artifact@v2
41+
uses: actions/upload-artifact@v4
4242
with:
4343
name: PesterResults
4444
path: '**.TestResults.xml'

arm-ttk/arm-ttk.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@{
2-
ModuleVersion = 0.25
2+
ModuleVersion = 0.26
33
ModuleToProcess = 'arm-ttk.psm1'
44
Description = 'Validation tools for Azure Resource Manager Templates'
55
FormatsToProcess = 'arm-ttk.format.ps1xml'
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<#
2+
.Synopsis
3+
Ensures that all adminPasswords are expressions
4+
.Description
5+
Ensures that all properties within a template named adminPassword are expressions, not literal strings
6+
#>
7+
param(
8+
[Parameter(Mandatory = $true)]
9+
[PSObject]
10+
$TemplateObject
11+
)
12+
13+
# Find all references to an adminPassword
14+
# Filtering the complete $TemplateObject directly fails with "The script failed due to call depth overflow." errors
15+
function Check-PasswordsInTemplate {
16+
param (
17+
[Parameter(Mandatory = $true)]
18+
[PSObject]
19+
$TemplateObject,
20+
[Parameter(Mandatory = $true)]
21+
[string]
22+
$AdminPwd
23+
)
24+
if ("resources" -in $TemplateObject.PSobject.Properties.Name) {
25+
$adminPwdRefsResources = $TemplateObject.resources |
26+
Find-JsonContent -Key $AdminPwd -Value * -Like
27+
28+
foreach ($ref in $adminPwdRefsResources) {
29+
# Walk over each one
30+
# if the property is not a string, then it's likely a param value for a nested deployment, and we should skip it.
31+
$a = $ref.$AdminPwd
32+
if ($a -isnot [string]) {
33+
#check to see if this is a param value on a nested deployment - it will have a value property
34+
if ($a.value -is [string]) {
35+
$trimmedPwd = "$($a.value)".Trim()
36+
}
37+
else {
38+
continue # since we don't know what object shape we're testing at this point (could be a param declaration on a nested deployment)
39+
}
40+
}
41+
else {
42+
$trimmedPwd = "$($a)".Trim()
43+
}
44+
if ($trimmedPwd -notmatch '\[[^\]]+\]') {
45+
# If they aren't expressions
46+
Write-Error -TargetObject $ref -Message "AdminPassword `"$trimmedPwd`" is not an expression" -ErrorId AdminPassword.Is.Literal # write an error
47+
continue # and move onto the next
48+
}
49+
}
50+
}
51+
52+
if ("variables" -in $TemplateObject.PSobject.Properties.Name) {
53+
$adminPwdRefsVariables = $TemplateObject.variables |
54+
Find-JsonContent -Key $AdminPwd -Value * -Like
55+
56+
foreach ($ref in $adminPwdRefsVariables) {
57+
# Walk over each one
58+
# if the property is not a string, then it's likely a param value for a nested deployment, and we should skip it.
59+
if ($ref.$AdminPwd -isnot [string]) { continue }
60+
$trimmedPwd = "$($ref.$AdminPwd)".Trim()
61+
if ($trimmedPwd -notmatch '\[[^\]]+\]') {
62+
# If they aren't expressions
63+
Write-Error -TargetObject $ref -Message "AdminPassword `"$trimmedPwd`" is variable which is not an expression" -ErrorId AdminPassword.Var.Is.Literal # write an error
64+
continue # and move onto the next
65+
}
66+
}
67+
68+
# TODO - irregular doesn't handle null gracefully so we need to test for it
69+
if ($trimmedPwd -ne $null) {
70+
$PwdHasVariable = $trimmedPwd | ?<ARM_Variable> -Extract
71+
# this will return the outer most function in the expression
72+
$PwdHasFunction = $trimmedPwd | ?<ARM_Template_Function> -Extract
73+
74+
# If we had a variable reference (not inside of another function) - then check it
75+
# TODO this will not flag things like concat so we should add a blacklist here to ensure it's still not a static or deterministic password
76+
if ($PwdHasVariable -and $PwdHasFunction.FunctionName -eq 'variables') {
77+
$variableValue = $TemplateObject.variables.($PwdHasVariable.VariableName)
78+
$variableValueExpression = $variableValue | ?<ARM_Template_Expression>
79+
if (-not $variableValueExpression) {
80+
Write-Error "AdminPassword references variable '$($PwdHasVariable.variableName)', which has a literal value. " -ErrorId AdminPassword.Is.Variable.Literal # write an error
81+
}
82+
}
83+
}
84+
}
85+
}
86+
87+
$pwdValues = @("administratorLoginPassword", "adminPassword")
88+
foreach ($pwdValue in $pwdValues) {
89+
Check-PasswordsInTemplate -TemplateObject $TemplateObject -AdminPwd $pwdValue
90+
}

arm-ttk/testcases/deploymentTemplate/adminUsername-Should-Not-Be-A-Literal.test.ps1

Lines changed: 66 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,67 +12,79 @@ param(
1212

1313
# Find all references to an adminUserName
1414
# Filtering the complete $TemplateObject directly fails with "The script failed due to call depth overflow." errors
15-
16-
if ("resources" -in $TemplateObject.PSobject.Properties.Name) {
17-
$adminUserNameRefsResources = $TemplateObject.resources |
18-
Find-JsonContent -Key adminUsername -Value * -Like
19-
20-
foreach ($ref in $adminUserNameRefsResources) {
21-
# Walk over each one
22-
# if the property is not a string, then it's likely a param value for a nested deployment, and we should skip it.
23-
$a = $ref.adminUsername
24-
if ($a -isnot [string]) {
25-
#check to see if this is a param value on a nested deployment - it will have a value property
26-
if ($a.value -is [string]) {
27-
$trimmedUserName = "$($a.value)".Trim()
15+
function Check-UsernamesInTemplate {
16+
param (
17+
[Parameter(Mandatory = $true)]
18+
[PSObject]
19+
$TemplateObject,
20+
[Parameter(Mandatory = $true)]
21+
[string]
22+
$AdminUsername
23+
)
24+
if ("resources" -in $TemplateObject.PSobject.Properties.Name) {
25+
$adminUserNameRefsResources = $TemplateObject.resources |
26+
Find-JsonContent -Key $AdminUsername -Value * -Like
27+
28+
foreach ($ref in $adminUserNameRefsResources) {
29+
# Walk over each one
30+
# if the property is not a string, then it's likely a param value for a nested deployment, and we should skip it.
31+
$a = $ref.$AdminUsername
32+
if ($a -isnot [string]) {
33+
#check to see if this is a param value on a nested deployment - it will have a value property
34+
if ($a.value -is [string]) {
35+
$trimmedUserName = "$($a.value)".Trim()
36+
}
37+
else {
38+
continue # since we don't know what object shape we're testing at this point (could be a param declaration on a nested deployment)
39+
}
2840
}
2941
else {
30-
continue # since we don't know what object shape we're testing at this point (could be a param declaration on a nested deployment)
42+
$trimmedUserName = "$($a)".Trim()
43+
}
44+
if ($trimmedUserName -notmatch '\[[^\]]+\]') {
45+
# If they aren't expressions
46+
Write-Error -TargetObject $ref -Message "AdminUsername `"$trimmedUserName`" is not an expression" -ErrorId AdminUsername.Is.Literal # write an error
47+
continue # and move onto the next
3148
}
32-
}
33-
else {
34-
$trimmedUserName = "$($a)".Trim()
35-
}
36-
if ($trimmedUserName -notmatch '\[[^\]]+\]') {
37-
# If they aren't expressions
38-
Write-Error -TargetObject $ref -Message "AdminUsername `"$trimmedUserName`" is not an expression" -ErrorId AdminUsername.Is.Literal # write an error
39-
continue # and move onto the next
4049
}
4150
}
42-
}
43-
44-
if ("variables" -in $TemplateObject.PSobject.Properties.Name) {
45-
$adminUserNameRefsVariables = $TemplateObject.variables |
46-
Find-JsonContent -Key adminUsername -Value * -Like
47-
48-
foreach ($ref in $adminUserNameRefsVariables) {
49-
# Walk over each one
50-
# if the property is not a string, then it's likely a param value for a nested deployment, and we should skip it.
51-
if ($ref.adminUserName -isnot [string]) { continue }
52-
$trimmedUserName = "$($ref.adminUserName)".Trim()
53-
if ($trimmedUserName -notmatch '\[[^\]]+\]') {
54-
# If they aren't expressions
55-
Write-Error -TargetObject $ref -Message "AdminUsername `"$trimmedUserName`" is variable which is not an expression" -ErrorId AdminUsername.Var.Is.Literal # write an error
56-
continue # and move onto the next
51+
52+
if ("variables" -in $TemplateObject.PSobject.Properties.Name) {
53+
$adminUserNameRefsVariables = $TemplateObject.variables |
54+
Find-JsonContent -Key $AdminUsername -Value * -Like
55+
56+
foreach ($ref in $adminUserNameRefsVariables) {
57+
# Walk over each one
58+
# if the property is not a string, then it's likely a param value for a nested deployment, and we should skip it.
59+
if ($ref.$AdminUsername -isnot [string]) { continue }
60+
$trimmedUserName = "$($ref.$AdminUsername)".Trim()
61+
if ($trimmedUserName -notmatch '\[[^\]]+\]') {
62+
# If they aren't expressions
63+
Write-Error -TargetObject $ref -Message "AdminUsername `"$trimmedUserName`" is variable which is not an expression" -ErrorId AdminUsername.Var.Is.Literal # write an error
64+
continue # and move onto the next
65+
}
5766
}
58-
}
59-
60-
# TODO - irregular doesn't handle null gracefully so we need to test for it
61-
if ($trimmedUserName -ne $null) {
62-
$UserNameHasVariable = $trimmedUserName | ?<ARM_Variable> -Extract
63-
# this will return the outer most function in the expression
64-
$userNameHasFunction = $trimmedUserName | ?<ARM_Template_Function> -Extract
65-
66-
# If we had a variable reference (not inside of another function) - then check it
67-
# TODO this will not flag things like concat so we should add a blacklist here to ensure it's still not a static or deterministic username
68-
if ($UserNameHasVariable -and $userNameHasFunction.FunctionName -eq 'variables') {
69-
$variableValue = $TemplateObject.variables.($UserNameHasVariable.VariableName)
70-
$variableValueExpression = $variableValue | ?<ARM_Template_Expression>
71-
if (-not $variableValueExpression) {
72-
Write-Error @"
73-
AdminUsername references variable '$($UserNameHasVariable.variableName)', which has a literal value.
74-
"@ -ErrorId AdminUserName.Is.Variable.Literal # write an error
67+
68+
# TODO - irregular doesn't handle null gracefully so we need to test for it
69+
if ($trimmedUserName -ne $null) {
70+
$UserNameHasVariable = $trimmedUserName | ?<ARM_Variable> -Extract
71+
# this will return the outer most function in the expression
72+
$userNameHasFunction = $trimmedUserName | ?<ARM_Template_Function> -Extract
73+
74+
# If we had a variable reference (not inside of another function) - then check it
75+
# TODO this will not flag things like concat so we should add a blacklist here to ensure it's still not a static or deterministic username
76+
if ($UserNameHasVariable -and $userNameHasFunction.FunctionName -eq 'variables') {
77+
$variableValue = $TemplateObject.variables.($UserNameHasVariable.VariableName)
78+
$variableValueExpression = $variableValue | ?<ARM_Template_Expression>
79+
if (-not $variableValueExpression) {
80+
Write-Error "AdminUsername references variable '$($UserNameHasVariable.variableName)', which has a literal value. " -ErrorId AdminUserName.Is.Variable.Literal # write an error
81+
}
7582
}
7683
}
7784
}
7885
}
86+
87+
$usernameValues = @("administratorLogin", "adminUsername")
88+
foreach ($usernameValue in $usernameValues) {
89+
Check-UsernamesInTemplate -TemplateObject $TemplateObject -AdminUsername $usernameValue
90+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3+
"contentVersion": "1.0.0.0",
4+
"variables": {
5+
"administratorLogin": "fixedusername",
6+
"administratorLoginPassword": "fixedpassword"
7+
},
8+
"resources": [
9+
{
10+
"type": "Microsoft.DBforMySQL/flexibleServers",
11+
"apiVersion": "2021-05-01",
12+
"name": "name",
13+
"location": "location",
14+
"properties": {
15+
"administratorLogin": "[variables('administratorLogin')]",
16+
"administratorLoginPassword": "[variables('administratorLoginPassword')]"
17+
}
18+
}
19+
]
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3+
"contentVersion": "1.0.0.0",
4+
"variables": {
5+
"deploymentName": "Deployment-1.0"
6+
},
7+
"resources": [
8+
{
9+
"properties": {
10+
"parameters": {
11+
"adminPassword": {
12+
"value": "[reference(resourceId('Microsoft.Resources/deployments', variables('deploymentName')), '2019-10-01').outputs.accountSettings.value.accountAdminName]"
13+
}
14+
}
15+
}
16+
}
17+
]
18+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
#requires -module arm-ttk
3+
. $PSScriptRoot\..\arm-ttk.test.functions.ps1
4+
Test-TTK $psScriptRoot
5+
return
6+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3+
"contentVersion": "1.0.0.0",
4+
"variables": {
5+
"administratorLogin": "fixedusername",
6+
"administratorLoginPassword": "fixedpassword"
7+
},
8+
"resources": [
9+
{
10+
"type": "Microsoft.DBforMySQL/flexibleServers",
11+
"apiVersion": "2021-05-01",
12+
"name": "name",
13+
"location": "location",
14+
"properties": {
15+
"administratorLogin": "[variables('administratorLogin')]",
16+
"administratorLoginPassword": "[variables('administratorLoginPassword')]"
17+
}
18+
}
19+
]
20+
}

0 commit comments

Comments
 (0)