Framework/Listeners/RemoteReports/ControlTelemetry.ps1

Set-StrictMode -Version Latest

class ControlTelemetry: ListenerBase {
    [Microsoft.ApplicationInsights.TelemetryClient] $TelemetryClient;

    hidden ControlTelemetry() {
        $this.TelemetryClient = [Microsoft.ApplicationInsights.TelemetryClient]::new()
    }

    hidden static [ControlTelemetry] $Instance = $null;

    static [ControlTelemetry] GetInstance() {
        if ( $null  -eq [ControlTelemetry]::Instance -or  $null  -eq [ControlTelemetry]::Instance.TelemetryClient) {
            [ControlTelemetry]::Instance = [ControlTelemetry]::new();
        }
        return [ControlTelemetry]::Instance
    }

    [void] RegisterEvents() {
        $this.UnregisterEvents();

        $this.RegisterEvent([AzSdkRootEvent]::GenerateRunIdentifier, {
            $currentInstance = [ControlTelemetry]::GetInstance();
            try
            {
                $runIdentifier = [AzSdkRootEventArgument] ($Event.SourceArgs | Select-Object -First 1)
                $currentInstance.SetRunIdentifier($runIdentifier);
            }
            catch
            {
                $currentInstance.PublishException($_);
            }
        });

        $this.RegisterEvent([SVTEvent]::EvaluationCompleted, {

            if(![RemoteReportHelper]::IsControlTelemetryEnabled()){ return; };

            $currentInstance = [ControlTelemetry]::GetInstance();
            $currentInstance.TelemetryClient.InstrumentationKey = [RemoteReportHelper]::GetControlTelemetryKey();
            try
            {
                $invocationContext = [System.Management.Automation.InvocationInfo] $currentInstance.InvocationContext
                $featureGroup = [RemoteReportHelper]::GetFeatureGroup($invocationContext.MyCommand.Name)
                $SVTEventContexts = [SVTEventContext[]] $Event.SourceArgs
                if($featureGroup -eq [FeatureGroup]::Subscription){
                    [ControlTelemetry]::PushSubscriptionScanResults($currentInstance, $SVTEventContexts)
                }elseif($featureGroup -eq [FeatureGroup]::Service){
                    [ControlTelemetry]::PushServiceScanResults($currentInstance, $SVTEventContexts)
                }else{

                }
            }
            catch
            {
                $currentInstance.PublishException($_);
            }
            $currentInstance.TelemetryClient.Flush()
        });
    }

