Framework/Abstracts/CommandBase.ps1

using namespace System.Management.Automation
Set-StrictMode -Version Latest
# Base class for all classes being called from PS commands
# Provides functionality to fire important events at command call
class CommandBase: AzSdkRoot {
    [string[]] $FilterTags = @();
    [bool] $DoNotOpenOutputFolder = $false;
    CommandBase([string] $subscriptionId, [InvocationInfo] $invocationContext):
    Base($subscriptionId) {
        [Helpers]::AbstractClass($this, [CommandBase]);
        if (-not $invocationContext) {
            throw [System.ArgumentException] ("The argument 'invocationContext' is null. Pass the `$PSCmdlet.MyInvocation from PowerShell command.");
        }
        $this.InvocationContext = $invocationContext;
        if($this.InvocationContext.BoundParameters["DoNotOpenOutputFolder"] -ne $Null)
        {
            $this.DoNotOpenOutputFolder = $this.InvocationContext.BoundParameters["DoNotOpenOutputFolder"];
        }
    }

    [void] CommandStarted() {
        $this.PublishAzSdkRootEvent([AzSdkRootEvent]::CommandStarted, $this.CheckModuleVersion());
    }

    [void] CommandError([System.Management.Automation.ErrorRecord] $exception) {
        [AzSdkRootEventArgument] $arguments = $this.CreateRootEventArgumentObject();
        $arguments.ExceptionMessage = $exception;

        $this.PublishEvent([AzSdkRootEvent]::CommandError, $arguments);
    }

    [void] CommandCompleted([MessageData[]] $messages) {
        $this.PublishAzSdkRootEvent([AzSdkRootEvent]::CommandCompleted, $messages);
    }

    [string] InvokeFunction([PSMethod] $methodToCall) {
        return $this.InvokeFunction($methodToCall, @());
    }

    [string] InvokeFunction([PSMethod] $methodToCall, [System.Object[]] $arguments) {
        if (-not $methodToCall) {
            throw [System.ArgumentException] ("The argument 'methodToCall' is null. Pass the reference of method to call. e.g.: [YourClass]::new().YourMethod");
        }

        $this.PublishRunIdentifier($this.InvocationContext);
        [AppInsightsHelper]::TrackCommandExecution("Command Started",
            @{"RunIdentifier" = $this.RunIdentifier}, @{}, $this.InvocationContext);
        $sw = [System.Diagnostics.Stopwatch]::StartNew();
        $isExecutionSuccessful = $true
        $this.CommandStarted();
        $methodResult = @();
        try {
            $methodResult = $methodToCall.Invoke($arguments);
        }
        catch {
            $isExecutionSuccessful = $true
            # Unwrapping the first layer of exception which is added by Invoke function
            [AppInsightsHelper]::TrackCommandExecution("Command Errored",
                @{"RunIdentifier" = $this.RunIdentifier; "ErrorRecord"= $_.Exception.InnerException.ErrorRecord},
                @{"TimeTakenInMs" = $sw.ElapsedMilliseconds; "SuccessCount" = 0},
                $this.InvocationContext);
            $this.CommandError($_.Exception.InnerException.ErrorRecord);
        }

        $this.CommandCompleted($methodResult);
        [AppInsightsHelper]::TrackCommandExecution("Command Completed",
            @{"RunIdentifier" = $this.RunIdentifier},
            @{"TimeTakenInMs" = $sw.ElapsedMilliseconds; "SuccessCount" = 1},
            $this.InvocationContext)
        $this.PostCommandCompletedAction($methodResult);

        $folderPath = $this.GetOutputFolderPath();

        #Generate PDF report
        $GeneratePDFReport = $this.InvocationContext.BoundParameters["GeneratePDF"];

        try {
            if (-not [string]::IsNullOrEmpty($folderpath)) {
                switch ($GeneratePDFReport) {
                    None {
                        # Do nothing
                    }
                    Landscape {
                        [AzSDKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $true);
                    }
                    Portrait {
                        [AzSDKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $false);
                    }
                }
            }
        }
        catch {
            # Unwrapping the first layer of exception which is added by Invoke function
            $this.CommandError($_);
        }

        $AttestControlParamFound = $this.InvocationContext.BoundParameters["AttestControls"];
        if($null -eq $AttestControlParamFound)
        {
            if((-not $this.DoNotOpenOutputFolder) -and (-not [string]::IsNullOrEmpty($folderPath)))
            {
                try
                {
                    Invoke-Item -Path $folderPath;
                }
                catch
                {
                    #ignore if any exception occurs
                }
            }
        }
        return $folderPath;
    }

    [string] GetOutputFolderPath() {
        return [WriteFolderPath]::GetInstance().FolderPath;
    }


    [MessageData[]] CheckModuleVersion() {
        $serverVersion = [System.Version] ([ConfigurationManager]::GetAzSdkConfigData().GetLatestAzSDKVersion($this.GetModuleName()));
        if ($serverVersion -gt $this.GetCurrentModuleVersion()) {
            
             [MessageData[]] $versionMessages = @();
             $versionMessages += [MessageData]::new(([Constants]::VersionCheckMessage -f $serverVersion), [MessageType]::Warning);
             $versionMessages += [MessageData]::new(([ConfigurationManager]::GetAzSdkConfigData().InstallationCommand + "`r`n"), [MessageType]::Update);
             $versionMessages += [MessageData]::new([Constants]::VersionWarningMessage, [MessageType]::Warning);
             return $versionMessages;
        }

        return $null;
    }

    [void] CommandProgress([int] $totalItems, [int] $currentItem) {
        $this.CommandProgress($totalItems, $currentItem, 1);
    }

    [void] CommandProgress([int] $totalItems, [int] $currentItem, [int] $granularity) {
        if ($totalItems -gt 0) {
            # $granularity indicates the number of items after which percentage progress will be printed
            # Set the max granularity to total items
            if ($granularity -gt $totalItems) {
                $granularity = $totalItems;
            }

            # Conditions for posting progress: 0%, 100% and based on granularity
            if ($currentItem -eq 0 -or $currentItem -eq $totalItems -or (($currentItem % $granularity) -eq 0)) {
                $this.PublishCustomMessage("$([int](($currentItem / $totalItems) * 100))% Completed");
            }
        }
    }

    # Dummy function declaration to define the function signature
    [void] PostCommandCompletedAction([MessageData[]] $messages)
    { }
}