Framework/Helpers/OMSHelper.ps1

Set-StrictMode -Version Latest 
Class OMSHelper{
    static [string] $DefaultOMSType = "AzSK"
    # Create the function to create and post the request
    static PostOMSData([string] $OMSWorkspaceID, [string] $SharedKey, $Body, $LogType)
    {
        if([string]::IsNullOrWhiteSpace($LogType))
        {
            $LogType = [OMSHelper]::DefaultOMSType
        }
        [string] $method = "POST"
        [string] $contentType = "application/json"
        [string] $resource = "/api/logs"
        $rfc1123date = [System.DateTime]::UtcNow.ToString("r")
        [int] $contentLength = $Body.Length
        [string] $signature = [OMSHelper]::GetOMSSignature($OMSWorkspaceID , $SharedKey , $rfc1123date ,$contentLength ,$method ,$contentType ,$resource)
        [string] $uri = "https://" + $OMSWorkspaceID + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
        [DateTime] $TimeStampField = [System.DateTime]::UtcNow
        $headers = @{
            "Authorization" = $signature;
            "Log-Type" = $LogType;
            "x-ms-date" = $rfc1123date;
            "time-generated-field" = $TimeStampField;
        }
        $response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $Body -UseBasicParsing
    }

    static [string] GetOMSSignature ($OMSWorkspaceID, $SharedKey, $Date, $ContentLength, $Method, $ContentType, $Resource)
    {
            [string] $xHeaders = "x-ms-date:" + $Date
            [string] $stringToHash = $Method + "`n" + $ContentLength + "`n" + $ContentType + "`n" + $xHeaders + "`n" + $Resource
        
            [byte[]]$bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash)
            
            [byte[]]$keyBytes = [Convert]::FromBase64String($SharedKey)

            [System.Security.Cryptography.HMACSHA256] $sha256 = New-Object System.Security.Cryptography.HMACSHA256
            $sha256.Key = $keyBytes
            [byte[]]$calculatedHash = $sha256.ComputeHash($bytesToHash)
            $encodedHash = [Convert]::ToBase64String($calculatedHash)
            $authorization = 'SharedKey {0}:{1}' -f $OMSWorkspaceID,$encodedHash
            return $authorization   
    }

    static [PSObject[]] GetOMSBodyObjects([SVTEventContext] $eventContext,[AzSKContextDetails] $AzSKContext)
    {
        [PSObject[]] $output = @();
        [array] $eventContext.ControlResults | ForEach-Object{
            Set-Variable -Name ControlResult -Value $_ -Scope Local
            $out = [OMSModel]::new() 
            if($eventContext.IsResource())
            {
                $out.ResourceType=$eventContext.ResourceContext.ResourceType
                $out.ResourceGroup=$eventContext.ResourceContext.ResourceGroupName            
                $out.ResourceName=$eventContext.ResourceContext.ResourceName
                $out.ResourceId = $eventContext.ResourceContext.ResourceId
                $out.ChildResourceName=$ControlResult.ChildResourceName
            }

            $out.Reference=$eventContext.Metadata.Reference
            $out.ControlStatus=$ControlResult.VerificationResult.ToString()
            $out.ActualVerificationResult=$ControlResult.ActualVerificationResult.ToString()
            $out.ControlId=$eventContext.ControlItem.ControlID
            $out.SubscriptionName=$eventContext.SubscriptionContext.SubscriptionName
            $out.SubscriptionId=$eventContext.SubscriptionContext.SubscriptionId
            $out.FeatureName=$eventContext.FeatureName
            $out.Recommendation=$eventContext.ControlItem.Recommendation
            $out.ControlSeverity=$eventContext.ControlItem.ControlSeverity.ToString()
            $out.Source=$AzSKContext.Source
            $out.Tags=$eventContext.ControlItem.Tags
            $out.RunIdentifier = $AzSKContext.RunIdentifier
            $out.HasRequiredAccess = $ControlResult.CurrentSessionContext.Permissions.HasRequiredAccess 
            $out.ScannerVersion = $AzSKContext.Version
            $out.IsBaselineControl = $eventContext.ControlItem.IsBaselineControl
            $out.HasAttestationWritePermissions = $ControlResult.CurrentSessionContext.Permissions.HasAttestationWritePermissions
            $out.HasAttestationReadPermissions = $ControlResult.CurrentSessionContext.Permissions.HasAttestationReadPermissions                
            $out.IsLatestPSModule = $ControlResult.CurrentSessionContext.IsLatestPSModule
            #mapping the attestation properties
            if($null -ne $ControlResult -and $null -ne $ControlResult.StateManagement -and $null -ne $ControlResult.StateManagement.AttestedStateData)
            {
                $attestedData = $ControlResult.StateManagement.AttestedStateData;
                $out.AttestationStatus = $ControlResult.AttestationStatus.ToString();
                $out.AttestedBy = $attestedData.AttestedBy;
                $out.Justification = $attestedData.Justification;
                $out.AttestedDate = $attestedData.AttestedDate
                $out.ExpiryDate = $attestedData.ExpiryDate
            }
            $output += $out
        }
        return $output    
    }

    static [void] PostApplicableControlSet([SVTEventContext[]] $contexts,[AzSKContextDetails] $AzSKContext) {
        if (($contexts | Measure-Object).Count -lt 1) { return; }
        $set = [OMSHelper]::ConvertToSimpleSet($contexts,$AzSKContext);
        [OMSHelper]::WriteControlResult($set,"AzSK_Inventory")
        $omsMetadata = [ConfigurationManager]::LoadServerConfigFile("OMSSettings.json")
        [OMSHelper]::WriteControlResult($omsMetadata,"AzSK_MetaData")        
    }

    static [void] WriteControlResult([PSObject[]] $omsDataObject, [string] $OMSEventType)
    {
        try
        {
            $settings = [ConfigurationManager]::GetAzSKSettings()
            if([string]::IsNullOrWhiteSpace($OMSEventType))
            {
                $OMSEventType = $settings.OMSType
            }

            if((-not [string]::IsNullOrWhiteSpace($settings.OMSWorkspaceId)) -or (-not [string]::IsNullOrWhiteSpace($settings.AltOMSWorkspaceId)))
            {
                $omsDataObject | ForEach-Object{
                    Set-Variable -Name tempBody -Value $_ -Scope Local
                    $body = $tempBody | ConvertTo-Json
                    $omsBodyByteArray = ([System.Text.Encoding]::UTF8.GetBytes($body))
                    #publish to primary workspace
                    if(-not [string]::IsNullOrWhiteSpace($settings.OMSWorkspaceId))
                    {
                        [OMSHelper]::PostOMSData($settings.OMSWorkspaceId, $settings.OMSSharedKey, $omsBodyByteArray, $OMSEventType)
                    }
                    #publish to secondary workspace
                    if(-not [string]::IsNullOrWhiteSpace($settings.AltOMSWorkspaceId))
                    {
                        [OMSHelper]::PostOMSData($settings.AltOMSWorkspaceId, $settings.AltOMSSharedKey, $omsBodyByteArray, $OMSEventType)
                    }                
                }            
            }
        }
        catch
        {
            [Exception] $ex = [Exception]::new(("Invalid OMS Settings: " + $_.Exception.ToString()), $_.Exception)
            throw [SuppressedException] $ex
        }
    }

    static [PSObject[]] ConvertToSimpleSet($contexts,[AzSKContextDetails] $AzSKContext)
    {
        $ControlSet = [System.Collections.ArrayList]::new()
        foreach ($item in $contexts) {
            $set = [OMSResourceInvModel]::new()
            $set.RunIdentifier = $AzSKContext.RunIdentifier
            $set.SubscriptionId = $item.SubscriptionContext.SubscriptionId
            $set.SubscriptionName = $item.SubscriptionContext.SubscriptionName
            $set.Source = $AzSKContext.Source
            $set.ScannerVersion = $AzSKContext.Version
            $set.FeatureName = $item.FeatureName
            if([Helpers]::CheckMember($item,"ResourceContext"))
            {
                $set.ResourceGroupName = $item.ResourceContext.ResourceGroupName
                $set.ResourceName = $item.ResourceContext.ResourceName
                $set.ResourceId = $item.ResourceContext.ResourceId
            }
            $set.ControlIntId = $item.ControlItem.Id
            $set.ControlId = $item.ControlItem.ControlID
            $set.ControlSeverity = $item.ControlItem.ControlSeverity
            $set.Tags = $item.ControlItem.Tags
            $set.IsBaselineControl = $item.ControlItem.IsBaselineControl
            $ControlSet.Add($set) 
        }
        return $ControlSet;
    }

    #TODO: Find right place for this function
    static [void] SetOMSDetails()
    {
        #Check if Settings already contain details of OMS
        $settings = [ConfigurationManager]::GetAzSKSettings()
        #Step 1: if OMS details are not present on machine
        if([string]::IsNullOrWhiteSpace($settings.OMSWorkspaceId) -or [string]::IsNullOrWhiteSpace($settings.AltOMSWorkspaceId))
        {
            $rgName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName
            #Step 2: Validate if CA is enabled on subscription
            $automationAccDetails= Get-AzureRmAutomationAccount -ResourceGroupName $rgName -ErrorAction SilentlyContinue 
            if($automationAccDetails)
            {
                if([string]::IsNullOrWhiteSpace($settings.OMSWorkspaceId))
                {
                    #Step 3: Get workspace id from automation account variables
                    $omsWorkSpaceId = Get-AzureRmAutomationVariable -ResourceGroupName $automationAccDetails.ResourceGroupName -AutomationAccountName $automationAccDetails.AutomationAccountName -Name "OMSWorkspaceId" -ErrorAction SilentlyContinue
                    #Step 4: Get OMS with workspace id #TODO: Validate on sub where OMS not present
                    if($omsWorkSpaceId)
                    {
                        $omsSharedKey = Get-AzureRmAutomationVariable -ResourceGroupName $automationAccDetails.ResourceGroupName -AutomationAccountName $automationAccDetails.AutomationAccountName -Name "OMSSharedKey"                        
                        if([Helpers]::CheckMember($omsSharedKey,"Value") -and (-not [string]::IsNullOrWhiteSpace($omsSharedKey.Value)))
                        {
                            #Step 6: Assign it to AzSKSettings Object
                            $settings.OMSWorkspaceId = $omsWorkSpaceId.Value
                            $settings.OMSSharedKey = $omsSharedKey.Value
                        }                    

                    }
                }

                if([string]::IsNullOrWhiteSpace($settings.AltOMSWorkspaceId))
                {
                    #Step 3: Get workspace id from automation account variables
                    $omsWorkSpaceId = Get-AzureRmAutomationVariable -ResourceGroupName $automationAccDetails.ResourceGroupName -AutomationAccountName $automationAccDetails.AutomationAccountName -Name "AltOMSWorkspaceId" -ErrorAction SilentlyContinue
                    #Step 4: Get OMS with workspace id #TODO: Validate on sub where OMS not present
                    if($omsWorkSpaceId)
                    {
                        $omsSharedKey = Get-AzureRmAutomationVariable -ResourceGroupName $automationAccDetails.ResourceGroupName -AutomationAccountName $automationAccDetails.AutomationAccountName -Name "AltOMSSharedKey"                        
                        if([Helpers]::CheckMember($omsSharedKey,"Value") -and (-not [string]::IsNullOrWhiteSpace($omsSharedKey.Value)))
                        {
                            #Step 6: Assign it to AzSKSettings Object
                            $settings.AltOMSWorkspaceId = $omsWorkSpaceId.Value
                            $settings.AltOMSSharedKey = $omsSharedKey.Value
                        }
                    }
                }                
            }
        }        
    }

    static PostResourceInventory([AzSKContextDetails] $AzSKContext)
    {
        if($AzSKContext.Source.Equals("CC", [System.StringComparison]::OrdinalIgnoreCase) -or
        $AzSKContext.Source.Equals("CA", [System.StringComparison]::OrdinalIgnoreCase)){
            $resourceSet = [System.Collections.ArrayList]::new()
            $resourcesFlat = Find-AzureRmResource
            $supportedResourceTypes = [SVTMapping]::GetSupportedResourceMap()
            # Not considering nested resources to reduce complexity
            $filteredResoruces = $resourcesFlat | where { $supportedResourceTypes.ContainsKey($_.ResourceType.ToLower()) }
            foreach($resource in $filteredResoruces){
                $set = [OMSResourceModel]::new()
                $set.RunIdentifier = $AzSKContext.RunIdentifier
                $set.SubscriptionId = $resource.SubscriptionId
                #$set.SubscriptionName = $item.SubscriptionContext.SubscriptionName
                $set.Source = $AzSKContext.Source
                $set.ScannerVersion = $AzSKContext.Version
                $set.ResourceType = $resource.ResourceType                
                $set.ResourceGroupName = $resource.ResourceGroupName
                $set.ResourceName = $resource.Name
                $set.ResourceId = $resource.ResourceId

            $resourceSet.Add($set) 
        }
            [OMSHelper]::WriteControlResult($resourceSet,"AzSK_Inventory")
            $omsMetadata = [ConfigurationManager]::LoadServerConfigFile("OMSSettings.json")
            [OMSHelper]::WriteControlResult($omsMetadata,"AzSK_MetaData")
        }            
    }

}



