manifests/collectors/Storage/StorageAccounts.psd1
|
# # GENERATED by scripts/ConvertTo-ScoutCollectorDefinition.ps1 from Modules/Public/InventoryModules/Storage/StorageAccounts.ps1 (AB#5660). # Field expressions are copied verbatim from the original collector and evaluate in an # equivalent scope -- see docs/design/decisions/declarative-collectors.md. # Review before trusting; regenerate rather than hand-patch if the source collector changes. # @{ ResourceTypes = @( 'microsoft.storage/storageaccounts' ) ResourceTypeMatching = 'Grouped' AdditionalFilter = $null FilterPreamble = '' RowLoopVariable = '1' Preamble = @' $ResUCount = 1 # An EMPTY $sub1 is not $null -- the match is empty for any resource whose subscription # is outside the requested scope -- and reading .Name off an empty collection throws # under StrictMode (AB#5671). Resolved once here, as VirtualMachine.ps1 already does. $sub1 = $SUB | Where-Object { $_.Id -eq $1.subscriptionId } # The else arm is $null, NOT '': with StrictMode off $sub1.Name on an unmatched ($null) # $sub1 evaluated to $null, and the ~110 collectors that still read $sub1.Name directly # emit $null here. '' was a silent behaviour change -- the declarative equivalence proof # caught it on 11 collectors, and it would have been invisible on the rest (AB#5659). $SubscriptionName = if ($sub1) { @($sub1)[0].Name } else { $null } $data = $1.PROPERTIES $Retired = Foreach ($Retirement in $Retirements) { if ($Retirement.id -eq $1.id) { $Retirement } } if ($Retired) { $RetiredFeature = foreach ($Retire in $Retired) { $RetiredServiceID = $Unsupported | Where-Object {$_.Id -eq $Retired.ServiceID} $tmp0 = [pscustomobject]@{ 'RetiredFeature' = $RetiredServiceID.RetiringFeature 'RetiredDate' = $RetiredServiceID.RetirementDate } $tmp0 } $RetiringFeature = if (@($RetiredFeature.RetiredFeature).count -gt 1) { $RetiredFeature.RetiredFeature | ForEach-Object { $_ + ' ,' } }else { $RetiredFeature.RetiredFeature} $RetiringFeature = [string]$RetiringFeature $RetiringFeature = if ($RetiringFeature -like '* ,*') { $RetiringFeature -replace ".$" }else { $RetiringFeature } $RetiringDate = if (@($RetiredFeature.RetiredDate).count -gt 1) { $RetiredFeature.RetiredDate | ForEach-Object { $_ + ' ,' } }else { $RetiredFeature.RetiredDate} $RetiringDate = [string]$RetiringDate $RetiringDate = if ($RetiringDate -like '* ,*') { $RetiringDate -replace ".$" }else { $RetiringDate } } else { $RetiringFeature = $null $RetiringDate = $null } # The creationTime field is absent (not present-and-null) on older API versions and some # resource kinds, so the raw read throws under StrictMode -- and [datetime] of a null # produced a bogus 0001-01-01 before that (AB#5671). $timecreated = Get-AZSCSafeProperty -InputObject $data -Path 'creationTime' $timecreated = if ($timecreated) { ([datetime]$timecreated).ToString("yyyy-MM-dd HH:mm") } else { '' } $TLSv = if ((Get-AZSCSafeProperty -InputObject $data -Path 'minimumTlsVersion' -Enumerate) -eq 'TLS1_2') { "TLS 1.2" }elseif ((Get-AZSCSafeProperty -InputObject $data -Path 'minimumTlsVersion' -Enumerate) -eq 'TLS1_1') { "TLS 1.1" }else { "TLS 1.0" } # AB#5671: an untagged resource's Resource Graph row OMITS the tags property rather # than carrying an empty object, so the raw read throws under StrictMode -- and so # does psobject.properties on a $null. The historic '0' sentinel existed only to make # the tag loop below run ONCE for an untagged resource, but '0'.Name throws too; an # empty tag object runs it once AND emits the identical [string]-cast empty Name/Value. $RowTags = Get-AZSCSafeProperty -InputObject $1 -Path 'tags' $TagProps = if ($null -ne $RowTags) { $RowTags.psobject.properties } else { $null } $Tags = if (![string]::IsNullOrEmpty($TagProps)) { $TagProps } else { [pscustomobject]@{ Name = $null; Value = $null } } $VNETRules = if(![string]::IsNullOrEmpty((Get-AZSCSafeProperty -InputObject $data -Path 'networkacls.virtualnetworkrules' -Enumerate))){(Get-AZSCSafeProperty -InputObject $data -Path 'networkacls.virtualnetworkrules' -Enumerate)}else{' '} $BlobAccess = if ((Get-AZSCSafeProperty -InputObject $data -Path 'allowBlobPublicAccess' -Enumerate) -eq $false){$false}else{$true} $KeyAccess = if((Get-AZSCSafeProperty -InputObject $data -Path 'allowsharedkeyaccess' -Enumerate) -eq $true){$true}else{$false} $SFTPEnabled = if((Get-AZSCSafeProperty -InputObject $data -Path 'isSftpEnabled' -Enumerate) -eq $true){$true}else{$false} $HNSEnabled = if((Get-AZSCSafeProperty -InputObject $data -Path 'ishnsenabled' -Enumerate) -eq $true){$true}else{$false} $NFSv3 = if((Get-AZSCSafeProperty -InputObject $data -Path 'isnfsv3enabled' -Enumerate) -eq $true){$true}else{$false} $LargeFileShare = if((Get-AZSCSafeProperty -InputObject $data -Path 'largeFileSharesState' -Enumerate) -eq $true){$true}else{$false} $CrossTNT = if((Get-AZSCSafeProperty -InputObject $data -Path 'allowCrossTenantReplication' -Enumerate) -eq $true){$true}else{$false} $InfrastructureEncryption = if((Get-AZSCSafeProperty -InputObject $data -Path 'encryption.requireInfrastructureEncryption' -Enumerate) -eq "True"){$true}else{$false} if ((Get-AZSCSafeProperty -InputObject $data -Path 'azureFilesIdentityBasedAuthentication.directoryServiceOptions' -Enumerate) -eq 'None') { $EntraID = $false } elseif ([string]::IsNullOrEmpty((Get-AZSCSafeProperty -InputObject $data -Path 'azureFilesIdentityBasedAuthentication.directoryServiceOptions' -Enumerate))) { $EntraID = $false } else { $EntraID = $true } # This if/elseif chain has NO else, so an account that matches none of the three cases # (no networkAcls block and no publicNetworkAccess field -- both are optional) left # $PubNetAccess unset. Under StrictMode the later read is then an error; without # StrictMode it was worse but silent, because the variable survives from one loop # iteration to the next, so such an account reported the PREVIOUS account's value. # Resetting per account is the same fix VirtualMachine.ps1 already applies to its # capability variables (AB#5671). $PubNetAccess = $null if ((Get-AZSCSafeProperty -InputObject $data -Path 'networkacls.defaultaction' -Enumerate) -eq 'allow') { $PubNetAccess = 'Enabled from all networks' } elseif ((Get-AZSCSafeProperty -InputObject $data -Path 'networkacls.defaultaction' -Enumerate) -eq 'Deny' -and (Get-AZSCSafeProperty -InputObject $data -Path 'publicNetworkAccess' -Enumerate) -eq 'Enabled') { $PubNetAccess = 'Enabled from selected virtual networks and IP addresses' } elseif ((Get-AZSCSafeProperty -InputObject $data -Path 'publicNetworkAccess' -Enumerate) -eq 'Disabled') { $PubNetAccess = 'Disabled' } $PVTEndpoints = @() foreach ($pvt in (Get-AZSCSafeProperty -InputObject $data -Path 'privateEndpointConnections.properties.privateendpoint' -Enumerate)) { $PVTEndpoints += if(![string]::IsNullOrEmpty((Get-AZSCSafeProperty -InputObject $pvt -Path 'id' -Enumerate))){(Get-AZSCIdSegment -Id (Get-AZSCSafeProperty -InputObject $pvt -Path 'id' -Enumerate) -Index 8)}else{$null} } $DirectResources = @() foreach ($DiRes in (Get-AZSCSafeProperty -InputObject $data -Path 'networkacls.resourceaccessrules' -Enumerate)) { $DirectResources += if(![string]::IsNullOrEmpty((Get-AZSCSafeProperty -InputObject $DiRes -Path 'resourceid' -Enumerate))){(Get-AZSCIdSegment -Id (Get-AZSCSafeProperty -InputObject $DiRes -Path 'resourceid' -Enumerate) -Index 8)}else{$null} } $FinalDirectResources = if ($DirectResources.count -gt 1) { $DirectResources | ForEach-Object { $_ + ' ,' } }else { $DirectResources } $FinalDirectResources = [string]$FinalDirectResources $FinalDirectResources = if ($FinalDirectResources -like '* ,*') { $FinalDirectResources -replace ".$" }else { $FinalDirectResources } $FinalPVTEndpoint = if ($PVTEndpoints.count -gt 1) { $PVTEndpoints | ForEach-Object { $_ + ' ,' } }else { $PVTEndpoints } $FinalPVTEndpoint = [string]$FinalPVTEndpoint $FinalPVTEndpoint = if ($FinalPVTEndpoint -like '* ,*') { $FinalPVTEndpoint -replace ".$" }else { $FinalPVTEndpoint } $FinalACLIPs = if (@((Get-AZSCSafeProperty -InputObject $data -Path 'networkacls.iprules.value' -Enumerate)).count -gt 1) { (Get-AZSCSafeProperty -InputObject $data -Path 'networkacls.iprules.value' -Enumerate) | ForEach-Object { $_ + ' ,' } }else { (Get-AZSCSafeProperty -InputObject $data -Path 'networkacls.iprules.value' -Enumerate) } $FinalACLIPs = [string]$FinalACLIPs $FinalACLIPs = if ($FinalACLIPs -like '* ,*') { $FinalACLIPs -replace ".$" }else { $FinalACLIPs } # Service properties are pre-fetched into a typed envelope during collection. $OperationalEnvelope = @($Resources | Where-Object { $_.TYPE -eq 'AZSC/Operational/StorageAccount' -and $_.id -eq $1.id }) | Select-Object -First 1 $blobProperties = Get-AZSCSafeProperty -InputObject $OperationalEnvelope -Path 'properties.BlobService' $fileProperties = Get-AZSCSafeProperty -InputObject $OperationalEnvelope -Path 'properties.FileService' '@ AdditionalRowLoops = @( @{ Variable = '2' Source = '$VNETRules' Preamble = @' $VNET = if(![string]::IsNullOrEmpty((Get-AZSCSafeProperty -InputObject $2 -Path 'id' -Enumerate))){(Get-AZSCIdSegment -Id (Get-AZSCSafeProperty -InputObject $2 -Path 'id' -Enumerate) -Index 8)}else{''} $Subnet = if(![string]::IsNullOrEmpty((Get-AZSCSafeProperty -InputObject $2 -Path 'id' -Enumerate))){(Get-AZSCIdSegment -Id (Get-AZSCSafeProperty -InputObject $2 -Path 'id' -Enumerate) -Index 10)}else{''} '@ } ) TagLoop = @{ Variable = 'Tag' Source = '$Tags' Preamble = '' } Fields = @( @{ Name = 'ID' Expression = '$1.id' } @{ Name = 'Subscription' Expression = '$SubscriptionName' } @{ Name = 'Resource Group' Expression = '$1.RESOURCEGROUP' } @{ Name = 'Name' Expression = '$1.NAME' } @{ Name = 'Location' Expression = '$1.LOCATION' } @{ Name = 'Retiring Feature' Expression = '$RetiringFeature' } @{ Name = 'Retiring Date' Expression = '$RetiringDate' } @{ Name = 'Zone' Expression = '$1.ZONES' } @{ Name = 'SKU' Expression = '(Get-AZSCSafeProperty -InputObject $1 -Path ''sku.name'' -Enumerate)' } @{ Name = 'Tier' Expression = '(Get-AZSCSafeProperty -InputObject $1 -Path ''sku.tier'' -Enumerate)' } @{ Name = 'Storage Account Kind' Expression = '$1.kind' } @{ Name = 'Secure Transfer Required' Expression = '(Get-AZSCSafeProperty -InputObject $data -Path ''supportsHttpsTrafficOnly'' -Enumerate)' } @{ Name = 'Allow Blob Anonymous Access' Expression = '$BlobAccess' } @{ Name = 'Minimum TLS Version' Expression = '$TLSv' } @{ Name = 'Microsoft Entra Authorization' Expression = '$EntraID' } @{ Name = 'Allow Storage Account Key Access' Expression = '$KeyAccess' } @{ Name = 'SFTP Enabled' Expression = '$SFTPEnabled' } @{ Name = 'Blob Soft Delete Days' Expression = 'if (Get-AZSCSafeProperty -InputObject $blobProperties -Path ''DeleteRetentionPolicy.Enabled'') { Get-AZSCSafeProperty -InputObject $blobProperties -Path ''DeleteRetentionPolicy.Days'' } else { ''N/A'' }' } @{ Name = 'Container Soft Delete Days' Expression = 'if (Get-AZSCSafeProperty -InputObject $blobProperties -Path ''containerDeleteRetentionPolicy.Enabled'') { Get-AZSCSafeProperty -InputObject $blobProperties -Path ''containerDeleteRetentionPolicy.Days'' } else { ''N/A'' }' } @{ Name = 'File Share Soft Delete Days' Expression = 'if (Get-AZSCSafeProperty -InputObject $fileProperties -Path ''ShareDeleteRetentionPolicy.Enabled'') { Get-AZSCSafeProperty -InputObject $fileProperties -Path ''ShareDeleteRetentionPolicy.Days'' } else { ''N/A'' }' } @{ Name = 'Hierarchical Namespace' Expression = '$HNSEnabled' } @{ Name = 'NFSv3 Enabled' Expression = '$NFSv3' } @{ Name = 'Large File Shares' Expression = '$LargeFileShare' } @{ Name = 'Access Tier' Expression = '(Get-AZSCSafeProperty -InputObject $data -Path ''accessTier'' -Enumerate)' } @{ Name = 'Allow Cross Tenant Replication' Expression = '$CrossTNT' } @{ Name = 'Infrastructure Encryption Enabled' Expression = '$InfrastructureEncryption' } @{ Name = 'Public Network Access' Expression = '$PubNetAccess' } @{ Name = 'Private Endpoints' Expression = '$FinalPVTEndpoint' } @{ Name = 'Direct Access Resources' Expression = '$FinalDirectResources' } @{ Name = 'Virtual Networks' Expression = '$VNET' } @{ Name = 'Subnet' Expression = '$Subnet' } @{ Name = 'Direct Access IPs' Expression = '$FinalACLIPs' } @{ Name = 'Firewall Exceptions' Expression = '[string](Get-AZSCSafeProperty -InputObject $data -Path ''networkacls.bypass'' -Enumerate)' } @{ Name = 'Primary Location' Expression = '(Get-AZSCSafeProperty -InputObject $data -Path ''primaryLocation'' -Enumerate)' } @{ Name = 'Status Of Primary Location' Expression = '(Get-AZSCSafeProperty -InputObject $data -Path ''statusOfPrimary'' -Enumerate)' } @{ Name = 'Secondary Location' Expression = '(Get-AZSCSafeProperty -InputObject $data -Path ''secondaryLocation'' -Enumerate)' } @{ Name = 'Status Of Secondary Location' Expression = '(Get-AZSCSafeProperty -InputObject $data -Path ''statusofsecondary'' -Enumerate)' } @{ Name = 'Created Time' Expression = '$timecreated' } @{ Name = 'Resource U' Expression = '$ResUCount' } @{ Name = 'Tag Name' Expression = '[string]$Tag.Name' } @{ Name = 'Tag Value' Expression = '[string]$Tag.Value' } ) Export = @{ WorksheetName = 'Storage Accounts' TableNamePrefix = 'StorAccTable_' Columns = @( 'Subscription' 'Resource Group' 'Name' 'Location' 'Zone' 'SKU' 'Tier' 'Storage Account Kind' 'Retiring Feature' 'Retiring Date' 'Secure Transfer Required' 'Allow Blob Anonymous Access' 'Minimum TLS Version' 'Microsoft Entra Authorization' 'Allow Storage Account Key Access' 'SFTP Enabled' 'Blob Soft Delete Days' 'Container Soft Delete Days' 'File Share Soft Delete Days' 'Hierarchical Namespace' 'NFSv3 Enabled' 'Large File Shares' 'Access Tier' 'Allow Cross Tenant Replication' 'Infrastructure Encryption Enabled' 'Public Network Access' 'Private Endpoints' 'Direct Access Resources' 'Virtual Networks' 'Subnet' 'Direct Access IPs' 'Firewall Exceptions' 'Primary Location' 'Status Of Primary Location' 'Secondary Location' 'Status Of Secondary Location' 'Created Time' 'Resource U' ) TagColumns = @( 'Tag Name' 'Tag Value' ) TagColumnsBefore = 'Resource U' NumberFormat = '0' ConditionalText = @( 'New-ConditionalText false -Range K:K' 'New-ConditionalText true -Range L:L' 'New-ConditionalText 1.0 -Range M:M' 'New-ConditionalText 1.1 -Range M:M' 'New-ConditionalText true -Range O:O' 'New-ConditionalText all -Range Z:Z' 'New-ConditionalText . -Range AF:AF -ConditionalType ContainsText' 'New-ConditionalText unavailable -Range AH:AH' 'New-ConditionalText unavailable -Range AI:AI' 'New-ConditionalText -Range I2:I100 -ConditionalType ContainsText' ) } SourceCollector = 'Modules/Public/InventoryModules/Storage/StorageAccounts.ps1' } |