Framework/Core/SVT/AzSKCfg/AzSKCfg.ps1


Set-StrictMode -Version Latest
class AzSKCfg: AzSVTBase
{
    $RGPerms = $false
    AzSKCfg([string] $subscriptionId,[SVTResource] $svtResource):
        Base($subscriptionId,  $svtResource )
    {
        
    }
    hidden [ControlResult] CheckifCAPresent([ControlResult] $controlResult)
    {
        $AzSKRGName=[ConfigurationManager]::GetAzSKConfigData().AzSKRGName
        $this.RGPerms = $this.ControlStateExt.HasControlStateReadAccessPermissions()
        if ($this.RGPerms){

            $AutomationAccount=[Constants]::AutomationAccount
            

            $caAutomationAccount = Get-AzAutomationAccount -Name  $AutomationAccount -ResourceGroupName $AzSKRGName -ErrorAction SilentlyContinue
            if($caAutomationAccount)
            {
                
                $controlResult.AddMessage([VerificationResult]::Passed,
                                        [MessageData]::new("CA account '$($AutomationAccount)' is present in the subscription."));
            
            }
            else
            {
                    $controlResult.AddMessage([VerificationResult]::Failed,
                                        [MessageData]::new("CA account '$($AutomationAccount)' is not present in the subscription."));
        
            }
        }
        else{
            $controlResult.AddMessage([VerificationResult]::Manual,
                                        [MessageData]::new("You do not have required permissions to evaluate this control. You will need reader access on ["+ $AzSKRGName +"]" ));

        }

    return $controlResult
    }