Class OMSModel {
    [string] $RunIdentifier
    [string] $ResourceType 
    [string] $ResourceGroup 
    [string] $Reference
    [string] $ResourceName 
    [string] $ChildResourceName 
    [string] $ResourceId
    [string] $ControlStatus 
    [string] $ActualVerificationResult 
    [string] $ControlId 
    [string] $SubscriptionName 
    [string] $SubscriptionId 
    [string] $FeatureName 
    [string] $Source 
    [string] $Recommendation 
    [string] $ControlSeverity 
    [string] $TimeTakenInMs 
    [string] $AttestationStatus 
    [string] $AttestedBy 
    [string] $Justification 
    [string] $AttestedDate
    [bool] $HasRequiredAccess
    [bool] $HasAttestationWritePermissions
    [bool] $HasAttestationReadPermissions
    [bool] $IsLatestPSModule
    [string[]] $Tags
    [string] $ScannerVersion
    [bool] $IsBaselineControl
    [string] $ExpiryDate
}

Class OMSResourceInvModel{
    [string] $RunIdentifier
    [string] $SubscriptionId
    [string] $SubscriptionName
    [string] $Source
    [string] $ScannerVersion
    [string] $FeatureName
    [string] $ResourceGroupName
    [string] $ResourceName
    [string] $ResourceId
    [string] $ControlId
    [string] $ControlIntId
    [string] $ControlSeverity
    [string[]] $Tags
    [bool] $IsBaselineControl
}

Class OMSResourceModel{
    [string] $RunIdentifier
    [string] $SubscriptionId
    [string] $Source
    [string] $ScannerVersion
    [string] $ResourceType
    [string] $ResourceGroupName
    [string] $ResourceName
    [string] $ResourceId
}

Class AzSKContextDetails {
    [string] $RunIdentifier
    [string] $Version
    [string] $Source
    }