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;
        }
}