Framework/Core/SVT/Services/Storage.ps1
using namespace Microsoft.Azure.Management.Storage.Models using namespace Microsoft.WindowsAzure.Storage.Shared.Protocol Set-StrictMode -Version Latest class Storage: SVTBase { hidden [PSObject] $ResourceObject; Storage([string] $subscriptionId, [string] $resourceGroupName, [string] $resourceName): Base($subscriptionId, $resourceGroupName, $resourceName) { $this.GetResourceObject(); } Storage([string] $subscriptionId, [SVTResource] $svtResource): Base($subscriptionId, $svtResource) { $this.GetResourceObject(); } hidden [PSObject] GetResourceObject() { if (-not $this.ResourceObject) { $this.ResourceObject = Get-AzureRmStorageAccount -Name $this.ResourceContext.ResourceName -ResourceGroupName $this.ResourceContext.ResourceGroupName -ErrorAction Stop if(-not $this.ResourceObject) { throw ("Resource '{0}' not found under Resource Group '{1}'" -f ($this.ResourceContext.ResourceName), ($this.ResourceContext.ResourceGroupName)) } } return $this.ResourceObject; } hidden [ControlResult] CheckStorageContainerPublicAccessTurnOff([ControlResult] $controlResult) { $allContainers = @(); try { $allContainers += Get-AzureStorageContainer -Context $this.ResourceObject.Context -ErrorAction Stop } catch { if(($_.Exception).Response.StatusCode -eq [System.Net.HttpStatusCode]::Forbidden) { $controlResult.AddMessage([VerificationResult]::Manual, ($_.Exception).Message); return $controlResult } else { throw $_ } } #Containers others than private $publicContainers = $allContainers | Where-Object { $_.PublicAccess -ne [Microsoft.WindowsAzure.Storage.Blob.BlobContainerPublicAccessType]::Off } if(($publicContainers | Measure-Object ).Count -eq 0) { $controlResult.AddMessage([VerificationResult]::Passed, "There are no containers having pubic access with anonymous authentication"); } else { $controlResult.AddMessage([VerificationResult]::Failed , [MessageData]::new("Remove public access from following containers. Total - $(($publicContainers | Measure-Object ).Count)", ($publicContainers | Select-Object -Property Name, PublicAccess))); } return $controlResult; } hidden [ControlResult] CheckStorageEnableDiagnosticsLog([ControlResult] $controlResult) { #Checking for storage kind $serviceMapping = $this.ControlSettings.StorageKindMapping | Where-Object { $_.Kind -eq $this.ResourceObject.Kind } | Select-Object -First 1; if(-not $serviceMapping) { #Currently only 'General purpose' or 'Blob storage' account kind is present #If new storage kind is introduced code needs to be updated as per new storage kind $controlResult.AddMessage("Storage Account kind is not supported"); return $controlResult; } #Checking for applicable sku $daignosticsSkuMapping = $this.ControlSettings.StorageDiagnosticsSkuMapping | Where-Object { $_ -eq $this.ResourceObject.Sku.Name } | Select-Object -First 1; if(-not $daignosticsSkuMapping) { #Diagnostics settings are not available for premium storage and zone redundant storage. $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("Diagnostics settings are not supported for Sku Tier - [$($this.ResourceObject.Sku.Name)]")); return $controlResult; } try{ $result = $true #Check Metrics diagnostics log property $serviceMapping.DiagnosticsLogServices | ForEach-Object { $result = $this.GetServiceLoggingProperty($_, $controlResult) -and $result ; } #Check Metrics logging property $serviceMapping.Services | ForEach-Object { $result = $this.GetServiceMetricsProperty($_, $controlResult) -and $result ; } if($result){ $controlResult.VerificationResult = [VerificationResult]::Passed } else{ $controlResult.VerificationResult = [VerificationResult]::Failed } } catch{ #With Reader Role exception will be is thrown. if(($_.Exception).Response.StatusCode -eq [System.Net.HttpStatusCode]::Forbidden) { $controlResult.AddMessage(($_.Exception).Message); return $controlResult } else { throw $_ } } return $controlResult; } hidden [ControlResult] CheckStorageGeoRedundantReplication([ControlResult] $controlResult) { if($null -ne $this.ResourceObject.Sku.Tier -and $null -ne $this.ResourceObject.Sku.Name){ $controlResult.AddMessage("Current storage sku tier is - [$($this.ResourceObject.Sku.Tier)] and sku name is - [$($this.ResourceObject.Sku.Name)]"); } else{ $controlResult.AddMessage("Unable to get sku details for - [$($this.ResourceContext.ResourceName)]"); return $controlResult } if($this.ResourceObject.Sku.Tier -eq [SkuTier]::Standard){ $isGeoRedundantSku = $this.ControlSettings.StorageGeoRedundantSku | Where-Object { $_ -eq $this.ResourceObject.Sku.Name } | Select-Object -First 1; if($isGeoRedundantSku){ $controlResult.VerificationResult = [VerificationResult]::Passed } else { $controlResult.EnableFixControl = $true; $controlResult.VerificationResult = [VerificationResult]::Failed } } else{ $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("A premium storage account supports only locally redundant storage as the replication option")); } return $controlResult; } hidden [ControlResult] CheckStorageBlobEncryptionEnabled([ControlResult] $controlResult) { if($null -ne $this.ResourceObject.Encryption) { if($null -eq $this.ResourceObject.Encryption.Services.Blob.Enabled ){ $controlResult.EnableFixControl = $true; $controlResult.AddMessage([MessageData]::new("Unable to get Blob encryption settings")) return $controlResult; } if($this.ResourceObject.Encryption.Services.Blob.Enabled -eq $true){ $controlResult.VerificationResult = [VerificationResult]::Passed } else{ $controlResult.EnableFixControl = $true; $controlResult.VerificationResult = [VerificationResult]::Failed } } else { $controlResult.EnableFixControl = $true; $controlResult.AddMessage([MessageData]::new("Storage blob encryption is not enabled")) $controlResult.VerificationResult = [VerificationResult]::Failed } return $controlResult; } hidden [ControlResult] CheckStorageFileEncryptionEnabled([ControlResult] $controlResult) { if($null -ne $this.ResourceObject.Encryption) { [bool] $file = [Helpers]::CheckMember($this.ResourceObject.Encryption.Services, "File") if($file){ if($null -eq $this.ResourceObject.Encryption.Services.File ) { $controlResult.EnableFixControl = $true; $controlResult.AddMessage([VerificationResult]::Failed, "This could be an old storage account for which the file encryption is not enabled. Please follow the recommendation in the security report.") return $controlResult; } else { if($this.ResourceObject.Encryption.Services.File.Enabled -eq $true) { $controlResult.VerificationResult = [VerificationResult]::Passed } else { $controlResult.EnableFixControl = $true; $controlResult.VerificationResult = [VerificationResult]::Failed } } } else{ $controlResult.EnableFixControl = $true; $controlResult.AddMessage([VerificationResult]::Failed, "This could be an old storage account for which the file encryption is not enabled. Please follow the recommendation in the security report.") } } else { $controlResult.EnableFixControl = $true; $controlResult.AddMessage([MessageData]::new("Storage file encryption is not enabled")) $controlResult.VerificationResult = [VerificationResult]::Failed } return $controlResult; } hidden [ControlResult] CheckStorageMetricAlert([ControlResult] $controlResult) { $serviceMapping = $this.ControlSettings.StorageKindMapping | Where-Object { $_.Kind -eq $this.ResourceObject.Kind } | Select-Object -First 1; if(-not $serviceMapping) { #Currently only 'General purpose' or 'Blob storage' account kind is present #If new storage kind is introduced code needs to be updated as per new storage kind $controlResult.AddMessage("Storage Account kind is not supported"); return $controlResult; } #Checking for applicable sku $daignosticsSkuMapping = $this.ControlSettings.StorageAlertSkuMapping | Where-Object { $_ -eq $this.ResourceObject.Sku.Name } | Select-Object -First 1; if(-not $daignosticsSkuMapping) { #Metrics or logging capability not enabled for premium storage and zone redundant storage account. $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("Diagnostics settings are not supported for Sku Tier - [$($this.ResourceObject.Sku.Name)]")); return $controlResult; } $result = $true; $serviceMapping.Services | ForEach-Object { $result = $this.CheckMetricAlertConfiguration($this.ControlSettings.MetricAlert.Storage, $controlResult, ("/services/" + $_)) -and $result ; } if($result) { $controlResult.VerificationResult = [VerificationResult]::Passed } else { $controlResult.VerificationResult = [VerificationResult]::Failed } return $controlResult; } hidden [boolean] GetServiceLoggingProperty([string] $serviceType,[ControlResult] $controlResult) { $loggingProperty = Get-AzureStorageServiceLoggingProperty -ServiceType $ServiceType -Context $this.ResourceObject.Context -ErrorAction Stop if($null -ne $loggingProperty){ #Check For Retention day's if($loggingProperty.LoggingOperations -eq [LoggingOperations]::All -and (($loggingProperty.RetentionDays -eq $this.ControlSettings.Diagnostics_RetentionPeriod_Forever) -or ($loggingProperty.RetentionDays -eq $this.ControlSettings.Diagnostics_RetentionPeriod_Min))){ return $True } else{ $controlResult.AddMessage("Diagnostics settings($($serviceType) logs) is either disabled OR not retaining logs for at least $($this.ControlSettings.Diagnostics_RetentionPeriod_Min) days for service type - [$($serviceType)]") return $false } } else { $controlResult.AddMessage("Diagnostics settings($($serviceType) logs) is disabled for service type - [$($serviceType)]") return $false } } hidden [boolean] GetServiceMetricsProperty([string] $serviceType,[ControlResult] $controlResult) { $serviceMetricsProperty= Get-AzureStorageServiceMetricsProperty -MetricsType Hour -ServiceType $ServiceType -Context $this.ResourceObject.Context -ErrorAction Stop if($null -ne $serviceMetricsProperty){ #Check For Retention day's if($serviceMetricsProperty.MetricsLevel -eq [MetricsLevel]::ServiceAndApi -and (($serviceMetricsProperty.RetentionDays -eq $this.ControlSettings.Diagnostics_RetentionPeriod_Min) -or ($serviceMetricsProperty.RetentionDays -eq $this.ControlSettings.Diagnostics_RetentionPeriod_Forever))) { return $True } else { $controlResult.AddMessage("Diagnostics settings($($serviceType) aggregate metrics,$($serviceType) per API metrics) is either disabled OR not retaining logs for at least $($this.ControlSettings.Diagnostics_RetentionPeriod_Min) days for service type - [$($serviceType)]") return $false } } else { $controlResult.AddMessage("Diagnostics settings($($serviceType) aggregate metrics,$($serviceType) per API metrics) is disabled for service type - [$($serviceType)]") return $false } } hidden [ControlResult] CheckStorageEncryptionInTransit([ControlResult] $controlResult) { if($null -ne $this.ResourceObject.EnableHttpsTrafficOnly){ if($this.ResourceObject.EnableHttpsTrafficOnly -eq $true){ $controlResult.VerificationResult = [VerificationResult]::Passed $controlResult.AddMessage([MessageData]::new("Storage secure transfer is enabled")) } else{ $controlResult.EnableFixControl = $true; $controlResult.VerificationResult = [VerificationResult]::Failed $controlResult.AddMessage([MessageData]::new("Storage secure transfer is not enabled")) } } else{ $controlResult.EnableFixControl = $true; $controlResult.AddMessage([MessageData]::new("Storage secure transfer is not enabled")) $controlResult.VerificationResult = [VerificationResult]::Failed } return $controlResult; } } |