Framework/Core/SVT/Services/DBForMySql.ps1

Set-StrictMode -Version Latest
class DBForMySql: AzSVTBase {
    hidden [PSObject[]] $MySqlFirewallDetails = $null;
    hidden [PSObject] $ResourceAppIdURI = $null;
    hidden [PSObject] $AccessToken = $null;
    hidden [PSObject] $header = $null;
    hidden [PSObject] $headers = $null;
    hidden [PSObject] $ResourceObject;
    hidden [PSObject] $MySQLFirewallRules;
    DBForMySql([string] $subscriptionId, [SVTResource] $svtResource): 
    Base($subscriptionId, $svtResource) { 
       
        $this.GetResourceObject();
        $this.ResourceAppIdURI = [WebRequestHelper]::GetResourceManagerUrl()     
        
    }
    
    hidden [PSObject] GetResourceObject() {
        if (-not $this.ResourceObject) {
            
            $this.ResourceObject = Get-AzResource -ResourceId $this.ResourceContext.ResourceId
            
            if (-not $this.ResourceObject) {
                throw ([SuppressedException]::new(("Resource '{0}' not found under Resource Group '{1}'" -f ($this.ResourceContext.ResourceName), ($this.ResourceContext.ResourceGroupName)), [SuppressedExceptionType]::InvalidOperation))
            }
        }
        return $this.ResourceObject;
    }

    hidden [ControlResult] CheckMySQLSSLConnection([ControlResult] $controlResult) {
     
        if (([Helpers]::CheckMember($this.ResourceObject.properties, "sslEnforcement"))) {
            $ssl_option = $this.ResourceObject.properties.sslEnforcement
        }
        else{
          $ssl_option = 'error'
        }
        
        if ($ssl_option -eq 'error') {
            $controlResult.AddMessage([VerificationResult]::Manual, "Unable to get SSL details for - [$($this.ResourceContext.ResourceName)]");
        }
        else {
            if ($ssl_option.ToLower() -eq 'enabled') {
                $controlResult.AddMessage([VerificationResult]::Passed, "SSL connection is enabled.");
            }
            else {
                $controlResult.AddMessage([VerificationResult]::Failed, "SSL connection is disabled.");
            }
        }
    
        #return
        return $controlResult
    }

    hidden [ControlResult] CheckMySQLBCDRStatus([ControlResult] $controlResult) {
        #fetching backup details
        $backupSettings = @{ 
            "backupRetentionDays" = $this.ResourceObject.properties.storageProfile.backupRetentionDays;
            "geoRedundantBackup"  = $this.ResourceObject.properties.storageProfile.geoRedundantBackup
        }

        $controlResult.AddMessage([VerificationResult]::Verify, "Verify backup settings for MySql server", $backupSettings);
        $controlResult.SetStateData("Backup setting:", $backupSettings);

        return $controlResult;
    }
    
    hidden [ControlResult] CheckMySQLServerVnetRules([ControlResult] $controlResult) {
        $virtualNetworkRules = ''
        $uri = [system.string]::Format($this.ResourceAppIdURI + "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DBforMySQL/servers/{2}/virtualNetworkRules/?api-version=2017-12-01", $this.SubscriptionContext.SubscriptionId, $this.ResourceContext.ResourceGroupName, $this.ResourceContext.ResourceName)      
      
        try {
            $virtualNetworkRules = [WebRequestHelper]::InvokeGetWebRequest($uri);
        }
        catch {
            $controlResult.AddMessage([VerificationResult]::Manual, "Unable to fetch details of functions.");
        }
        if ([Helpers]::CheckMember($virtualNetworkRules, "id")) {   
            $vnetRules = $virtualNetworkRules | ForEach-Object {
                @{ 'name' = "$($_.name)"; 'id' = "$($_.id)"; 'virtualNetworkSubnetId' = "$($_.properties.virtualNetworkSubnetId)" }
            }
            $controlResult.AddMessage([VerificationResult]::Passed, "The enabled virtual network rules are:", $vnetRules);
            $controlResult.SetStateData("Configured virtual network rules:", $vnetRules);
        }
        else {
            $controlResult.AddMessage([VerificationResult]::Verify, "There are no virtual network rules enabled for '$($this.ResourceContext.ResourceName)' server. Consider using virtual network rules for improved isolation.");
        }
      
        return $controlResult
    
    }

    hidden [ControlResult] CheckMySQLServerATP([ControlResult] $controlResult) {
        $uri = [system.string]::Format($this.ResourceAppIdURI + "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DBforMySQL/servers/{2}/securityAlertPolicies/Default?api-version=2017-12-01", $this.SubscriptionContext.SubscriptionId, $this.ResourceContext.ResourceGroupName, $this.ResourceContext.ResourceName)      
        try {
            $response = [WebRequestHelper]::InvokeGetWebRequest($uri); 
            if ([Helpers]::CheckMember($response[0], "properties.state")) {
                if ($response[0].properties.state.ToLower() -eq "enabled") {
                    $controlResult.AddMessage([VerificationResult]::Passed, "Advanced threat protection is enabled.");
                }
                else {
                    $controlResult.AddMessage([VerificationResult]::Failed, "Advanced threat protection is disabled.");
                }
            }
        }

        catch {
            $controlResult.AddMessage(($_.Exception).Message);
        }
        return $controlResult

    }

