From 34dbe900207973568c8f018251d98ebda24bc7d7 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Tue, 14 Jul 2026 16:14:28 +0300 Subject: [PATCH 1/7] feat(fleet): add Azure.Fleet.SecureBoot rule (AZR-000545) Implements #3729. Signed-off-by: Alex Chen --- src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 b/src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 index b0f8c37903a..2f4ac50edb7 100644 --- a/src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 +++ b/src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 @@ -13,4 +13,13 @@ Rule 'Azure.Fleet.PublicKey' -Ref 'AZR-000541' -Type 'Microsoft.AzureFleet/fleet Reason($LocalizedData.FleetPublicKey, $PSRule.TargetName) } + +# Synopsis: Azure Fleets should use Trusted Launch with Secure Boot enabled. +Rule 'Azure.Fleet.SecureBoot' -Ref 'AZR-000545' -Type 'Microsoft.AzureFleet/fleets' -Tag @{ release = 'GA'; ruleSet = '2026_06'; 'Azure.WAF/pillar' = 'Security'; } -Labels @{ 'Azure.WAF/maturity' = 'L2' } { + $Assert.In($TargetObject, 'properties.computeProfile.baseVirtualMachineProfile.securityProfile.securityType', @('TrustedLaunch', 'ConfidentialVM')). + Reason($LocalizedData.FleetSecureBoot, $PSRule.TargetName) + $Assert.HasFieldValue($TargetObject, 'properties.computeProfile.baseVirtualMachineProfile.securityProfile.uefiSettings.secureBootEnabled', $True). + Reason($LocalizedData.FleetSecureBootEnabled, $PSRule.TargetName) +} + #endregion Rules From c61a8f9a67da705e17d1e2fdd08c9eea7be22669 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Tue, 14 Jul 2026 16:14:30 +0300 Subject: [PATCH 2/7] feat(fleet): add SecureBoot localized strings Signed-off-by: Alex Chen --- src/PSRule.Rules.Azure/en/PSRule-rules.psd1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 b/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 index 890758609f7..06db5cdc5b5 100644 --- a/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 +++ b/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 @@ -79,6 +79,8 @@ ReplicaInSecondaryNotFound = "A replica in a secondary region was not found." VMSSPublicKey = "The virtual machine scale set '{0}' should have password authentication disabled." FleetPublicKey = "The Azure Fleet '{0}' should have password authentication disabled." + FleetSecureBoot = "The Azure Fleet '{0}' should set the 'securityType' property to 'TrustedLaunch' or 'ConfidentialVM'." + FleetSecureBootEnabled = "The Azure Fleet '{0}' should have Secure Boot enabled." ACRSoftDeletePolicy = "The container registry '{0}' should have soft delete policy enabled." ACRSoftDeletePolicyRetention = "The container registry '{0}' should have retention period value between one to 90 days for the soft delete policy." ContainerRegistryAuditDiagnosticSetting = "Minimum one diagnostic setting should have ({0}) configured or category group ({1}) configured." From 06757043f28d9340414386716a1285fe8f053b82 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Tue, 14 Jul 2026 16:14:32 +0300 Subject: [PATCH 3/7] test(fleet): cover Azure.Fleet.SecureBoot Signed-off-by: Alex Chen --- .../Azure.Fleet.Tests.ps1 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 b/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 index 2a73bdcdec8..5d68b649b66 100644 --- a/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 +++ b/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 @@ -56,5 +56,21 @@ Describe 'Azure.Fleet' -Tag 'Fleet' { $ruleResult.Length | Should -Be 1; $ruleResult.TargetName | Should -Be 'fleet-001'; } + + It 'Azure.Fleet.SecureBoot' { + $filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.Fleet.SecureBoot' }; + + # Fail + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 2; + $ruleResult.TargetName | Should -BeIn 'fleet-002', 'fleet-003'; + + # Pass + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 1; + $ruleResult.TargetName | Should -Be 'fleet-001'; + } } } From 2185745b21fd0674f253c3f0066c1fb5f38c0667 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Tue, 14 Jul 2026 16:14:34 +0300 Subject: [PATCH 4/7] test(fleet): set SecureBoot fixture coverage Signed-off-by: Alex Chen --- .../Resources.Fleet.json | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json b/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json index a5b25f8e378..54563756c7b 100644 --- a/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json +++ b/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json @@ -83,7 +83,12 @@ "SubscriptionId": "00000000-0000-0000-0000-000000000000", "ResourceGroupName": "test-rg", "Type": "Microsoft.AzureFleet/fleets", - "Tags": {} + "Tags": {}, + "properties": { + "computeProfile": { + "baseVirtualMachineProfile": {} + } + } }, { "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.AzureFleet/fleets/fleet-002", @@ -161,7 +166,12 @@ "SubscriptionId": "00000000-0000-0000-0000-000000000000", "ResourceGroupName": "test-rg", "Type": "Microsoft.AzureFleet/fleets", - "Tags": {} + "Tags": {}, + "properties": { + "computeProfile": { + "baseVirtualMachineProfile": {} + } + } }, { "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.AzureFleet/fleets/fleet-003", @@ -236,7 +246,12 @@ "SubscriptionId": "00000000-0000-0000-0000-000000000000", "ResourceGroupName": "test-rg", "Type": "Microsoft.AzureFleet/fleets", - "Tags": {} + "Tags": {}, + "properties": { + "computeProfile": { + "baseVirtualMachineProfile": {} + } + } }, { "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.AzureFleet/fleets/fleet-004", @@ -320,6 +335,11 @@ "SubscriptionId": "00000000-0000-0000-0000-000000000000", "ResourceGroupName": "test-rg", "Type": "Microsoft.AzureFleet/fleets", - "Tags": {} + "Tags": {}, + "properties": { + "computeProfile": { + "baseVirtualMachineProfile": {} + } + } } ] From 8d595c2c11d5c680c4147e2784611868a91d9cc9 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Tue, 14 Jul 2026 16:14:36 +0300 Subject: [PATCH 5/7] docs(fleet): document Azure.Fleet.SecureBoot Signed-off-by: Alex Chen --- docs/en/rules/Azure.Fleet.SecureBoot.md | 104 ++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/en/rules/Azure.Fleet.SecureBoot.md diff --git a/docs/en/rules/Azure.Fleet.SecureBoot.md b/docs/en/rules/Azure.Fleet.SecureBoot.md new file mode 100644 index 00000000000..c089c290761 --- /dev/null +++ b/docs/en/rules/Azure.Fleet.SecureBoot.md @@ -0,0 +1,104 @@ +--- +reviewed: 2026-07-14 +severity: Important +pillar: Security +category: SE:08 Hardening resources +resource: Azure Fleet +resourceType: Microsoft.AzureFleet/fleets +online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.Fleet.SecureBoot/ +--- + +# Azure Fleet Secure Boot is not enabled + +## SYNOPSIS + +Operating systems or drivers may be maliciously modified or injected if an actor gains access to VM/ OS storage or build media. + +## DESCRIPTION + +Azure Fleets deploy and manage virtual machines at scale using a base virtual machine profile. +A malicious actor may attempt to tamper or inject operating system and driver components to gain access to resources and persist between reboots. + +When a fleet VM is started, Azure is able to verify if: + +1. The operating system and drivers originate from a trusted source. +2. These components are in their original unaltered state. + +Azure is able to perform this verification by Secure Boot and Trusted Launch features. +These features verify the cryptographic signatures of early boot components before they start. + +Secure Boot and Trusted Launch are on by default for many configurations. +However, if you are running an older configuration these features may need to be enabled. + +## RECOMMENDATION + +Consider enabling Trusted Launch or Confidential VM with Secure Boot for Azure Fleet base virtual machine profiles to protect against boot-level attacks. + +## EXAMPLES + +### Configure with Bicep + +To deploy Azure Fleets that pass this rule: + +- Set the `properties.computeProfile.baseVirtualMachineProfile.securityProfile.securityType` property to `TrustedLaunch` or `ConfidentialVM`. +- Set the `properties.computeProfile.baseVirtualMachineProfile.securityProfile.uefiSettings.secureBootEnabled` property to `true`. + +For example: + +```bicep +resource fleet 'Microsoft.AzureFleet/fleets@2024-11-01' = { + name: name + location: location + properties: { + computeProfile: { + baseVirtualMachineProfile: { + securityProfile: { + securityType: 'TrustedLaunch' + uefiSettings: { + secureBootEnabled: true + vTpmEnabled: true + } + } + } + } + } +} +``` + +### Configure with Azure template + +To deploy Azure Fleets that pass this rule: + +- Set the `properties.computeProfile.baseVirtualMachineProfile.securityProfile.securityType` property to `TrustedLaunch` or `ConfidentialVM`. +- Set the `properties.computeProfile.baseVirtualMachineProfile.securityProfile.uefiSettings.secureBootEnabled` property to `true`. + +For example: + +```json +{ + "type": "Microsoft.AzureFleet/fleets", + "apiVersion": "2024-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "computeProfile": { + "baseVirtualMachineProfile": { + "securityProfile": { + "securityType": "TrustedLaunch", + "uefiSettings": { + "secureBootEnabled": true, + "vTpmEnabled": true + } + } + } + } + } +} +``` + +## LINKS + +- [SE:08 Hardening resources](https://learn.microsoft.com/security/benchmark/azure/security-baselines-platform#se-08-hardening-resources) +- [Trusted launch for Azure virtual machines](https://learn.microsoft.com/azure/virtual-machines/trusted-launch) +- [Security profile](https://learn.microsoft.com/azure/templates/microsoft.azurefleet/fleets#securityprofile) +- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.azurefleet/fleets) From 5210c40fcb6a42a8c180bf5108946df01bdc11c1 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Tue, 14 Jul 2026 16:15:14 +0300 Subject: [PATCH 6/7] test(fleet): put SecureBoot profile on Properties for fleet-001 Signed-off-by: Alex Chen --- .../Resources.Fleet.json | 44 +++++-------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json b/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json index 54563756c7b..c270ac4cd2f 100644 --- a/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json +++ b/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json @@ -64,6 +64,14 @@ } } ] + }, + "securityProfile": { + "securityType": "TrustedLaunch", + "encryptionAtHost": true, + "uefiSettings": { + "secureBootEnabled": true, + "vTpmEnabled": true + } } } }, @@ -83,12 +91,7 @@ "SubscriptionId": "00000000-0000-0000-0000-000000000000", "ResourceGroupName": "test-rg", "Type": "Microsoft.AzureFleet/fleets", - "Tags": {}, - "properties": { - "computeProfile": { - "baseVirtualMachineProfile": {} - } - } + "Tags": {} }, { "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.AzureFleet/fleets/fleet-002", @@ -166,12 +169,7 @@ "SubscriptionId": "00000000-0000-0000-0000-000000000000", "ResourceGroupName": "test-rg", "Type": "Microsoft.AzureFleet/fleets", - "Tags": {}, - "properties": { - "computeProfile": { - "baseVirtualMachineProfile": {} - } - } + "Tags": {} }, { "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.AzureFleet/fleets/fleet-003", @@ -246,12 +244,7 @@ "SubscriptionId": "00000000-0000-0000-0000-000000000000", "ResourceGroupName": "test-rg", "Type": "Microsoft.AzureFleet/fleets", - "Tags": {}, - "properties": { - "computeProfile": { - "baseVirtualMachineProfile": {} - } - } + "Tags": {} }, { "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.AzureFleet/fleets/fleet-004", @@ -268,14 +261,6 @@ "Properties": { "computeProfile": { "baseVirtualMachineProfile": { - "securityProfile": { - "securityType": "TrustedLaunch", - "encryptionAtHost": true, - "uefiSettings": { - "secureBootEnabled": true, - "vTpmEnabled": true - } - }, "osProfile": { "computerNamePrefix": "fleet", "adminUsername": "azureuser", @@ -335,11 +320,6 @@ "SubscriptionId": "00000000-0000-0000-0000-000000000000", "ResourceGroupName": "test-rg", "Type": "Microsoft.AzureFleet/fleets", - "Tags": {}, - "properties": { - "computeProfile": { - "baseVirtualMachineProfile": {} - } - } + "Tags": {} } ] From 88a44351251e0f40a56d141432bfc70fd79cd9ae Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Tue, 14 Jul 2026 16:15:15 +0300 Subject: [PATCH 7/7] test(fleet): expect 3 SecureBoot failures including fleet-004 Signed-off-by: Alex Chen --- tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 b/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 index 5d68b649b66..d0a9d9888ed 100644 --- a/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 +++ b/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 @@ -63,8 +63,8 @@ Describe 'Azure.Fleet' -Tag 'Fleet' { # Fail $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' }); $ruleResult | Should -Not -BeNullOrEmpty; - $ruleResult.Length | Should -Be 2; - $ruleResult.TargetName | Should -BeIn 'fleet-002', 'fleet-003'; + $ruleResult.Length | Should -Be 3; + $ruleResult.TargetName | Should -BeIn 'fleet-002', 'fleet-003', 'fleet-004'; # Pass $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });