Skip to content

Commit d16ff84

Browse files
feat(new): Added Azure.AppService.MigrateNodeJs (Azure#2889)
* feat(new): Added Azure.AppService.MigrateNodeJs * feat: Updated Azure.NodeJsVersion * fix: Fixed typo * feat: Updated Azure.AppService.NodeJsVersion * fix: Fixed whitespace
1 parent 5e5a82e commit d16ff84

File tree

5 files changed

+1731
-44
lines changed

5 files changed

+1731
-44
lines changed

docs/CHANGELOG-v1.md

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
3030
## Unreleased
3131

3232
- New rules:
33+
- App Service:
34+
- Check that applications uses supported Node.js runtime versions by @BenjaminEngeset.
35+
[#2879](https://github.com/Azure/PSRule.Rules.Azure/issues/2879)
3336
- Azure Cache for Redis:
3437
- Verify that cache instances have Entra ID authentication enabled by @BenjaminEngeset.
3538
[#2899](https://github.com/Azure/PSRule.Rules.Azure/issues/2899)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
---
2+
severity: Important
3+
pillar: Security
4+
category: SE:02 Secured development lifecycle
5+
resource: App Service
6+
online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.AppService.NodeJsVersion/
7+
---
8+
9+
# Use a supported Node.js runtime version
10+
11+
## SYNOPSIS
12+
13+
Configure applications to use supported Node.js runtime versions.
14+
15+
## DESCRIPTION
16+
17+
In an App Service app, you can configure the Node.js runtime version used to run your application or site code.
18+
19+
Extended support for Node.js 18 LTS will end on April 30, 2025. While apps hosted on App Service will continue to operate, security updates and customer support for Node.js 18 LTS will no longer be provided after this date.
20+
21+
To avoid potential security vulnerabilities and minimize risks for your App Service apps, it is recommended to upgrade your apps to Node.js 20 LTS before April 30, 2025.
22+
23+
## RECOMMENDATION
24+
25+
Consider updating applications to use supported Node.js runtime versions to maintain security and support.
26+
27+
## EXAMPLES
28+
29+
### Configure with Azure template
30+
31+
To deploy App Services that pass this rule:
32+
33+
- For Linux-based apps and slots:
34+
- Set the `properties.siteConfig.linuxFxVersion` property to `NODE|20-lts`.
35+
- For Windows-based apps and slots:
36+
- Add an app setting within `properties.siteConfig.appSettings` by creating an object with the `name` and `value` properties.
37+
- Set the `name` property to `WEBSITE_NODE_DEFAULT_VERSION`.
38+
- Set the `value` property to `~20`.
39+
40+
In addition to setting the `properties.siteConfig` property, you can also use a sub-resource.
41+
42+
For example (Linux app):
43+
44+
```json
45+
{
46+
"type": "Microsoft.Web/sites",
47+
"apiVersion": "2022-09-01",
48+
"name": "[parameters('name')]",
49+
"location": "[parameters('location')]",
50+
"identity": {
51+
"type": "SystemAssigned"
52+
},
53+
"kind": "web",
54+
"properties": {
55+
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('planName'))]",
56+
"httpsOnly": true,
57+
"siteConfig": {
58+
"minTlsVersion": "1.2",
59+
"linuxFxVersion": "NODE|20-lts"
60+
}
61+
},
62+
"dependsOn": [
63+
"[resourceId('Microsoft.Web/serverfarms', parameters('planName'))]"
64+
]
65+
}
66+
```
67+
68+
For example (Windows app):
69+
70+
```json
71+
{
72+
"type": "Microsoft.Web/sites",
73+
"apiVersion": "2022-09-01",
74+
"name": "[parameters('name')]",
75+
"location": "[parameters('location')]",
76+
"identity": {
77+
"type": "SystemAssigned"
78+
},
79+
"kind": "web",
80+
"properties": {
81+
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('planName'))]",
82+
"httpsOnly": true,
83+
"siteConfig": {
84+
"minTlsVersion": "1.2",
85+
"appSettings": [
86+
{
87+
"name": "WEBSITE_NODE_DEFAULT_VERSION",
88+
"value": "~20"
89+
}
90+
]
91+
}
92+
},
93+
"dependsOn": [
94+
"[resourceId('Microsoft.Web/serverfarms', parameters('planName'))]"
95+
]
96+
}
97+
```
98+
99+
### Configure with Bicep
100+
101+
To deploy App Services that pass this rule:
102+
103+
- For Linux-based apps and slots:
104+
- Set the `properties.siteConfig.linuxFxVersion` property to `NODE|20-lts`.
105+
- For Windows-based apps and slots:
106+
- Add an app setting within `properties.siteConfig.appSettings` by creating an object with the `name` and `value` properties.
107+
- Set the `name` property to `WEBSITE_NODE_DEFAULT_VERSION`.
108+
- Set the `value` property to `~20`.
109+
110+
In addition to setting the `properties.siteConfig` property, you can also use a sub-resource.
111+
112+
For example (Linux app):
113+
114+
```bicep
115+
resource linuxWeb 'Microsoft.Web/sites@2022-09-01' = {
116+
name: name
117+
location: location
118+
identity: {
119+
type: 'SystemAssigned'
120+
}
121+
kind: 'web'
122+
properties: {
123+
serverFarmId: plan.id
124+
httpsOnly: true
125+
siteConfig: {
126+
minTlsVersion: '1.2'
127+
linuxFxVersion: 'NODE|20-lts'
128+
}
129+
}
130+
}
131+
```
132+
133+
For example (Windows app):
134+
135+
```bicep
136+
resource windowsWeb 'Microsoft.Web/sites@2022-09-01' = {
137+
name: name
138+
location: location
139+
identity: {
140+
type: 'SystemAssigned'
141+
}
142+
kind: 'web'
143+
properties: {
144+
serverFarmId: plan.id
145+
httpsOnly: true
146+
siteConfig: {
147+
minTlsVersion: '1.2'
148+
appSettings: [
149+
{
150+
name: 'WEBSITE_NODE_DEFAULT_VERSION'
151+
value: '~20'
152+
}
153+
]
154+
}
155+
}
156+
}
157+
```
158+
159+
## LINKS
160+
161+
- [SE:02 Secured development lifecycle](https://learn.microsoft.com/azure/well-architected/security/secure-development-lifecycle)
162+
- [Upgrade your App Service apps to Node 20 LTS by 30 April 2025](https://azure.microsoft.com/updates/action-required-upgrade-your-app-service-apps-to-node-20-lts-by-30-april-2025/)
163+
- [Node.js on App Service](https://github.com/Azure/app-service-linux-docs/blob/master/Runtime_Support/node_support.md)
164+
- [Azure resource deployment](https://learn.microsoft.com/azure/templates/microsoft.web/sites)
165+
- [Azure resource deployment](https://learn.microsoft.com/azure/templates/microsoft.web/sites/slots)
166+
- [Azure resource deployment](https://learn.microsoft.com/azure/templates/microsoft.web/sites/config-web)
167+
- [Azure resource deployment](https://learn.microsoft.com/azure/templates/microsoft.web/sites/slots/config-web)
168+
- [Azure resource deployment](https://learn.microsoft.com/azure/templates/microsoft.web/sites/config-appsettings)
169+
- [Azure resource deployment](https://learn.microsoft.com/azure/templates/microsoft.web/sites/slots/config-appsettings)

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

+85-18
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ Rule 'Azure.AppService.MinTLS' -Ref 'AZR-000073' -Type 'Microsoft.Web/sites', 'M
1515
$siteConfigs = @(GetWebSiteConfig);
1616
if ($siteConfigs.Length -eq 0) {
1717
return $Assert.
18-
HasFieldValue($TargetObject, 'properties.siteConfig.minTlsVersion', '1.2').
19-
ReasonFrom('properties.siteConfig.minTlsVersion', $LocalizedData.MinTLSVersion, $TargetObject.properties.siteConfig.minTlsVersion);
18+
HasFieldValue($TargetObject, 'properties.siteConfig.minTlsVersion', '1.2').
19+
ReasonFrom('properties.siteConfig.minTlsVersion', $LocalizedData.MinTLSVersion, $TargetObject.properties.siteConfig.minTlsVersion);
2020
}
2121
foreach ($siteConfig in $siteConfigs) {
2222
$path = $siteConfig._PSRule.path;
2323
$Assert.
24-
HasFieldValue($siteConfig, 'properties.minTlsVersion', '1.2').
25-
ReasonFrom('properties.minTlsVersion', $LocalizedData.MinTLSVersion, $siteConfig.properties.minTlsVersion).PathPrefix($path);
24+
HasFieldValue($siteConfig, 'properties.minTlsVersion', '1.2').
25+
ReasonFrom('properties.minTlsVersion', $LocalizedData.MinTLSVersion, $siteConfig.properties.minTlsVersion).PathPrefix($path);
2626
}
2727
}
2828

@@ -129,8 +129,8 @@ Rule 'Azure.AppService.HTTP2' -Ref 'AZR-000078' -Type 'Microsoft.Web/sites', 'Mi
129129
# Synopsis: Configure and enable instance health probes.
130130
Rule 'Azure.AppService.WebProbe' -Ref 'AZR-000079' -With 'Azure.AppService.IsWebApp' -Tag @{ release = 'GA'; ruleSet = '2022_06'; 'Azure.WAF/pillar' = 'Reliability'; } {
131131
$siteConfigs = @(GetWebSiteConfig | Where-Object {
132-
$Assert.HasField($_, 'Properties.healthCheckPath').Result
133-
});
132+
$Assert.HasField($_, 'Properties.healthCheckPath').Result
133+
});
134134
if ($siteConfigs.Length -eq 0) {
135135
return $Assert.HasFieldValue($TargetObject, 'properties.siteConfig.healthCheckPath');
136136
}
@@ -142,8 +142,8 @@ Rule 'Azure.AppService.WebProbe' -Ref 'AZR-000079' -With 'Azure.AppService.IsWeb
142142
# Synopsis: Web apps should use a dedicated health check path.
143143
Rule 'Azure.AppService.WebProbePath' -Ref 'AZR-000080' -With 'Azure.AppService.IsWebApp' -Tag @{ release = 'GA'; ruleSet = '2022_06'; 'Azure.WAF/pillar' = 'Reliability'; } {
144144
$siteConfigs = @(GetWebSiteConfig | Where-Object {
145-
$Assert.HasField($_, 'properties.healthCheckPath').Result
146-
});
145+
$Assert.HasField($_, 'properties.healthCheckPath').Result
146+
});
147147
if ($siteConfigs.Length -eq 0) {
148148
return $Assert.Greater($TargetObject, 'properties.siteConfig.healthCheckPath', 1);
149149
}
@@ -155,19 +155,37 @@ Rule 'Azure.AppService.WebProbePath' -Ref 'AZR-000080' -With 'Azure.AppService.I
155155
# Synopsis: Web apps should disable insecure FTP and configure SFTP when required.
156156
Rule 'Azure.AppService.WebSecureFtp' -Ref 'AZR-000081' -With 'Azure.AppService.IsWebApp' -Tag @{ release = 'GA'; ruleSet = '2022_06'; 'Azure.WAF/pillar' = 'Security'; } -Labels @{ 'Azure.MCSB.v1/control' = 'DP-3' } {
157157
$siteConfigs = @(GetWebSiteConfig | Where-Object {
158-
$Assert.HasField($_, 'Properties.ftpsState').Result
159-
});
158+
$Assert.HasField($_, 'Properties.ftpsState').Result
159+
});
160160
if ($siteConfigs.Length -eq 0) {
161161
return $Assert.In($TargetObject, 'Properties.siteConfig.ftpsState', @(
162-
'FtpsOnly'
163-
'Disabled'
164-
));
162+
'FtpsOnly'
163+
'Disabled'
164+
));
165165
}
166166
foreach ($siteConfig in $siteConfigs) {
167167
$Assert.In($siteConfig, 'Properties.ftpsState', @(
168-
'FtpsOnly'
169-
'Disabled'
170-
));
168+
'FtpsOnly'
169+
'Disabled'
170+
));
171+
}
172+
}
173+
174+
# Synopsis: Configure applications to use supported Node.js runtime versions.
175+
Rule 'Azure.AppService.NodeJsVersion' -Ref 'AZR-000428' -Type 'Microsoft.Web/sites', 'Microsoft.Web/sites/config', 'Microsoft.Web/sites/slots', 'Microsoft.Web/sites/slots/config' -Tag @{ release = 'GA'; ruleSet = '2024_06'; 'Azure.WAF/pillar' = 'Security'; } {
176+
$versions = Get-NodeVersions
177+
178+
$pass = $true
179+
foreach ($version in $versions) {
180+
if ($version -lt '20.0') {
181+
$pass = $false
182+
$Assert.Version($version.ToString(), '.', '>=20.0.0')
183+
}
184+
}
185+
186+
# Pass if the version is not defined or version is 20 or greater.
187+
if ($pass) {
188+
$Assert.Pass()
171189
}
172190
}
173191

@@ -203,10 +221,59 @@ function global:GetWebSiteConfig {
203221
param ()
204222
process {
205223
$siteConfigs = @(GetSubResources -ResourceType 'Microsoft.Web/sites/config', 'Microsoft.Web/sites/slots/config' | Where-Object {
206-
$_.Name -notlike "*/*" -or $_.Name -like "*/web" -or $_.Id -like "*/web"
207-
})
224+
$_.Name -notlike "*/*" -or $_.Name -like "*/web" -or $_.Id -like "*/web"
225+
})
208226
$siteConfigs;
209227
}
210228
}
211229

230+
function global:Get-NodeVersions {
231+
<#
232+
.SYNOPSIS
233+
Get the Node.js versions for the App Service.
234+
235+
.DESCRIPTION
236+
This function retrieves the Node.js versions for the App Service.
237+
238+
.OUTPUTS
239+
Output is a list of Node.js versions used, except the 'NODE|lts' version as this is not version specific,
240+
hence not parsable.
241+
#>
242+
[CmdletBinding()]
243+
param ( )
244+
245+
[Version[]]$versions = @(
246+
# App Service on Linux. Works when main object equals Microsoft.Web/sites or Microsoft.Web/sites/slots
247+
$TargetObject.properties.siteConfig.linuxFxVersion | Where-Object { $_ -like 'NODE|*' -and $_ -ne 'NODE|lts' }
248+
# App Service on Linux. Works for when main object equals Microsoft.Web/sites/config 'web' or Microsoft.Web/sites/slots/config 'web'
249+
$TargetObject.properties.linuxFxVersion | Where-Object { $_ -like 'NODE|*' -and $_ -ne 'NODE|lts' }
250+
# App Service on Linux.
251+
GetSubResources -ResourceType 'Microsoft.Web/sites/slots' |
252+
ForEach-Object { $_.properties.siteConfig.linuxFxVersion | Where-Object { $_ -like 'NODE|*' -and $_ -ne 'NODE|lts' } }
253+
# App Service on Linux.
254+
GetSubResources -ResourceType 'Microsoft.Web/sites/config', 'Microsoft.Web/sites/slots/config' |
255+
Where-Object { $_.name -eq 'web' -or $_.name -like '*/web' } |
256+
ForEach-Object { $_.properties.linuxFxVersion | Where-Object { $_ -like 'NODE|*' -and $_ -ne 'NODE|lts' } }
257+
258+
# App Service on Windows. Works for when main object equals Microsoft.Web/sites or Microsoft.Web/sites/slots
259+
$TargetObject.properties.siteConfig.appSettings | Where-Object name -eq 'WEBSITE_NODE_DEFAULT_VERSION' | ForEach-Object { $_.value }
260+
# App Service on Windows. Works for when main object equals Microsoft.Web/sites/config 'appsettings' or Microsoft.Web/sites/slots/config 'appsettings'
261+
$TargetObject.properties.WEBSITE_NODE_DEFAULT_VERSION
262+
# App Service on Windows. Works for when main object equals Microsoft.Web/sites/config 'web' or Microsoft.Web/sites/slots/config 'web'
263+
$TargetObject.properties.appSettings | Where-Object name -eq 'WEBSITE_NODE_DEFAULT_VERSION' | ForEach-Object { $_.value }
264+
# App Service on Windows.
265+
GetSubResources -ResourceType 'Microsoft.Web/sites/slots' |
266+
ForEach-Object { $_.properties.siteConfig.appSettings | Where-Object name -eq 'WEBSITE_NODE_DEFAULT_VERSION' | ForEach-Object { $_.value } }
267+
# App Service on Windows.
268+
GetSubResources -ResourceType 'Microsoft.Web/sites/config', 'Microsoft.Web/sites/slots/config' |
269+
Where-Object { $_.name -eq 'appsettings' -or $_.name -like '*/appsettings' } |
270+
ForEach-Object { $_.properties.WEBSITE_NODE_DEFAULT_VERSION }
271+
# App Service on Windows.
272+
GetSubResources -ResourceType 'Microsoft.Web/sites/config', 'Microsoft.Web/sites/slots/config' |
273+
Where-Object { $_.name -eq 'web' -or $_.name -like '*/web' } |
274+
ForEach-Object { $_.properties.appSettings | Where-Object name -eq 'WEBSITE_NODE_DEFAULT_VERSION' | ForEach-Object { $_.value } }
275+
) -replace '[^\d.]' -match '.' -replace '^\d+$', '$0.0'
276+
$versions
277+
}
278+
212279
#endregion Helper functions

0 commit comments

Comments
 (0)