    hidden [ControlResult] CheckMySQLFirewallAccessAzureService([ControlResult] $controlResult) {
        $uri = [system.string]::Format($this.ResourceAppIdURI + "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DBforMySQL/servers/{2}/firewallRules/AllowAllWindowsAzureIps?api-version=2017-12-01", $this.SubscriptionContext.SubscriptionId, $this.ResourceContext.ResourceGroupName, $this.ResourceContext.ResourceName)      
        try {
            $response = [WebRequestHelper]::InvokeGetWebRequest($uri); 
            if ($null -ne $response) {
                if ([Helpers]::CheckMember($response[0], "name")) {
                    if ($response[0].name.ToLower() -eq "allowallwindowsazureips") {
                        $controlResult.AddMessage([VerificationResult]::Verify, "Setting 'Allow Access to Azure Services' is enabled. Please verify if your scenario really requires it.");
                    }
                    else {
                        $controlResult.AddMessage([VerificationResult]::Passed, "Setting 'Allow Access to Azure Services' is disabled.");
                    }
                }
            }
            else {
                $controlResult.AddMessage([VerificationResult]::Passed, "Setting 'Allow Access to Azure Services' is disabled.");
            }
        }
        catch { 
            #API call throws an exception when allow Access to Azure Service is disabled
            if (([Helpers]::CheckMember($_.Exception, "ExceptionType") -and ($_.Exception).ExceptionType.ToString().ToLower() -eq "invalidoperation")) {
                $controlResult.AddMessage([VerificationResult]::Passed, "Setting 'Allow Access to Azure Services' is disabled.");
            } 
            else {
              $controlResult.AddMessage([VerificationResult]::Manual, "Unable to verify setting 'Allow Access to Azure Services'.");
            }   
        }
        return $controlResult
    }
   
    [PSObject] GetFirewallRules() {
        if ($null -eq $this.MySQLFirewallRules) {
            $uri = [system.string]::Format($this.ResourceAppIdURI + "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DBforMySQL/servers/{2}/firewallRules?api-version=2017-12-01", $this.SubscriptionContext.SubscriptionId, $this.ResourceContext.ResourceGroupName, $this.ResourceContext.ResourceName) 
            try {
                $this.MySQLFirewallRules = [WebRequestHelper]::InvokeGetWebRequest($uri);
            }
            catch {
                $this.MySQLFirewallRules = 'error'
            }
        }
        return $this.MySQLFirewallRules
    }

    hidden [ControlResult] CheckMySQLFirewallIpRange([ControlResult] $controlResult) {
        $firewallRules = $this.GetFirewallRules()
        if ($firewallRules -eq 'error') {
            $controlResult.AddMessage([VerificationResult]::Manual, "Unable to get firewall rules for - [$($this.ResourceContext.ResourceName)]");
        }
        else {
            if ([Helpers]::CheckMember($firewallRules, "id")) {
                $firewallRulesForAzure = $firewallRules | Where-Object { $_.name -ne "AllowAllWindowsAzureIps" }
                if (($firewallRulesForAzure | Measure-Object ).Count -eq 0) {
                    $controlResult.AddMessage([VerificationResult]::Passed, "No custom firewall rules found.");
                    return $controlResult
                }

                $controlResult.AddMessage([MessageData]::new("Current firewall settings for - [" + $this.ResourceContext.ResourceName + "]",
                        $firewallRulesForAzure));

                $anyToAnyRule = $firewallRulesForAzure | Where-Object { $_.properties.StartIpAddress -eq $this.ControlSettings.IPRangeStartIP -and $_.properties.EndIpAddress -eq $this.ControlSettings.IPRangeEndIP }
                if (($anyToAnyRule | Measure-Object).Count -gt 0) {
                    $controlResult.AddMessage([VerificationResult]::Failed,
                        [MessageData]::new("Firewall rule covering all IPs (Start IP address: $($this.ControlSettings.IPRangeStartIP) To End IP Address: $($this.ControlSettings.IPRangeEndIP)) is defined."));
                }
                else {
                    $controlResult.VerificationResult = [VerificationResult]::Verify
                }
                $controlResult.SetStateData("Firewall IP addresses", $firewallRules);
            }
            else {
                $controlResult.AddMessage([VerificationResult]::Passed, "No custom firewall rules found.");  
            }
          
        }

        return $controlResult;
    }

   


}