From 1f4a9260265f0dc57753c0c3b6b10bb0085c6428 Mon Sep 17 00:00:00 2001 From: Mathieu <39670015+mathieusouflis@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:54:23 +0100 Subject: [PATCH] feat(file-shares): Add rule to disable public network access --- docs/en/rules/Azure.Share.PublicAccess.md | 85 +++++++ .../rules/Azure.Share.Rule.yaml | 29 +++ .../Azure.Share.Rule.Tests.ps1 | 60 +++++ .../Resources.Share.json | 227 ++++++++++++++++++ 4 files changed, 401 insertions(+) create mode 100644 docs/en/rules/Azure.Share.PublicAccess.md create mode 100644 src/PSRule.Rules.Azure/rules/Azure.Share.Rule.yaml create mode 100644 tests/PSRule.Rules.Azure.Tests/Azure.Share.Rule.Tests.ps1 create mode 100644 tests/PSRule.Rules.Azure.Tests/Resources.Share.json diff --git a/docs/en/rules/Azure.Share.PublicAccess.md b/docs/en/rules/Azure.Share.PublicAccess.md new file mode 100644 index 0000000000..e37d5bdfdd --- /dev/null +++ b/docs/en/rules/Azure.Share.PublicAccess.md @@ -0,0 +1,85 @@ +--- +reviewed: 2025-11-04 +severity: Critical +pillar: Security +category: SE:06 Network controls +resource: File Shares +resourceType: Microsoft.Kusto/clusters +online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.Share.PublicAccess/ +--- + +# Disable public network access on File Shares clusters + +## SYNOPSIS + +Azure File Shares (Shares) clusters should have public network access disabled. + +## DESCRIPTION + +Disabling public network access improves security by ensuring that the cluster isn't exposed on the public internet. +You can control exposure of your clusters by creating private endpoints instead. + +## RECOMMENDATION + +Consider disabling public network access on Azure File Shares clusters, using private endpoints to control connectivity. + +## EXAMPLES + +### Configure with Azure template + +To deploy File Shares clusters that pass this rule: + +- Set the `properties.publicNetworkAccess` property to `Disabled`. + +For example: + +```json +{ + "type": "Microsoft.Kusto/clusters", + "apiVersion": "2024-04-13", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard_D11_v2", + "tier": "Standard" + }, + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "publicNetworkAccess": "Disabled" + } +} +``` + +### Configure with Bicep + +To deploy File Shares clusters that pass this rule: + +- Set the `properties.publicNetworkAccess` property to `Disabled`. + +For example: + +```bicep +resource adx 'Microsoft.Kusto/clusters@2024-04-13' = { + name: name + location: location + sku: { + name: 'Standard_D11_v2' + tier: 'Standard' + } + identity: { + type: 'SystemAssigned' + } + properties: { + enableDiskEncryption: true + publicNetworkAccess: 'Disabled' + } +} +``` + +## LINKS + +- [SE:06 Network controls](https://learn.microsoft.com/azure/well-architected/security/networking) +- [Security: Level 4](https://learn.microsoft.com/azure/well-architected/security/maturity-model?tabs=level4) +- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.kusto/clusters) diff --git a/src/PSRule.Rules.Azure/rules/Azure.Share.Rule.yaml b/src/PSRule.Rules.Azure/rules/Azure.Share.Rule.yaml new file mode 100644 index 0000000000..634640fc52 --- /dev/null +++ b/src/PSRule.Rules.Azure/rules/Azure.Share.Rule.yaml @@ -0,0 +1,29 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# +# Validation rules for FileShares +# + +#region Rules + +--- +# Synopsis: Disable export of artifacts from container registries. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Rule +metadata: + name: Azure.Share.PublicAccess + ref: AZR-000538 + tags: + release: preview + ruleSet: 2026_03 + Azure.WAF/pillar: Security + +spec: + type: + - Microsoft.Kusto/clusters + condition: + allOf: + - field: properties.publicNetworkAccess + equals: Disabled +#endregion Rules diff --git a/tests/PSRule.Rules.Azure.Tests/Azure.Share.Rule.Tests.ps1 b/tests/PSRule.Rules.Azure.Tests/Azure.Share.Rule.Tests.ps1 new file mode 100644 index 0000000000..7a96f1dd80 --- /dev/null +++ b/tests/PSRule.Rules.Azure.Tests/Azure.Share.Rule.Tests.ps1 @@ -0,0 +1,60 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# +# Unit tests for Azure File Shares 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.Share' -Tag 'Share' { + Context 'Conditions' { + BeforeAll { + $invokeParams = @{ + Baseline = 'Azure.All' + Module = 'PSRule.Rules.Azure' + WarningAction = 'Ignore' + ErrorAction = 'Stop' + Outcome = 'All' + } + $dataPath = Join-Path -Path $here -ChildPath 'Resources.Share.json'; + $result = Invoke-PSRule @invokeParams -InputPath $dataPath; + } + + + It 'Azure.Share.PublicAccess' { + $filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.Share.PublicAccess' }; + + # Fail + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 2; + $ruleResult.TargetName | Should -Be 'cluster-A', 'cluster-B'; + + $ruleResult.Detail.Reason.Path | Should -BeIn 'properties.publicNetworkAccess'; + + # Pass + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 1; + $ruleResult.TargetName | Should -BeIn 'cluster-C'; + } + } +} diff --git a/tests/PSRule.Rules.Azure.Tests/Resources.Share.json b/tests/PSRule.Rules.Azure.Tests/Resources.Share.json new file mode 100644 index 0000000000..72d19ec5f0 --- /dev/null +++ b/tests/PSRule.Rules.Azure.Tests/Resources.Share.json @@ -0,0 +1,227 @@ +[ + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-A", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-A", + "Kind": null, + "Location": "region", + "ManagedBy": null, + "ResourceName": "cluster-A", + "Name": "cluster-A", + "ExtensionResourceName": null, + "ParentResource": null, + "Plan": null, + "Properties": { + "state": "Running", + "stateReason": null, + "uri": "https://cluster-A.region.kusto.windows.net", + "dataIngestionUri": "https://ingest-cluster-A.region.kusto.windows.net", + "trustedExternalTenants": [ + { + "value": "*" + } + ], + "virtualNetworkConfiguration": null, + "optimizedAutoscale": null, + "enableDiskEncryption": false, + "enableStreamingIngest": true, + "keyVaultProperties": null, + "languageExtensions": { + "value": [] + }, + "enablePurge": null, + "enableDoubleEncryption": false, + "engineType": "V3", + "acceptedAudiences": [], + "restrictOutboundNetworkAccess": "Disabled", + "allowedFqdnList": [], + "publicNetworkAccess": "Enabled", + "allowedIpRangeList": [], + "enableAutoStop": false, + "provisioningState": "Succeeded" + }, + "ResourceGroupName": "test-rg", + "Type": "Microsoft.Kusto/Clusters", + "ResourceType": "Microsoft.Kusto/Clusters", + "ExtensionResourceType": null, + "Sku": { + "name": "Dev(No SLA)_Standard_D11_v2", + "tier": "Basic", + "capacity": 1 + }, + "Tags": null, + "SubscriptionId": "00000000-0000-0000-0000-000000000000", + "CreatedTime": null, + "ChangedTime": null, + "resources": [ + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-A/Databases/db-A", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-A/Databases/db-A", + "Identity": null, + "Kind": "ReadWrite", + "Location": "region", + "ManagedBy": null, + "ResourceName": "cluster-A/db-A", + "Name": "cluster-A/db-A", + "ExtensionResourceName": null, + "ParentResource": null, + "Plan": null, + "Properties": { + "softDeletePeriod": "P365D", + "hotCachePeriod": "P90D", + "statistics": { + "size": 5000.0 + }, + "isFollowed": false, + "provisioningState": "Succeeded" + }, + "ResourceGroupName": "test-rg", + "Type": "Microsoft.Kusto/Clusters/Databases", + "ResourceType": "Microsoft.Kusto/Clusters/Databases", + "ExtensionResourceType": null, + "Sku": null, + "Tags": null, + "TagsTable": null, + "SubscriptionId": "00000000-0000-0000-0000-000000000000", + "CreatedTime": null, + "ChangedTime": null, + "ETag": null + } + ] + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-B", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-B", + "Identity": { + "PrincipalId": "00000000-0000-0000-0000-000000000000", + "TenantId": "00000000-0000-0000-0000-000000000000", + "Type": "SystemAssigned", + "UserAssignedIdentities": null + }, + "Kind": null, + "Location": "region", + "ManagedBy": null, + "ResourceName": "cluster-B", + "Name": "cluster-B", + "ExtensionResourceName": null, + "ParentResource": null, + "Plan": null, + "Properties": { + "state": "Stopped" + }, + "ResourceGroupName": "test-rg", + "Type": "Microsoft.Kusto/Clusters", + "ResourceType": "Microsoft.Kusto/Clusters", + "ExtensionResourceType": null, + "Sku": { + "Name": "Standard_D11_v2", + "Tier": "Standard", + "Size": null, + "Family": null, + "Model": null, + "Capacity": 2 + }, + "Tags": null, + "SubscriptionId": "00000000-0000-0000-0000-000000000000", + "CreatedTime": null, + "ChangedTime": null, + "resources": [ + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-B/Databases/db-A", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-B/Databases/db-A", + "Identity": null, + "Kind": "ReadWrite", + "Location": "region", + "ManagedBy": null, + "ResourceName": "cluster-B/db-A", + "Name": "cluster-B/db-A", + "ExtensionResourceName": null, + "ParentResource": null, + "Plan": null, + "Properties": { + "softDeletePeriod": "P365D", + "hotCachePeriod": "P90D", + "statistics": { + "size": 5000.0 + }, + "isFollowed": false, + "provisioningState": "Succeeded" + }, + "ResourceGroupName": "test-rg", + "Type": "Microsoft.Kusto/Clusters/Databases", + "ResourceType": "Microsoft.Kusto/Clusters/Databases", + "ExtensionResourceType": null, + "Sku": null, + "Tags": null, + "TagsTable": null, + "SubscriptionId": "00000000-0000-0000-0000-000000000000", + "CreatedTime": null, + "ChangedTime": null, + "ETag": null + } + ] + }, + { + "ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-C", + "Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Kusto/Clusters/cluster-C", + "Identity": { + "PrincipalId": "00000000-0000-0000-0000-000000000000", + "TenantId": "00000000-0000-0000-0000-000000000000", + "Type": "SystemAssigned", + "UserAssignedIdentities": null + }, + "Kind": null, + "Location": "region", + "ManagedBy": null, + "ResourceName": "cluster-C", + "Name": "cluster-C", + "ExtensionResourceName": null, + "ParentResource": null, + "Plan": null, + "Properties": { + "state": "Running", + "stateReason": null, + "uri": "https://cluster-C.region.kusto.windows.net", + "dataIngestionUri": "https://ingest-cluster-C.region.kusto.windows.net", + "trustedExternalTenants": [ + { + "value": "*" + } + ], + "virtualNetworkConfiguration": null, + "optimizedAutoscale": null, + "enableDiskEncryption": true, + "enableStreamingIngest": true, + "keyVaultProperties": null, + "languageExtensions": { + "value": [] + }, + "enablePurge": null, + "enableDoubleEncryption": false, + "engineType": "V3", + "acceptedAudiences": [], + "restrictOutboundNetworkAccess": "Disabled", + "allowedFqdnList": [], + "publicNetworkAccess": "Disabled", + "allowedIpRangeList": [], + "enableAutoStop": false, + "provisioningState": "Succeeded" + }, + "ResourceGroupName": "test-rg", + "Type": "Microsoft.Kusto/Clusters", + "ResourceType": "Microsoft.Kusto/Clusters", + "ExtensionResourceType": null, + "Sku": { + "Name": "Standard_D11_v2", + "Tier": "Standard", + "Size": null, + "Family": null, + "Model": null, + "Capacity": 2 + }, + "Tags": null, + "SubscriptionId": "00000000-0000-0000-0000-000000000000", + "CreatedTime": null, + "ChangedTime": null, + "resources": [] + } +]