Framework/Listeners/OMS/OMSOutput.ps1
Set-StrictMode -Version Latest class OMSOutput: ListenerBase { hidden static [OMSOutput] $Instance = $null; #Default source is kept as SDL / PowerShell. #This value must be set in respective environment i.e. CICD,CC [string] $OMSSource; hidden static [bool] $IsIssueLogged = $false OMSOutput() { } static [OMSOutput] GetInstance() { if($null -eq [OMSOutput]::Instance) { [OMSOutput]::Instance = [OMSOutput]::new(); } return [OMSOutput]::Instance; } [void] RegisterEvents() { $this.UnregisterEvents(); # Mandatory: Generate Run Identifier Event $this.RegisterEvent([AzSdkRootEvent]::GenerateRunIdentifier, { $currentInstance = [OMSOutput]::GetInstance(); try { $currentInstance.SetRunIdentifier([AzSdkRootEventArgument] ($Event.SourceArgs | Select-Object -First 1)); [OMSOutput]::IsIssueLogged = $false } catch { $currentInstance.PublishException($_); } }); $this.RegisterEvent([SVTEvent]::ControlCompleted, { $currentInstance = [OMSOutput]::GetInstance(); try { $currentInstance.WriteControlResult([SVTEventContext] ($Event.SourceArgs | Select-Object -First 1)); } catch { $currentInstance.PublishException($_); } }); } hidden [void] WriteControlResult([SVTEventContext] $eventContext) { try { $settings = [ConfigurationManager]::GetAzSdkSettings() if(-not [string]::IsNullOrWhiteSpace($settings.OMSSource)) { $this.OMSSource = $settings.OMSSource } if(-not [string]::IsNullOrWhiteSpace($settings.OMSWorkspaceId)) { $tempBodyObjects = $this.GetOMSBodyObjects($this.OMSSource,$eventContext) #need to prioritize this $tempBodyObjects | ForEach-Object{ Set-Variable -Name tempBody -Value $_ -Scope Local $body = $tempBody | ConvertTo-Json $this.PostOMSData($settings.OMSWorkspaceId, $settings.OMSSharedKey, ([System.Text.Encoding]::UTF8.GetBytes($body)),$settings.OMSType) } } } catch { [Exception] $ex = [Exception]::new(("Invalid OMS Settings: " + $_.Exception.ToString()), $_.Exception) throw $ex } } hidden [PSObject[]] GetOMSBodyObjects([string] $Source,[SVTEventContext] $eventContext) { [PSObject[]] $output = @(); [array] $eventContext.ControlResults | ForEach-Object{ Set-Variable -Name ControlResult -Value $_ -Scope Local $out = "" | Select-Object ResourceType, ResourceGroup, Reference, ResourceName, ChildResourceName, ControlStatus, ActualVerificationResult, ControlId, SubscriptionName, SubscriptionId, FeatureName, Source, Recommendation, ControlSeverity, TimeTakenInMs, AttestationStatus, AttestedBy, Justification if($eventContext.IsResource()) { $out.ResourceType=$eventContext.ResourceContext.ResourceType $out.ResourceGroup=$eventContext.ResourceContext.ResourceGroupName $out.ResourceName=$eventContext.ResourceContext.ResourceName $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=$Source #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.TimeTakenInMs=[int] $Metrics["TimeTakenInMs"] $output += $out } return $output } hidden [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 } # Create the function to create and post the request hidden PostOMSData([string] $OMSWorkspaceID, [string] $SharedKey, $Body, $LogType) { try{ [string] $method = "POST" [string] $contentType = "application/json" [string] $resource = "/api/logs" $rfc1123date = [System.DateTime]::UtcNow.ToString("r") [int] $contentLength = $Body.Length [string] $signature = $this.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 } catch { if(-not [OMSOutput]::IsIssueLogged) { $this.PublishException($_); [OMSOutput]::IsIssueLogged = $true } } } } |