tests/Storage.Module.Tests.ps1
|
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } #Requires -Modules ImportExcel <# .SYNOPSIS Pester tests for Storage inventory modules (NetApp, StorageAccounts). #> $StoragePath = Join-Path (Split-Path -Parent $PSScriptRoot) 'Modules' 'Public' 'InventoryModules' 'Storage' $StorageModules = @( @{ Name = 'NetApp'; File = 'NetApp.ps1'; Type = 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes'; Worksheet = 'NetApp' } @{ Name = 'StorageAccounts'; File = 'StorageAccounts.ps1'; Type = 'microsoft.storage/storageaccounts'; Worksheet = 'Storage Accounts' } ) BeforeAll { $script:ModuleRoot = Split-Path -Parent $PSScriptRoot $script:StoragePath = Join-Path $script:ModuleRoot 'Modules' 'Public' 'InventoryModules' 'Storage' $script:TempDir = Join-Path $env:TEMP 'AZSC_StorageTests' if (Test-Path $script:TempDir) { Remove-Item $script:TempDir -Recurse -Force } New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null # Mock Az Storage cmdlets that StorageAccounts.ps1 calls function Get-AzStorageBlobServiceProperty { param([string]$ResourceGroupName, [string]$StorageAccountName) [PSCustomObject]@{ DeleteRetentionPolicy = [PSCustomObject]@{ Enabled = $true; Days = 7 } containerDeleteRetentionPolicy = [PSCustomObject]@{ Enabled = $true; Days = 7 } } } function Get-AzStorageFileServiceProperty { param([string]$ResourceGroupName, [string]$StorageAccountName) [PSCustomObject]@{ ShareDeleteRetentionPolicy = [PSCustomObject]@{ Enabled = $true; Days = 7 } } } $script:MockResources = @() # --- NetApp Volume --- $netAppRes = [PSCustomObject]@{ id = '/subscriptions/sub-00000001/resourceGroups/rg-storage/providers/Microsoft.NetApp/netAppAccounts/na-prod/capacityPools/pool1/volumes/vol1' NAME = 'na-prod/pool1/vol1' TYPE = 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes' KIND = '' LOCATION = 'eastus' RESOURCEGROUP = 'rg-storage' subscriptionId = 'sub-00000001' tags = [PSCustomObject]@{ env = 'prod' } PROPERTIES = [PSCustomObject]@{ subnetId = '/subscriptions/sub-00000001/resourceGroups/rg-net/providers/Microsoft.Network/virtualNetworks/vnet-netapp/subnets/anf-subnet' serviceLevel = 'Premium' usageThreshold = 1099511627776 # 1 TB protocolTypes = @('NFSv3') throughputMibps = 64 exportPolicy = [PSCustomObject]@{ rules = @([PSCustomObject]@{ruleIndex=1}) } networkFeatures = 'Standard' securityStyle = 'unix' smbEncryption = $false unixPermissions = '0770' coolAccess = $false avsDataStore = 'Disabled' ldapEnabled = $false } } $script:MockResources += $netAppRes # --- Storage Account --- $saResource = [PSCustomObject]@{ id = '/subscriptions/sub-00000001/resourceGroups/rg-storage/providers/microsoft.storage/storageaccounts/saprod01' NAME = 'saprod01' TYPE = 'microsoft.storage/storageaccounts' KIND = 'StorageV2' LOCATION = 'eastus' RESOURCEGROUP = 'rg-storage' subscriptionId = 'sub-00000001' tags = [PSCustomObject]@{ env = 'prod' } SKU = [PSCustomObject]@{ name = 'Standard_LRS'; tier = 'Standard' } ZONES = @('1') PROPERTIES = [PSCustomObject]@{ creationTime = '2025-01-10T08:00:00Z' supportsHttpsTrafficOnly = $true allowBlobPublicAccess = $false minimumTlsVersion = 'TLS1_2' allowsharedkeyaccess = $true isSftpEnabled = $false ishnsenabled = $false isnfsv3enabled = $false largeFileSharesState = 'Disabled' allowCrossTenantReplication = $false encryption = [PSCustomObject]@{ requireInfrastructureEncryption = $true } azureFilesIdentityBasedAuthentication = [PSCustomObject]@{ directoryServiceOptions = 'None' } networkacls = [PSCustomObject]@{ defaultaction = 'Allow' virtualnetworkrules = @([PSCustomObject]@{ id = '/subscriptions/sub-00000001/resourceGroups/rg-net/providers/Microsoft.Network/virtualNetworks/vnet-sa/subnets/sa-subnet' }) iprules = @([PSCustomObject]@{ value = '10.0.0.1' }) bypass = 'AzureServices' resourceaccessrules = @([PSCustomObject]@{ resourceid = '/subscriptions/sub-00000001/resourceGroups/rg-other/providers/Microsoft.Synapse/workspaces/syn-prod' }) } publicNetworkAccess = 'Enabled' privateEndpointConnections = @([PSCustomObject]@{ properties = [PSCustomObject]@{ privateendpoint = [PSCustomObject]@{ id = '/subscriptions/sub-00000001/resourceGroups/rg-storage/providers/Microsoft.Network/privateEndpoints/pe-sa' } } }) accessTier = 'Hot' primaryLocation = 'eastus' statusOfPrimary = 'available' secondaryLocation = $null statusofsecondary = $null } } $saResource | Add-Member -NotePropertyName 'sku' -NotePropertyValue $saResource.SKU -Force $saResource | Add-Member -NotePropertyName 'kind' -NotePropertyValue $saResource.KIND -Force $script:MockResources += $saResource } AfterAll { if (Test-Path $script:TempDir) { Remove-Item $script:TempDir -Recurse -Force } } Describe 'Storage Module Files Exist' { It 'Storage module folder exists' { $script:StoragePath | Should -Exist } It '<Name> module file exists' -ForEach $StorageModules { Join-Path $script:StoragePath $File | Should -Exist } } Describe 'Storage Module Processing Phase — <Name>' -ForEach $StorageModules { BeforeAll { $script:ModFile = Join-Path $script:StoragePath $File $script:ResType = $Type } It 'Processing returns results when matching resources are present' { $matchedResources = $script:MockResources | Where-Object { $_.TYPE -eq $script:ResType } if ($matchedResources) { $content = Get-Content -Path $script:ModFile -Raw $sb = [ScriptBlock]::Create($content) $result = Invoke-Command -ScriptBlock $sb -ArgumentList $null, $null, $null, $script:MockResources, $null, 'Processing', $null, $null, 'Light20', $null $result | Should -Not -BeNullOrEmpty } else { Set-ItResult -Skipped -Because "No mock resource of type '$script:ResType'" } } It 'Processing does not throw when given an empty resource list' { $content = Get-Content -Path $script:ModFile -Raw $sb = [ScriptBlock]::Create($content) { Invoke-Command -ScriptBlock $sb -ArgumentList $null, $null, $null, @(), $null, 'Processing', $null, $null, 'Light20', $null } | Should -Not -Throw } } Describe 'Storage Module Reporting Phase — <Name>' -ForEach $StorageModules { BeforeAll { $script:ModFile = Join-Path $script:StoragePath $File $script:ResType = $Type $script:XlsxFile = Join-Path $script:TempDir ("Stor_{0}_{1}.xlsx" -f $Name, [System.IO.Path]::GetRandomFileName()) $matchedResources = $script:MockResources | Where-Object { $_.TYPE -eq $script:ResType } if ($matchedResources) { $content = Get-Content -Path $script:ModFile -Raw $sb = [ScriptBlock]::Create($content) $script:ProcessedData = Invoke-Command -ScriptBlock $sb -ArgumentList $null, $null, $null, $script:MockResources, $null, 'Processing', $null, $null, 'Light20', $null } else { $script:ProcessedData = $null } } It 'Reporting phase does not throw' { if ($script:ProcessedData) { $content = Get-Content -Path $script:ModFile -Raw $sb = [ScriptBlock]::Create($content) { Invoke-Command -ScriptBlock $sb -ArgumentList $null, $null, $null, $null, $null, 'Reporting', $script:XlsxFile, $script:ProcessedData, 'Light20', $null } | Should -Not -Throw } else { Set-ItResult -Skipped -Because 'No processed data' } } It 'Excel file is created' { if ($script:ProcessedData) { $script:XlsxFile | Should -Exist } else { Set-ItResult -Skipped -Because 'No processed data' } } } |