Framework/Abstracts/SVTCommandBase.ps1

using namespace System.Management.Automation
Set-StrictMode -Version Latest
# Base class for SVT classes being called from PS commands
# Provides functionality to fire important events at command call
class SVTCommandBase: CommandBase
{    
    [string[]] $ExcludeTags = @();
    [string[]] $ControlIds = @();
    [AttestControls] $AttestControls = [AttestControls]::None;
    [bool] $UsePartialCommits;
    [bool] $UseBaselineControls;
    hidden [ControlStateExtension] $ControlStateExt;
    hidden [bool] $UserHasStateAccess = $false;
    [bool] $GenerateFixScript = $false;
    
    SVTCommandBase([string] $subscriptionId, [InvocationInfo] $invocationContext): 
        Base($subscriptionId, $invocationContext)
    { 
        [Helpers]::AbstractClass($this, [SVTCommandBase]);
    }

    hidden [SVTEventContext] CreateSVTEventContextObject() 
    {
        return [SVTEventContext]@{
            SubscriptionContext = $this.SubscriptionContext;
        };
    }

    hidden [void] CommandStarted()
    {
        [SVTEventContext] $arg = $this.CreateSVTEventContextObject();

        $versionMessage = $this.CheckModuleVersion();
        if($versionMessage)
        {
            $arg.Messages += $versionMessage;
        }

        $this.PublishEvent([SVTEvent]::CommandStarted, $arg);    
    }

    hidden [void] CommandError([System.Management.Automation.ErrorRecord] $exception)
    {
        [SVTEventContext] $arg = $this.CreateSVTEventContextObject();
        $arg.ExceptionMessage = $exception;

        $this.PublishEvent([SVTEvent]::CommandError, $arg);    
    }

    hidden [void] CommandCompleted([SVTEventContext[]] $arguments) 
    {
        $this.PublishEvent([SVTEvent]::CommandCompleted, $arguments);
    }

    [string] InvokeFunction([PSMethod] $methodToCall)
    {
        throw [System.NotSupportedException] "The method is not supported in the class"
    }

    [string] EvaluateControlStatus()
    {
        return ([CommandBase]$this).InvokeFunction($this.RunAllControls);
    }
    
    # Dummy function declaration to define the function signature
    # Function is supposed to override in derived class
    hidden [SVTEventContext[]] RunAllControls()
    {
        return @();
    }

    hidden [void] SetSVTBaseProperties([PSObject] $svtObject)
    {
        $svtObject.FilterTags = $this.ConvertToStringArray($this.FilterTags);
        $svtObject.ExcludeTags = $this.ConvertToStringArray($this.ExcludeTags);
        $svtObject.ControlIds = $this.ConvertToStringArray($this.ControlIds);
        $svtObject.GenerateFixScript = $this.GenerateFixScript;

        $this.InitializeControlState();

        if($this.UserHasStateAccess)
        {
            $svtObject.ControlStateExt = $this.ControlStateExt;
        }
    }

    hidden [void] InitializeControlState()
    {
        if(-not $this.ControlStateExt)
        {
            $this.ControlStateExt = [ControlStateExtension]::new();
            $this.ControlStateExt.Initialize($false);
            $this.UserHasStateAccess = $this.ControlStateExt.HasControlStateReadAccessPermissions();
        }
    }

    [void] PostCommandCompletedAction([SVTEventContext[]] $arguments)
    { 
        if($this.AttestControls -ne [AttestControls]::None)
        {
            try 
            {
                [SVTControlAttestation] $svtControlAttestation = [SVTControlAttestation]::new($arguments, $this.AttestControls);
                #The current context user would be able to read the storage blob only if he has minimum of contributor access.
                if($svtControlAttestation.controlStateExtension.HasControlStateReadAccessPermissions())
                {                
                    $this.PublishCustomMessage([Constants]::HashLine + "`nStarting Control Attestation workflow...`n" + [Constants]::SingleDashLine);
                    [MessageData] $data = [MessageData]@{                            
                            Message = "`nPlease use utmost discretion when deciding to attest to controls. In effect, you are taking accountability that nothing will go wrong even though security is not fully configured.";
                            MessageType = [MessageType]::Warning;
                    };                    
                    $this.PublishCustomMessage($data)
                    $response = ""
                    while($response.Trim() -ne "y" -and $response.Trim() -ne "n")
                    {
                        if(-not [string]::IsNullOrEmpty($response))
                        {
                            Write-Host "Please select appropriate option."
                        }
                        $response = Read-Host "Do you want to continue (Y/N)?"
                    }
                    if($response.Trim() -eq "y")
                    {
                        $svtControlAttestation.StartControlAttestation();
                    }
                    else
                    {
                        $this.PublishCustomMessage("Exited the control attestation process.")
                    }
                }
                else
                {
                     [MessageData] $data = [MessageData]@{
                            Message = "You don't have the required permissions to perform control attestation. If you'd like to perform control attestation, please request your subscription owner to grant you 'Contributor' access to the 'AzSDKRG' resource group.";
                            MessageType = [MessageType]::Error;
                    };
                    $this.PublishCustomMessage($data)
                }
            }
            catch 
            {
                $this.CommandError($_);
            }  
        }
    }
}