From 073fe59403d4b58c8193e51ae1b83f339b9ad209 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:36:50 +0000 Subject: [PATCH 1/3] Initial plan From 8b831e5e088435c89740df5d5333d295bdf96733 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:56:42 +0000 Subject: [PATCH 2/3] Add pinned script dependency rules for Automation runbooks and Deployment scripts Co-authored-by: BernieWhite <13513058+BernieWhite@users.noreply.github.com> Agent-Logs-Url: https://github.com/Azure/PSRule.Rules.Azure/sessions/8f0eb337-f4fe-4a54-a839-c00b93239306 --- .ps-rule/Rule.Rule.ps1 | 2 +- docs/changelog.md | 7 ++ .../rules/Azure.Automation.RunbookPinned.md | 83 ++++++++++++++ .../en/rules/Azure.DeploymentScript.Pinned.md | 84 ++++++++++++++ src/PSRule.Rules.Azure/en/PSRule-rules.psd1 | 1 + .../rules/Azure.Automation.Rule.ps1 | 10 ++ .../rules/Azure.DeploymentScript.Rule.ps1 | 28 +++++ .../Azure.Automation.Tests.ps1 | 29 +++++ .../Azure.DeploymentScript.Tests.ps1 | 57 ++++++++++ .../Resources.Automation.Runbook.json | 67 +++++++++++ .../Resources.DeploymentScript.json | 104 ++++++++++++++++++ 11 files changed, 471 insertions(+), 1 deletion(-) create mode 100644 docs/en/rules/Azure.Automation.RunbookPinned.md create mode 100644 docs/en/rules/Azure.DeploymentScript.Pinned.md create mode 100644 src/PSRule.Rules.Azure/rules/Azure.DeploymentScript.Rule.ps1 create mode 100644 tests/PSRule.Rules.Azure.Tests/Azure.DeploymentScript.Tests.ps1 create mode 100644 tests/PSRule.Rules.Azure.Tests/Resources.Automation.Runbook.json create mode 100644 tests/PSRule.Rules.Azure.Tests/Resources.DeploymentScript.json diff --git a/.ps-rule/Rule.Rule.ps1 b/.ps-rule/Rule.Rule.ps1 index cbdfcc14175..3342bc5749c 100644 --- a/.ps-rule/Rule.Rule.ps1 +++ b/.ps-rule/Rule.Rule.ps1 @@ -30,7 +30,7 @@ Rule 'Rule.Release' -Type 'PSRule.Rules.Rule' { # Synopsis: Rules must be added to a rule set. Rule 'Rule.RuleSet' -Type 'PSRule.Rules.Rule' { Recommend 'Add a ruleSet the to the rule.' - $Assert.Match($TargetObject, 'Tag.ruleSet', '^(2020|2021|2022|2023|2024|2025)_(03|06|09|12)$') + $Assert.Match($TargetObject, 'Tag.ruleSet', '^(2020|2021|2022|2023|2024|2025|2026)_(03|06|09|12)$') } # Synopsis: Annotate rules with a valid Well-Architected Framework pillar. diff --git a/docs/changelog.md b/docs/changelog.md index 953a25e7049..63122d04df5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -30,6 +30,13 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers ## Unreleased +- New rules: + - Automation Account: + - Added `Azure.Automation.RunbookPinned` to check runbook external scripts use pinned commit hash URLs to prevent supply chain attacks. + [#3710](https://github.com/Azure/PSRule.Rules.Azure/issues/3710) + - Deployment Script: + - Added `Azure.DeploymentScript.Pinned` to check deployment script external script URIs use pinned commit hash URLs to prevent supply chain attacks. + [#3710](https://github.com/Azure/PSRule.Rules.Azure/issues/3710) - Updated rules: - Azure Kubernetes Service: - Updated `Azure.AKS.Version` to use `1.33.7` as the minimum version by @BernieWhite. diff --git a/docs/en/rules/Azure.Automation.RunbookPinned.md b/docs/en/rules/Azure.Automation.RunbookPinned.md new file mode 100644 index 00000000000..90bcf1a57c8 --- /dev/null +++ b/docs/en/rules/Azure.Automation.RunbookPinned.md @@ -0,0 +1,83 @@ +--- +reviewed: 2026-03-25 +severity: Important +pillar: Security +category: SE:02 Secured development lifecycle +resource: Automation Account +resourceType: Microsoft.Automation/automationAccounts/runbooks +online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.Automation.RunbookPinned/ +--- + +# Automation runbook is not pinned + +## SYNOPSIS + +Runbooks that use external scripts from an unpinned URL may be modified to execute malicious code. + +## DESCRIPTION + +When an Azure Automation runbook uses an external script from a URL, the script content could change between runs. +If the URL is not pinned to a specific commit, a supply chain attack could modify the script and execute malicious code with elevated privileges. + +When using scripts from GitHub, a URL should be pinned to a specific commit hash rather than a branch or tag. +A branch or tag can be modified to point to a different commit, allowing a malicious actor to modify the script. +A commit hash is unique and cannot be changed without creating a new commit. + +## RECOMMENDATION + +Consider updating the runbook to use a URL pinned to a specific commit hash. + +## EXAMPLES + +### Configure with Bicep + +To deploy automation runbooks that pass this rule: + +- Set the `properties.publishContentLink.uri` property to a URL that is pinned to a specific commit hash. + - For GitHub hosted scripts, use `https://raw.githubusercontent.com/{owner}/{repo}/{commit-sha}/{path}`. + +For example: + +```bicep +resource runbook 'Microsoft.Automation/automationAccounts/runbooks@2023-11-01' = { + parent: automationAccount + name: 'runbook-001' + location: location + properties: { + runbookType: 'PowerShell' + publishContentLink: { + uri: 'https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/pipeline-deps.ps1' + } + } +} +``` + +### Configure with Azure template + +To deploy automation runbooks that pass this rule: + +- Set the `properties.publishContentLink.uri` property to a URL that is pinned to a specific commit hash. + - For GitHub hosted scripts, use `https://raw.githubusercontent.com/{owner}/{repo}/{commit-sha}/{path}`. + +For example: + +```json +{ + "type": "Microsoft.Automation/automationAccounts/runbooks", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('automationAccountName'), 'runbook-001')]", + "location": "[parameters('location')]", + "properties": { + "runbookType": "PowerShell", + "publishContentLink": { + "uri": "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/pipeline-deps.ps1" + } + } +} +``` + +## LINKS + +- [SE:02 Secured development lifecycle](https://learn.microsoft.com/azure/well-architected/security/secure-development-lifecycle) +- [Manage runbooks in Azure Automation](https://learn.microsoft.com/azure/automation/manage-runbooks) +- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.automation/automationaccounts/runbooks) diff --git a/docs/en/rules/Azure.DeploymentScript.Pinned.md b/docs/en/rules/Azure.DeploymentScript.Pinned.md new file mode 100644 index 00000000000..316899b3517 --- /dev/null +++ b/docs/en/rules/Azure.DeploymentScript.Pinned.md @@ -0,0 +1,84 @@ +--- +reviewed: 2026-03-25 +severity: Important +pillar: Security +category: SE:02 Secured development lifecycle +resource: Deployment Script +resourceType: Microsoft.Resources/deploymentScripts +online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.DeploymentScript.Pinned/ +--- + +# Deployment script is not pinned + +## SYNOPSIS + +Deployment scripts that use external scripts from an unpinned URL may be modified to execute malicious code. + +## DESCRIPTION + +When an Azure Deployment Script uses an external script from a URL, the script content could change between runs. +If the URL is not pinned to a specific commit, a supply chain attack could modify the script and execute malicious code with elevated privileges. + +When using scripts from GitHub, a URL should be pinned to a specific commit hash rather than a branch or tag. +A branch or tag can be modified to point to a different commit, allowing a malicious actor to modify the script. +A commit hash is unique and cannot be changed without creating a new commit. + +## RECOMMENDATION + +Consider updating the deployment script to use a URL pinned to a specific commit hash. + +## EXAMPLES + +### Configure with Bicep + +To deploy deployment scripts that pass this rule: + +- Set the `properties.primaryScriptUri` property to a URL that is pinned to a specific commit hash. + - For GitHub hosted scripts, use `https://raw.githubusercontent.com/{owner}/{repo}/{commit-sha}/{path}`. +- For each item in `properties.supportingScriptUris`, use a URL that is pinned to a specific commit hash. + +For example: + +```bicep +resource script 'Microsoft.Resources/deploymentScripts@2023-08-01' = { + name: 'script-001' + location: location + kind: 'AzurePowerShell' + properties: { + azPowerShellVersion: '9.7' + retentionInterval: 'P1D' + primaryScriptUri: 'https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/pipeline-deps.ps1' + } +} +``` + +### Configure with Azure template + +To deploy deployment scripts that pass this rule: + +- Set the `properties.primaryScriptUri` property to a URL that is pinned to a specific commit hash. + - For GitHub hosted scripts, use `https://raw.githubusercontent.com/{owner}/{repo}/{commit-sha}/{path}`. +- For each item in `properties.supportingScriptUris`, use a URL that is pinned to a specific commit hash. + +For example: + +```json +{ + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "script-001", + "location": "[parameters('location')]", + "kind": "AzurePowerShell", + "properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "primaryScriptUri": "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/pipeline-deps.ps1" + } +} +``` + +## LINKS + +- [SE:02 Secured development lifecycle](https://learn.microsoft.com/azure/well-architected/security/secure-development-lifecycle) +- [Use deployment scripts in ARM templates](https://learn.microsoft.com/azure/azure-resource-manager/templates/deployment-script-template) +- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.resources/deploymentscripts) diff --git a/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 b/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 index e8781197c22..1fef72faf35 100644 --- a/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 +++ b/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 @@ -61,6 +61,7 @@ ERAvailabilityZoneSKU = "The ExpressRoute gateway ({0}) should be using one of the following AZ SKUs ({1})." AutomationAccountDiagnosticSetting = "The diagnostic setting ({0}) should enable ({1}) or category group ({2})." AutomationAccountAuditDiagnosticSetting = "Minimum one diagnostic setting should have ({0}) configured or category group ({1}) configured." + GitHubRawScriptUnpinned = "The script URL '{0}' is not pinned to a specific commit hash." TemplateResourceWithoutComment = "The template ({0}) has ({1}) resource/s without comments." TemplateResourceWithoutDescription = "The template ({0}) has ({1}) resource/s without descriptions." PremiumRedisCacheAvailabilityZone = "The premium redis cache ({0}) deployed to region ({1}) should use a minimum of two availability zones from the following [{2}]." diff --git a/src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 b/src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 index 9b3b1e170dd..e0145576d06 100644 --- a/src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 +++ b/src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 @@ -129,3 +129,13 @@ Rule 'Azure.Automation.PlatformLogs' -Ref 'AZR-000089' -Type 'Microsoft.Automati 'AllMetrics' ) } + +# Synopsis: Automation runbook scripts should use a pinned URL to prevent supply chain attacks. +Rule 'Azure.Automation.RunbookPinned' -Ref 'AZR-000535' -Type 'Microsoft.Automation/automationAccounts/runbooks' -Tag @{ release = 'GA'; ruleSet = '2026_06'; 'Azure.WAF/pillar' = 'Security'; } { + $pinnedPattern = '^https://raw\.githubusercontent\.com/[^/]+/[^/]+/[0-9a-f]{40}/'; + $uri = $TargetObject.properties.publishContentLink.uri; + if ($Null -eq $uri -or $uri -notLike 'https://raw.githubusercontent.com/*') { + return $Assert.Pass(); + } + $Assert.Create($uri -match $pinnedPattern, $LocalizedData.GitHubRawScriptUnpinned, $uri); +} diff --git a/src/PSRule.Rules.Azure/rules/Azure.DeploymentScript.Rule.ps1 b/src/PSRule.Rules.Azure/rules/Azure.DeploymentScript.Rule.ps1 new file mode 100644 index 00000000000..deae5c01d55 --- /dev/null +++ b/src/PSRule.Rules.Azure/rules/Azure.DeploymentScript.Rule.ps1 @@ -0,0 +1,28 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# +# Validation rules for Azure Deployment Scripts +# + +# Synopsis: Deployment scripts should use a pinned URL to prevent supply chain attacks. +Rule 'Azure.DeploymentScript.Pinned' -Ref 'AZR-000536' -Type 'Microsoft.Resources/deploymentScripts' -Tag @{ release = 'GA'; ruleSet = '2026_06'; 'Azure.WAF/pillar' = 'Security'; } { + $pinnedPattern = '^https://raw\.githubusercontent\.com/[^/]+/[^/]+/[0-9a-f]{40}/'; + + $uris = @(); + if ($Null -ne $TargetObject.properties.primaryScriptUri) { + $uris += $TargetObject.properties.primaryScriptUri; + } + if ($Null -ne $TargetObject.properties.supportingScriptUris) { + $uris += @($TargetObject.properties.supportingScriptUris); + } + + $gitHubUris = @($uris | Where-Object { $_ -like 'https://raw.githubusercontent.com/*' }); + if ($gitHubUris.Length -eq 0) { + return $Assert.Pass(); + } + + foreach ($uri in $gitHubUris) { + $Assert.Create($uri -match $pinnedPattern, $LocalizedData.GitHubRawScriptUnpinned, $uri); + } +} diff --git a/tests/PSRule.Rules.Azure.Tests/Azure.Automation.Tests.ps1 b/tests/PSRule.Rules.Azure.Tests/Azure.Automation.Tests.ps1 index 1ac5c89036e..fb07e9c2be6 100644 --- a/tests/PSRule.Rules.Azure.Tests/Azure.Automation.Tests.ps1 +++ b/tests/PSRule.Rules.Azure.Tests/Azure.Automation.Tests.ps1 @@ -134,6 +134,35 @@ Describe 'Azure.Automation' -Tag Automation { } } + Context 'Runbook conditions' { + BeforeAll { + $invokeParams = @{ + Baseline = 'Azure.All' + Module = 'PSRule.Rules.Azure' + WarningAction = 'Ignore' + ErrorAction = 'Stop' + } + $dataPath = Join-Path -Path $here -ChildPath 'Resources.Automation.Runbook.json'; + $result = Invoke-PSRule @invokeParams -InputPath $dataPath -Outcome All; + } + + It 'Azure.Automation.RunbookPinned' { + $filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.Automation.RunbookPinned' }; + + # Fail + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 1; + $ruleResult.TargetName | Should -Be 'runbook-C'; + + # Pass + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 3; + $ruleResult.TargetName | Should -BeIn 'runbook-A', 'runbook-B', 'runbook-D'; + } + } + Context 'With Configuration Option' { BeforeAll { $invokeParams = @{ diff --git a/tests/PSRule.Rules.Azure.Tests/Azure.DeploymentScript.Tests.ps1 b/tests/PSRule.Rules.Azure.Tests/Azure.DeploymentScript.Tests.ps1 new file mode 100644 index 00000000000..9e89c6f5b35 --- /dev/null +++ b/tests/PSRule.Rules.Azure.Tests/Azure.DeploymentScript.Tests.ps1 @@ -0,0 +1,57 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# +# Unit tests for Azure Deployment Script rules +# + +[CmdletBinding()] +param ( + +) + +BeforeAll { + # Setup error handling + $ErrorActionPreference = 'Stop'; + Set-StrictMode -Version latest; + + if ($Env:SYSTEM_DEBUG -eq 'true') { + $VerbosePreference = 'Continue'; + } + + # Setup tests paths + $rootPath = $PWD; + Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSRule.Rules.Azure) -Force; + $here = (Resolve-Path $PSScriptRoot).Path; +} + +Describe 'Azure.DeploymentScript' -Tag 'DeploymentScript' { + Context 'Conditions' { + BeforeAll { + $invokeParams = @{ + Baseline = 'Azure.All' + Module = 'PSRule.Rules.Azure' + WarningAction = 'Ignore' + ErrorAction = 'Stop' + } + $dataPath = Join-Path -Path $here -ChildPath 'Resources.DeploymentScript.json'; + $result = Invoke-PSRule @invokeParams -InputPath $dataPath -Outcome All; + } + + It 'Azure.DeploymentScript.Pinned' { + $filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.DeploymentScript.Pinned' }; + + # Fail + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 2; + $ruleResult.TargetName | Should -BeIn 'script-C', 'script-E'; + + # Pass + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 4; + $ruleResult.TargetName | Should -BeIn 'script-A', 'script-B', 'script-D', 'script-F'; + } + } +} diff --git a/tests/PSRule.Rules.Azure.Tests/Resources.Automation.Runbook.json b/tests/PSRule.Rules.Azure.Tests/Resources.Automation.Runbook.json new file mode 100644 index 00000000000..db1c0646d73 --- /dev/null +++ b/tests/PSRule.Rules.Azure.Tests/Resources.Automation.Runbook.json @@ -0,0 +1,67 @@ +[ + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Automation/automationAccounts/automation-A/runbooks/runbook-A", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Automation/automationAccounts/automation-A/runbooks/runbook-A", + "ResourceName": "runbook-A", + "Name": "runbook-A", + "Properties": { + "runbookType": "PowerShell", + "state": "Published" + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Automation/automationAccounts/runbooks", + "ResourceType": "Microsoft.Automation/automationAccounts/runbooks", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Automation/automationAccounts/automation-A/runbooks/runbook-B", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Automation/automationAccounts/automation-A/runbooks/runbook-B", + "ResourceName": "runbook-B", + "Name": "runbook-B", + "Properties": { + "runbookType": "PowerShell", + "publishContentLink": { + "uri": "https://contoso.com/scripts/runbook.ps1" + }, + "state": "Published" + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Automation/automationAccounts/runbooks", + "ResourceType": "Microsoft.Automation/automationAccounts/runbooks", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Automation/automationAccounts/automation-A/runbooks/runbook-C", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Automation/automationAccounts/automation-A/runbooks/runbook-C", + "ResourceName": "runbook-C", + "Name": "runbook-C", + "Properties": { + "runbookType": "PowerShell", + "publishContentLink": { + "uri": "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/refs/heads/main/scripts/pipeline-deps.ps1" + }, + "state": "Published" + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Automation/automationAccounts/runbooks", + "ResourceType": "Microsoft.Automation/automationAccounts/runbooks", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Automation/automationAccounts/automation-A/runbooks/runbook-D", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Automation/automationAccounts/automation-A/runbooks/runbook-D", + "ResourceName": "runbook-D", + "Name": "runbook-D", + "Properties": { + "runbookType": "PowerShell", + "publishContentLink": { + "uri": "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/pipeline-deps.ps1" + }, + "state": "Published" + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Automation/automationAccounts/runbooks", + "ResourceType": "Microsoft.Automation/automationAccounts/runbooks", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + } +] diff --git a/tests/PSRule.Rules.Azure.Tests/Resources.DeploymentScript.json b/tests/PSRule.Rules.Azure.Tests/Resources.DeploymentScript.json new file mode 100644 index 00000000000..350a6bc9ef9 --- /dev/null +++ b/tests/PSRule.Rules.Azure.Tests/Resources.DeploymentScript.json @@ -0,0 +1,104 @@ +[ + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-A", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-A", + "ResourceName": "script-A", + "Name": "script-A", + "Kind": "AzurePowerShell", + "Properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "scriptContent": "Write-Host 'Hello World'" + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Resources/deploymentScripts", + "ResourceType": "Microsoft.Resources/deploymentScripts", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-B", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-B", + "ResourceName": "script-B", + "Name": "script-B", + "Kind": "AzurePowerShell", + "Properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "primaryScriptUri": "https://contoso.com/scripts/deploy.ps1" + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Resources/deploymentScripts", + "ResourceType": "Microsoft.Resources/deploymentScripts", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-C", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-C", + "ResourceName": "script-C", + "Name": "script-C", + "Kind": "AzurePowerShell", + "Properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "primaryScriptUri": "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/refs/heads/main/scripts/pipeline-deps.ps1" + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Resources/deploymentScripts", + "ResourceType": "Microsoft.Resources/deploymentScripts", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-D", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-D", + "ResourceName": "script-D", + "Name": "script-D", + "Kind": "AzurePowerShell", + "Properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "primaryScriptUri": "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/pipeline-deps.ps1" + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Resources/deploymentScripts", + "ResourceType": "Microsoft.Resources/deploymentScripts", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-E", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-E", + "ResourceName": "script-E", + "Name": "script-E", + "Kind": "AzurePowerShell", + "Properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "primaryScriptUri": "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/pipeline-deps.ps1", + "supportingScriptUris": [ + "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/refs/heads/main/scripts/helper.ps1" + ] + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Resources/deploymentScripts", + "ResourceType": "Microsoft.Resources/deploymentScripts", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-F", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Resources/deploymentScripts/script-F", + "ResourceName": "script-F", + "Name": "script-F", + "Kind": "AzurePowerShell", + "Properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "primaryScriptUri": "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/pipeline-deps.ps1", + "supportingScriptUris": [ + "https://raw.githubusercontent.com/Azure/PSRule.Rules.Azure/8dc395b739a8be00571d039c0af9df88d85c1e2a/scripts/helper.ps1" + ] + }, + "ResourceGroupName": "rg-test", + "Type": "Microsoft.Resources/deploymentScripts", + "ResourceType": "Microsoft.Resources/deploymentScripts", + "SubscriptionId": "00000000-0000-0000-0000-000000000000" + } +] From e6fe855b4bd66a9da8f40656b955c71a343390ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 04:24:25 +0000 Subject: [PATCH 3/3] Update Azure.Automation.RunbookPinned rule ID to AZR-000543 Agent-Logs-Url: https://github.com/Azure/PSRule.Rules.Azure/sessions/0101cd06-ba50-4dbd-8fa8-ddd9f31e8313 Co-authored-by: BernieWhite <13513058+BernieWhite@users.noreply.github.com> --- src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 b/src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 index e0145576d06..62653e81d9f 100644 --- a/src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 +++ b/src/PSRule.Rules.Azure/rules/Azure.Automation.Rule.ps1 @@ -131,7 +131,7 @@ Rule 'Azure.Automation.PlatformLogs' -Ref 'AZR-000089' -Type 'Microsoft.Automati } # Synopsis: Automation runbook scripts should use a pinned URL to prevent supply chain attacks. -Rule 'Azure.Automation.RunbookPinned' -Ref 'AZR-000535' -Type 'Microsoft.Automation/automationAccounts/runbooks' -Tag @{ release = 'GA'; ruleSet = '2026_06'; 'Azure.WAF/pillar' = 'Security'; } { +Rule 'Azure.Automation.RunbookPinned' -Ref 'AZR-000543' -Type 'Microsoft.Automation/automationAccounts/runbooks' -Tag @{ release = 'GA'; ruleSet = '2026_06'; 'Azure.WAF/pillar' = 'Security'; } { $pinnedPattern = '^https://raw\.githubusercontent\.com/[^/]+/[^/]+/[0-9a-f]{40}/'; $uri = $TargetObject.properties.publishContentLink.uri; if ($Null -eq $uri -or $uri -notLike 'https://raw.githubusercontent.com/*') {