    static [void] PushSubscriptionScanResults(
        [ControlTelemetry] $Publisher, `
        [SVTEventContext[]] $SVTEventContexts)
    {
        $eventData = @{
            [TelemetryKeys]::FeatureGroup = [FeatureGroup]::Subscription;
            "ScanKind" = [RemoteReportHelper]::GetSubscriptionScanKind(
                $Publisher.InvocationContext.MyCommand.Name,
                $Publisher.InvocationContext.BoundParameters);
        }
        $SVTEventContexts | ForEach-Object {
            $context = $_
            [hashtable] $eventDataClone = $eventData.Clone();
            $eventDataClone.Add("ControlIntId", $context.ControlItem.Id);
            $eventDataClone.Add("ControlId", $context.ControlItem.ControlID);
            $eventDataClone.Add("ControlSeverity", $context.ControlItem.ControlSeverity);
            if ($context.ControlItem.Enabled) {
                $eventDataClone.Add("ActualVerificationResult", $context.ControlResults[0].ActualVerificationResult)
                $eventDataClone.Add("AttestationStatus", $context.ControlResults[0].AttestationStatus)
                $eventDataClone.Add("VerificationResult", $context.ControlResults[0].VerificationResult)
            }
            else {
                $eventDataClone.Add("ActualVerificationResult", [VerificationResult]::Disabled)
                $eventDataClone.Add("AttestationStatus", [AttestationStatus]::None)
                $eventDataClone.Add("VerificationResult", [VerificationResult]::Disabled)
            }
            [ControlTelemetry]::PushEvent($Publisher, $eventDataClone, @{})
        }
    }

    static [void] PushServiceScanResults(
        [ControlTelemetry] $Publisher, `
        [SVTEventContext[]] $SVTEventContexts)
    {
        $NA = "NA"
        $SVTEventContextFirst = $SVTEventContexts[0]
        $eventData = @{
            [TelemetryKeys]::FeatureGroup = [FeatureGroup]::Service;
            "ScanKind" = [RemoteReportHelper]::GetServiceScanKind(
                $Publisher.InvocationContext.MyCommand.Name,
                $Publisher.InvocationContext.BoundParameters);
            "Feature" = $SVTEventContextFirst.FeatureName;
            "ResourceGroup" = $SVTEventContextFirst.ResourceContext.ResourceGroupName;
            "ResourceName" = $SVTEventContextFirst.ResourceContext.ResourceName;
            "ResourceId" = $SVTEventContextFirst.ResourceContext.ResourceId;
        }
        $SVTEventContexts | ForEach-Object {
            $SVTEventContext = $_
            [hashtable] $eventDataClone = $eventData.Clone()
            $eventDataClone.Add("ControlIntId", $SVTEventContext.ControlItem.Id);
            $eventDataClone.Add("ControlId", $SVTEventContext.ControlItem.ControlID);
            $eventDataClone.Add("ControlSeverity", $SVTEventContext.ControlItem.ControlSeverity);
            if (!$SVTEventContext.ControlItem.Enabled) {
                $eventDataClone.Add("VerificationResult", [VerificationResult]::Disabled)
                $eventDataClone.Add("AttestationStatus", [AttestationStatus]::None)
                $eventDataClone.Add("VerificationResult", [VerificationResult]::Disabled)
                [ControlTelemetry]::PushEvent($Publisher, $eventDataClone, @{})
            }
            elseif ($SVTEventContext.ControlResults.Count -eq 1 -and `
                ($SVTEventContextFirst.ResourceContext.ResourceName -eq $SVTEventContext.ControlResults[0].ChildResourceName -or `
                    [string]::IsNullOrWhiteSpace($SVTEventContext.ControlResults[0].ChildResourceName)))
            {
                $eventDataClone.Add("ActualVerificationResult", $SVTEventContext.ControlResults[0].ActualVerificationResult)
                $eventDataClone.Add("AttestationStatus", $SVTEventContext.ControlResults[0].AttestationStatus)
                $eventDataClone.Add("VerificationResult", $SVTEventContext.ControlResults[0].VerificationResult)
                $eventDataClone.Add("IsNestedResource", 'No')
                $eventDataClone.Add("NestedResourceName", $NA)
                [ControlTelemetry]::PushEvent($Publisher, $eventDataClone, @{})
            }
            elseif ($SVTEventContext.ControlResults.Count -eq 1 -and `
                $SVTEventContextFirst.ResourceContext.ResourceName -ne $SVTEventContext.ControlResults[0].ChildResourceName)
            {
                $eventDataClone.Add("ActualVerificationResult", $SVTEventContext.ControlResults[0].ActualVerificationResult)
                $eventDataClone.Add("AttestationStatus", $SVTEventContext.ControlResults[0].AttestationStatus)
                $eventDataClone.Add("VerificationResult", $SVTEventContext.ControlResults[0].VerificationResult)
                $eventDataClone.Add("IsNestedResource", 'Yes')
                $eventDataClone.Add("NestedResourceName", $SVTEventContext.ControlResults[0].ChildResourceName)
                [ControlTelemetry]::PushEvent($Publisher, $eventDataClone, @{})
            }
            elseif ($SVTEventContext.ControlResults.Count -gt 1)
            {
                $eventDataClone.Add("IsNestedResource", 'Yes')
                $SVTEventContext.ControlResults | Foreach-Object {
                    [hashtable] $eventDataCloneL2 = $eventDataClone.Clone()
                    $eventDataCloneL2.Add("ActualVerificationResult", $_.ActualVerificationResult)
                    $eventDataCloneL2.Add("AttestationStatus", $_.AttestationStatus)
                    $eventDataCloneL2.Add("VerificationResult", $_.VerificationResult)
                    $eventDataCloneL2.Add("NestedResourceName", $_.ChildResourceName)
                    [ControlTelemetry]::PushEvent($Publisher, $eventDataCloneL2, @{})
                }
            }
        }
    }

    static [void] PushEvent([ControlTelemetry] $Publisher, `
                            [hashtable] $Properties, [hashtable] $Metrics)
    {
        try{
            [ControlTelemetry]::SetCommonProperties($Publisher, $Properties);
            $event = [Microsoft.ApplicationInsights.DataContracts.EventTelemetry]::new()
            $event.Name = "Control Scanned"
            $Properties.Keys | ForEach-Object {
                try{
                    $event.Properties.Add($_, $Properties[$_].ToString());
                }
                catch{
                }
            }
            $Metrics.Keys | ForEach-Object {
                try{
                    $event.Metrics.Add($_, $Metrics[$_]);
                }
                catch{
                }
            }
            $Publisher.TelemetryClient.TrackEvent($event);
        }
        catch{
        }
    }

    hidden static [void] SetCommonProperties([ControlTelemetry] $Publisher, [hashtable] $Properties)
    {
        try{
            $NA = "NA";
            $Properties.Add("InfoVersion", "V1");
            try{
                $Properties.Add("ScanSource", [RemoteReportHelper]::GetScanSource());
            }catch{}
            try{
                $Properties.Add("ScannerVersion", $Publisher.GetCurrentModuleVersion());
            }catch{}
            try{
                $Properties.Add("ControlVersion", $Publisher.GetCurrentModuleVersion());
            }catch{}
            try{
                $azureContext = Get-AzureRmContext
                try{
                    $Properties.Add([TelemetryKeys]::SubscriptionId, $azureContext.Subscription.SubscriptionId)
                }catch{}
                try{
                    $Properties.Add([TelemetryKeys]::SubscriptionName, $azureContext.Subscription.SubscriptionName)
                }catch{}
                try{
                    $Properties.Add("AzureEnv", $azureContext.Environment.Name)
                }catch{}
                try{
                    $Properties.Add("TenantId", $azureContext.Tenant.TenantId)
                }catch{}
                try{
                    $Properties.Add("AccountId", $azureContext.Account.Id)
                }catch{}
                try{
                    $Properties.Add("RunIdentifier",  [RemoteReportHelper]::Mask($azureContext.Account.Id + '##' + $Publisher.RunIdentifier));
                }catch{
                    $Properties.Add("RunIdentifier",  $Publisher.RunIdentifier);
                }
                try{
                    $Properties.Add("AccountType", $azureContext.Account.AccountType)
                }catch{}
            }catch{
            }
        }
        catch{
        }
    }
}