    hidden [ControlResult] CheckHealthofCA([ControlResult] $controlResult)
    {
        $this.RGPerms = $this.ControlStateExt.HasControlStateReadAccessPermissions()
        $AzSKRGName=[ConfigurationManager]::GetAzSKConfigData().AzSKRGName
    if ($this.RGPerms){
        $HasGraphAPIAccess = [RoleAssignmentHelper]::HasGraphAccess();
        
        $AutomationAccount=[Constants]::AutomationAccount
        $AzSKRG = Get-AzResourceGroup -Name $AzSKRGName -ErrorAction SilentlyContinue    
        $stepCount = 0;

        $caAutomationAccount = Get-AzAutomationAccount -Name  $AutomationAccount -ResourceGroupName $AzSKRGName -ErrorAction SilentlyContinue
        if($caAutomationAccount)
        {
            #region: runbook version check
            $stepCount++
            $azskMinReqdRunbookVersion = [ConfigurationManager]::GetAzSKConfigData().AzSKCAMinReqdRunbookVersion
            $azskLatestCARunbookVersion = [ConfigurationManager]::GetAzSKConfigData().AzSKCARunbookVersion
            $azskCurrentCARunbookVersion = ""
            $RunbookVersionTagName="AzSKCARunbookVersion"
            if($null -ne $AzSKRG)
            {
                if(($AzSKRG.Tags | Measure-Object).Count -gt 0 -and $AzSKRG.Tags.ContainsKey($RunbookVersionTagName))
                {
                    $azskCurrentCARunbookVersion = $AzSKRG.Tags[$RunbookVersionTagName]
                }
            }
            if(![string]::IsNullOrWhiteSpace($azskCurrentCARunbookVersion) -and ([System.Version]$azskCurrentCARunbookVersion -ge [System.Version]$azskMinReqdRunbookVersion))
            {
                if([System.Version]$azskCurrentCARunbookVersion -ne [System.Version]$azskLatestCARunbookVersion)
                {
                        $controlResult.AddMessage([VerificationResult]::Failed,
                                        [MessageData]::new("$($stepCount.ToString("00")):CA runbook is not current as per the required latest version. AzSK current runbook version is $([System.Version]$azskCurrentCARunbookVersion) and latest runbook version is $([System.Version]$azskLatestCARunbookVersion)."));
                        return $controlResult

                }
                else
                {
                    $controlResult.AddMessage([VerificationResult]::Passed,
                                        [MessageData]::new("$($stepCount.ToString("00")): CA runbook is current as per the required latest version. AzSK current runbook version is $([System.Version]$azskCurrentCARunbookVersion)."));
                                        
                }
            }
            else
            {
                    $controlResult.AddMessage([VerificationResult]::Failed,
                                            [MessageData]::new("$($stepCount.ToString("00")): CA Runbook is too old."));
                return    $controlResult            
            }        
    
            #endregion
                
            #region: active schedule
                
            $stepCount++
            $RunbookName=[Constants]::RunbookName
            $activeSchedules = $this.GetActiveSchedules($RunbookName)
            if(($activeSchedules|Measure-Object).Count -eq 0)
            {
                    
                $controlResult.AddMessage([VerificationResult]::Failed,
                                                [MessageData]::new("$($stepCount.ToString("00")): Runbook $($RunbookName) is not scheduled."));
                return    $controlResult            
                    
            }        
            else 
            {
                $controlResult.AddMessage([VerificationResult]::Passed,
                                                [MessageData]::new("$($stepCount.ToString("00")): Active job schedule(s) found."));
            }
            #endregion

            if($HasGraphAPIAccess)
            {    
                #region: Check if service principal is configured and it has at least Reader access to subscription and contributor access to "AzSKRG", if either is missing display error message
                $stepCount++
                $isPassed = $false
                $runAsConnection = $this.GetRunAsConnection()
                if($runAsConnection)
                {            
                    $CAAADApplicationID = $runAsConnection.FieldDefinitionValues.ApplicationId
                    $spObject = Get-AzADServicePrincipal -ServicePrincipalName $CAAADApplicationID -ErrorAction SilentlyContinue
                    $spName=""
                    if($spObject){$spName = $spObject.DisplayName}
                    $haveSubscriptionRBACAccess = $true;
                    $haveRGRBACAccess = $true;
                    $subRBACoutputs = @();            
                    $haveSubscriptionRBACAccess = $this.CheckServicePrincipalSubscriptionAccess($CAAADApplicationID)
                    $haveRGRBACAccess = $this.CheckServicePrincipalRGAccess($CAAADApplicationID)                
            
                    if($haveSubscriptionRBACAccess -and $haveRGRBACAccess)
                    {
                        $controlResult.AddMessage([VerificationResult]::Passed,
                                                    [MessageData]::new("$($stepCount.ToString("00")): RunAs Account is correctly set up."));
                        $isPassed = $true
                    }
                    if(!$isPassed)
                    {
                        $controlResult.AddMessage([VerificationResult]::Failed,
                                                    [MessageData]::new("$($stepCount.ToString("00")): Service principal account (Name: $($spName)) configured in RunAs Account doesn't have required access ('Security Reader' and 'Reader' access on Subscription and/or Contributor access on Resource group AzSKRG).."));
                        return    $controlResult            
                    
                    }
                }
                else
                {
                    $controlResult.AddMessage([VerificationResult]::Failed,
                                                    [MessageData]::new("$($stepCount.ToString("00")): RunAs Account does not exist in automation account."));
                    return    $controlResult            
                }
                #endregion
            
                #region:Check if certificate expiry is in near future(in next 1 month) or it's expired
                $stepCount++
                $certificateAssetName = "AzureRunAsCertificate"
        
                $runAsCertificate = Get-AzAutomationCertificate -AutomationAccountName  $AutomationAccount `
                -Name $certificateAssetName `
                -ResourceGroupName $AzSKRGName -ErrorAction SilentlyContinue
        
                if($runAsCertificate)
                {
                    $runAsConnection = $this.GetRunAsConnection();
                    $ADapp = Get-AzADApplication -ApplicationId $runAsConnection.FieldDefinitionValues.ApplicationId -ErrorAction SilentlyContinue
                    if(($runAsCertificate.ExpiryTime.UtcDateTime - $(get-date).ToUniversalTime()).TotalDays -le 7)
                    {
                            $controlResult.AddMessage([VerificationResult]::Failed,
                                                    [MessageData]::new("$($stepCount.ToString("00")): RunAs Certificate is going to expire within next 7 days. Expiry date: $($runAsCertificate.ExpiryTime)."));
                        return    $controlResult            
                    }
                    elseif(($runAsCertificate.ExpiryTime - $(get-date)).TotalDays -gt 0 -and ($runAsCertificate.ExpiryTime - $(get-date)).TotalDays -le 30)
                    {
                            $controlResult.AddMessage([VerificationResult]::Verify,
                                                    [MessageData]::new("$($stepCount.ToString("00")): RunAs Certificate is going to expire within next 30 days. Expiry date: $($runAsCertificate.ExpiryTime)."));
                        return    $controlResult            
                    }
                    else
                    {
                            $controlResult.AddMessage([VerificationResult]::Passed,
                                                    [MessageData]::new("$($stepCount.ToString("00")): RunAs Certificate is correctly set up."));
                    }
                }
                else
                {
                    $controlResult.AddMessage([VerificationResult]::Failed,
                                                    [MessageData]::new("$($stepCount.ToString("00")): RunAs Certificate does not exist in automation account."));
                    return    $controlResult            
                }
                #endregion
            }
            else
            {
                $controlResult.CurrentSessionContext.Permissions.HasRequiredAccess = $false;
                $controlResult.AddMessage([VerificationResult]::Manual, "Not able to query Graph API. This has to be manually verified.");        
            }    
        }
        else
        {
            $controlResult.AddMessage([VerificationResult]::Failed,
                            [MessageData]::new("CA account '$($AutomationAccount)' is not present in the subscription."));
        }
}
    else{
                $controlResult.AddMessage([VerificationResult]::Manual,
                                        [MessageData]::new("You do not have required permissions to evaluate this control. You will need reader access on ["+ $AzSKRGName +"]" ));
    }
        return $controlResult
    }

