Skip to content

Commit 3e5afed

Browse files
feat(new): Added Azure.PostgreSQL.MaintenanceWindow (Azure#2928)
1 parent 8ae0448 commit 3e5afed

File tree

5 files changed

+207
-5
lines changed

5 files changed

+207
-5
lines changed

docs/CHANGELOG-v1.md

+5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
2929

3030
## Unreleased
3131

32+
- New rules:
33+
- Azure Database for PostgreSQL:
34+
- Verify that Azure Database for PostgreSQL servers have a customer-controlled maintenance window configured by @BenjaminEngeset.
35+
[#2927](https://github.com/Azure/PSRule.Rules.Azure/issues/2927)
36+
3237
What's changed since pre-release v1.37.0:
3338

3439
- New rules:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
severity: Important
3+
pillar: Reliability
4+
category: RE:04 Target metrics
5+
resource: Azure Database for PostgreSQL
6+
online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.PostgreSQL.MaintenanceWindow/
7+
---
8+
9+
# Customer-controlled maintenance window configuration
10+
11+
## SYNOPSIS
12+
13+
Configure a customer-controlled maintenance window for Azure Database for PostgreSQL servers.
14+
15+
## DESCRIPTION
16+
17+
Azure Database for PostgreSQL flexible servers undergo periodic maintenance to ensure your managed database remains secure, stable, and up-to-date. This maintenance includes applying security updates, system upgrades, and software patches.
18+
19+
Maintenance windows can be scheduled in two ways for each flexible server:
20+
21+
- System-Managed Schedule: The system automatically selects a one-hour window between 11 PM and 7 AM in your server’s regional time.
22+
- Custom Schedule: You can specify a preferred maintenance window by choosing the day of the week and a one-hour time window.
23+
24+
By configuring a customer-controlled maintenance window, you can schedule updates to occur during a preferred time, ideally outside business hours, minimizing disruptions.
25+
26+
Only the flexible server deployment model supports customer-controlled maintenance windows.
27+
28+
## RECOMMENDATION
29+
30+
Consider using a customer-controlled maintenance window to efficiently schedule updates and minimize disruptions.
31+
32+
## EXAMPLES
33+
34+
### Configure with Azure template
35+
36+
To configure servers that pass this rule:
37+
38+
- Set the `properties.maintenanceWindow.customWindow` property to `Enabled`.
39+
40+
For example:
41+
42+
```json
43+
{
44+
"type": "Microsoft.DBforPostgreSQL/flexibleServers",
45+
"apiVersion": "2023-03-01-preview",
46+
"name": "[parameters('serverName')]",
47+
"location": "[parameters('location')]",
48+
"sku": {
49+
"name": "Standard_D16as",
50+
"tier": "GeneralPurpose"
51+
},
52+
"properties": {
53+
"administratorLogin": "[parameters('administratorLogin')]",
54+
"administratorLoginPassword": "[parameters('administratorLoginPassword')]",
55+
"createMode": "Default",
56+
"version": "[parameters('postgresqlVersion')]",
57+
"maintenanceWindow": {
58+
"customWindow": "Enabled",
59+
"dayOfWeek": "0",
60+
"startHour": "1",
61+
"startMinute": "0"
62+
}
63+
}
64+
}
65+
```
66+
67+
### Configure with Bicep
68+
69+
To configure servers that pass this rule:
70+
71+
- Set the `properties.maintenanceWindow.customWindow` property to `Enabled`.
72+
73+
For example:
74+
75+
```bicep
76+
resource postgresqlDbServer 'Microsoft.DBforPostgreSQL/flexibleServers@2023-03-01-preview' = {
77+
name: serverName
78+
location: location
79+
sku: {
80+
name: 'Standard_D16as'
81+
tier: 'GeneralPurpose'
82+
}
83+
properties: {
84+
administratorLogin: administratorLogin
85+
administratorLoginPassword: administratorLoginPassword
86+
createMode: 'Default'
87+
version: postgresqlVersion
88+
maintenanceWindow: {
89+
customWindow: 'Enabled'
90+
dayOfWeek: 0
91+
startHour: 1
92+
startMinute: 0
93+
}
94+
}
95+
}
96+
```
97+
98+
## NOTES
99+
100+
The custom schedule maintenance window feature is only available for the flexible server deployment model.
101+
102+
## LINKS
103+
104+
- [RE:04 Target metrics](https://learn.microsoft.com/azure/well-architected/reliability/metrics)
105+
- [Scheduled maintenance in Azure Database for PostgreSQL](https://learn.microsoft.com/azure/postgresql/flexible-server/concepts-maintenance)
106+
- [Select a maintenance window](https://learn.microsoft.com/azure/postgresql/flexible-server/concepts-maintenance#select-a-maintenance-window)
107+
- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.dbforpostgresql/flexibleservers)

src/PSRule.Rules.Azure/rules/Azure.PostgreSQL.Rule.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,22 @@ spec:
7070
- field: properties.authConfig.passwordAuth
7171
equals: Disabled
7272

73+
---
74+
# Synopsis: Configure a customer-controlled maintenance window for Azure Database for PostgreSQL servers.
75+
apiVersion: github.com/microsoft/PSRule/v1
76+
kind: Rule
77+
metadata:
78+
name: Azure.PostgreSQL.MaintenanceWindow
79+
ref: AZR-000433
80+
tags:
81+
release: GA
82+
ruleSet: 2024_06
83+
Azure.WAF/pillar: Reliability
84+
spec:
85+
type:
86+
- Microsoft.DBforPostgreSQL/flexibleServers
87+
condition:
88+
field: properties.maintenanceWindow.customWindow
89+
equals: Enabled
90+
7391
#endregion Region

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

+23-5
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ Describe 'Azure.PostgreSQL' -Tag 'PostgreSQL' {
128128
# Fail
129129
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
130130
$ruleResult | Should -Not -BeNullOrEmpty;
131-
$ruleResult.Length | Should -Be 4;
132-
$ruleResult.TargetName | Should -BeIn 'server-A', 'server-B', 'server-E', 'server-F';
131+
$ruleResult.Length | Should -Be 5;
132+
$ruleResult.TargetName | Should -BeIn 'server-A', 'server-B', 'server-E', 'server-F', 'server-G';
133133

134134
$ruleResult[0].Reason | Should -BeExactly "The Azure Database for PostgreSQL 'server-B' should have geo-redundant backup configured.";
135135
$ruleResult[1].Reason | Should -BeExactly "The Azure Database for PostgreSQL 'server-A' should have geo-redundant backup configured.";
@@ -168,8 +168,8 @@ Describe 'Azure.PostgreSQL' -Tag 'PostgreSQL' {
168168
# Fail
169169
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
170170
$ruleResult | Should -Not -BeNullOrEmpty;
171-
$ruleResult.Length | Should -Be 6;
172-
$ruleResult.TargetName | Should -BeIn 'server-A', 'server-B', 'server-D', 'server-E', 'ActiveDirectoryAdmin-A', 'ActiveDirectoryAdmin-C';
171+
$ruleResult.Length | Should -Be 7;
172+
$ruleResult.TargetName | Should -BeIn 'server-A', 'server-B', 'server-D', 'server-E', 'server-G', 'ActiveDirectoryAdmin-A', 'ActiveDirectoryAdmin-C';
173173

174174
$ruleResult[0].Reason | Should -BeIn 'Path properties.administratorType: Is null or empty.', 'Path properties.login: Is null or empty.', 'Path properties.sid: Is null or empty.';
175175
$ruleResult[1].Reason | Should -BeIn "A sub-resource of type 'Microsoft.DBforPostgreSQL/servers/administrators' has not been specified.";
@@ -196,8 +196,26 @@ Describe 'Azure.PostgreSQL' -Tag 'PostgreSQL' {
196196
# Pass
197197
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
198198
$ruleResult | Should -Not -BeNullOrEmpty;
199+
$ruleResult.Length | Should -Be 2;
200+
$ruleResult.TargetName | Should -BeIn 'server-F', 'server-G';
201+
}
202+
203+
It 'Azure.PostgreSQL.MaintenanceWindow' {
204+
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.PostgreSQL.MaintenanceWindow' };
205+
206+
# Fail
207+
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
208+
$ruleResult.Length | Should -Be 3;
209+
$ruleResult.TargetName | Should -BeIn 'server-D', 'server-E', 'server-F';
210+
211+
$ruleResult[0].Reason | Should -BeExactly "Path properties.maintenanceWindow.customWindow: The field 'properties.maintenanceWindow.customWindow' does not exist.";
212+
$ruleResult[1].Reason | Should -BeExactly "Path properties.maintenanceWindow.customWindow: Is set to 'notset'.";
213+
$ruleResult[2].Reason | Should -BeExactly "Path properties.maintenanceWindow.customWindow: Is set to 'Disabled'.";
214+
215+
# Pass
216+
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
199217
$ruleResult.Length | Should -Be 1;
200-
$ruleResult.TargetName | Should -BeIn 'server-F';
218+
$ruleResult.TargetName | Should -BeIn 'server-G';
201219
}
202220
}
203221

tests/PSRule.Rules.Azure.Tests/Resources.PostgreSQL.json

+54
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,12 @@
372372
"backup": {
373373
"backupRetentionDays": 7
374374
},
375+
"maintenanceWindow": {
376+
"customWindow": "notset",
377+
"dayOfWeek": "notset",
378+
"startHour": "notset",
379+
"startMinute": "notset"
380+
},
375381
"highAvailability": {
376382
"mode": "Disabled"
377383
},
@@ -423,6 +429,12 @@
423429
"backupRetentionDays": 7,
424430
"geoRedundantBackup": "Disabled"
425431
},
432+
"maintenanceWindow": {
433+
"customWindow": "Disabled",
434+
"dayOfWeek": 0,
435+
"startHour": 1,
436+
"startMinute": 0
437+
},
426438
"highAvailability": {
427439
"mode": "Disabled"
428440
},
@@ -454,6 +466,48 @@
454466
}
455467
]
456468
},
469+
{
470+
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.DBforPostgreSQL/flexibleServers/server-G",
471+
"Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.DBforPostgreSQL/flexibleServers/server-G",
472+
"Location": "region",
473+
"ResourceName": "server-G",
474+
"Name": "server-G",
475+
"Properties": {
476+
"administratorLogin": "db-admin",
477+
"authConfig": {
478+
"activeDirectoryAuth": "Enabled",
479+
"passwordAuth": "Disabled",
480+
"tenantId": "00000000-0000-0000-0000-000000000000"
481+
},
482+
"storage": {
483+
"storageSizeGB": 20
484+
},
485+
"backup": {
486+
"backupRetentionDays": 7,
487+
"geoRedundantBackup": "Disabled"
488+
},
489+
"maintenanceWindow": {
490+
"customWindow": "Enabled",
491+
"dayOfWeek": 0,
492+
"startHour": 1,
493+
"startMinute": 0
494+
},
495+
"highAvailability": {
496+
"mode": "Disabled"
497+
},
498+
"version": "14",
499+
"createMode": "Default"
500+
},
501+
"ResourceGroupName": "test-rg",
502+
"Type": "Microsoft.DBforPostgreSQL/flexibleServers",
503+
"ResourceType": "Microsoft.DBforPostgreSQL/flexibleServers",
504+
"Sku": {
505+
"Name": "E64",
506+
"Tier": "MemoryOptimized"
507+
},
508+
"Tags": null,
509+
"SubscriptionId": "00000000-0000-0000-0000-000000000000"
510+
},
457511
{
458512
"Name": "ActiveDirectoryAdmin-A",
459513
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.DBforPostgreSQL/servers/server-B/administrators/ActiveDirectoryAdmin-A",

0 commit comments

Comments
 (0)