    hidden [ControlResult] CheckifLatestModulePresent([ControlResult] $controlResult)
    {
        $AzSKModuleName= [Constants]::AzSKModuleName
        $currentModuleVersion= [Constants]::AzSKCurrentModuleVersion
        $serverVersion = [System.Version] ([ConfigurationManager]::GetAzSKConfigData().GetLatestAzSKVersion($AzSKModuleName));

        if($currentModuleVersion -ne $serverVersion)
                {
        
                $controlResult.AddMessage([VerificationResult]::Failed,
                                    [MessageData]::new("Latest AzSK module v.'$($serverVersion)' is not present. The version currently present is '$($currentModuleVersion)'"));
                }
                else
                {
                
                $controlResult.AddMessage([VerificationResult]::Passed,
                                    [MessageData]::new("Latest '$($AzSKModuleName)' module v.'$($currentModuleVersion)' is present"));    
                }
        return $controlResult
    }

    #Check if active schedules
    hidden [PSObject] GetActiveSchedules($runbookName)
    {
        
        $AutomationAccount=[Constants]::AutomationAccount
        $AzSKRGName=[ConfigurationManager]::GetAzSKConfigData().AzSKRGName
        $ScheduleName=[Constants]::ScheduleName
        $runbookSchedulesList = Get-AzAutomationScheduledRunbook -ResourceGroupName $AzSKRGName `
        -AutomationAccountName  $AutomationAccount `
        -RunbookName $runbookName -ErrorAction Stop
        if($runbookSchedulesList)
        {
            $schedules = Get-AzAutomationSchedule -ResourceGroupName $AzSKRGName `
            -AutomationAccountName  $AutomationAccount -Name $ScheduleName  | Where-Object{ $_.Name -eq $ScheduleName}
            $activeSchedule = $schedules | Where-Object{$_.IsEnabled -and `
            $_.Frequency -ne [Microsoft.Azure.Commands.Automation.Model.ScheduleFrequency]::Onetime -and `
            $_.ExpiryTime.UtcDateTime -gt $(get-date).ToUniversalTime()}

            return $activeSchedule
        }
        else
        {
            return $null
        }
    }
    
    hidden [PSObject] GetRunAsConnection()
    {
        
        $AutomationAccount=[Constants]::AutomationAccount
        $AzSKRGName=[ConfigurationManager]::GetAzSKConfigData().AzSKRGName
        $connectionAssetName=[Constants]::connectionAssetName
        $connection = Get-AzAutomationConnection -AutomationAccountName  $AutomationAccount `
            -Name  $connectionAssetName -ResourceGroupName `
            $AzSKRGName -ErrorAction SilentlyContinue
        if((Get-Member -InputObject $connection -Name FieldDefinitionValues -MemberType Properties) -and $connection.FieldDefinitionValues.ContainsKey("ApplicationId"))
        {
             $connection = $connection|Select-Object Name,Description,ConnectionTypeName,FieldDefinitionValues
             return $connection
        }
        else
        {
            return $null
        }
    }

    hidden [bool] CheckServicePrincipalRGAccess($applicationId)
    {
        
        $AzSKRGName=[ConfigurationManager]::GetAzSKConfigData().AzSKRGName
        $spPermissions = Get-AzRoleAssignment -serviceprincipalname $applicationId 
        #Check subscription access
        if(($spPermissions|Measure-Object).count -gt 0)
        {
            $haveRGAccess = ($spPermissions | Where-Object {$_.scope -eq (Get-AzResourceGroup -Name $AzSKRGName).ResourceId -and $_.RoleDefinitionName -eq "Contributor"}|measure-object).count -gt 0
            return $haveRGAccess    
        }
        else
        {
            return $false
        }
    
    }
    hidden [bool] CheckServicePrincipalSubscriptionAccess($applicationId)
    {
        #fetch SP permissions
        $spPermissions = Get-AzRoleAssignment -serviceprincipalname $applicationId 
        $currentContext = [ContextHelper]::GetCurrentRMContext();
        #Check subscription access
        if(($spPermissions|measure-object).count -gt 0)
        {
            $haveSubscriptionAccess = ($spPermissions | Where-Object {$_.scope -eq "/subscriptions/$($currentContext.Subscription.Id)" -and $_.RoleDefinitionName -eq "Reader"}|Measure-Object).count -gt 0
            return $haveSubscriptionAccess    
        }
        else
        {
            return $false
        }
    
    }
    
}