PsiPS.AzureAutomation.psm1


#Requires -Modules @{ ModuleName = 'AzureRM.Automation'; ModuleVersion = '6.1.1' };

$script:ModuleRoot = $PSScriptRoot;
$script:ModuleName = $MyInvocation.MyCommand.Name.Replace('.psm1', '');
$script:FeatureFlags = $MyInvocation.MyCommand.Module.PrivateData.FeatureFlags;
$script:RequiredModules = $MyInvocation.MyCommand.Module.RequiredModules;
$script:CurrentUserPrincipal = [Security.Principal.WindowsPrincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent());
$script:IsUserAdmin = $CurrentUserPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator);

$script:TabWidth = 4;
$script:TabString = ' ' * $script:TabWidth;

# Make sure to double up on any ` (backtick) characters that exist in the ASCII art.
$script:ModuleASCIIArt = @"
 
$script:TabString _____ _ _____ _____
$script:TabString| __ \ (_) __ \ / ____|
$script:TabString| |__) |__ _| |__) | (___
$script:TabString| ___/ __| | ___/ \___ \ _ _ _
$script:TabString| | /\\__ \ | | ____) | /\ | | | | (_)
$script:TabString|_|/ \___/_|_|_ |_____/___ / \ _ _| |_ ___ _ __ ___ __ _| |_ _ ___ _ __
$script:TabString / /\ \ |_ / | | | '__/ _ \ / /\ \| | | | __/ _ \| '_ `` _ \ / _`` | __| |/ _ \| '_ \
$script:TabString / ____ \ / /| |_| | | | __/ / ____ \ |_| | || (_) | | | | | | (_| | |_| | (_) | | | |
$script:TabString/_/ \_\/___|\__,_|_| \___| /_/ \_\__,_|\__\___/|_| |_| |_|\__,_|\__|_|\___/|_| |_|
 
"@
;

function Format-TimeSpan
{
    [CmdletBinding()]
    [OutputType([string])]
    param (
        [Parameter(Mandatory)]
        [TimeSpan] $Elapsed
    );

    $StringBuilder = [Text.StringBuilder]::new();

    if ($Elapsed.Hours -gt 0)
    {
        $StringBuilder.Append("$($Elapsed.Hours.ToString().PadLeft(2, '0'))h ") | Out-Null;
    }

    if ($Elapsed.Minutes -gt 0)
    {
        $StringBuilder.Append("$($Elapsed.Minutes.ToString().PadLeft(2, '0'))m ") | Out-Null;
    }

    if ($Elapsed.Seconds -gt 0 -and $Elapsed.Milliseconds -gt 0)
    {
        $StringBuilder.Append("$($Elapsed.Seconds.ToString().PadLeft(2, '0')).") | Out-Null;
        $StringBuilder.Append("$($Elapsed.Milliseconds.ToString().PadRight(4, '0'))s") | Out-Null;
    }
    elseif ($Elapsed.Seconds -eq 0 -and $Elapsed.Milliseconds -gt 0)
    {
        $StringBuilder.Append("00.$($Elapsed.Milliseconds.ToString().PadRight(4, '0'))s") | Out-Null;
    }
    elseif ($Elapsed.Seconds -gt 0 -and $Elapsed.Milliseconds -eq 0)
    {
        $StringBuilder.Append("$($Elapsed.Seconds.ToString().PadLeft(2, '0'))s") | Out-Null;
    }

    return $StringBuilder.ToString().Trim();
}

function Get-ModuleInformation
{
    [CmdletBinding()]
    param (
        [Parameter()]
        [switch] $IncludeFunctions
    );

    process
    {
        Write-Host $script:ModuleASCIIArt;

        $ModuleInfo = Get-Module -Name $script:ModuleName `
            | Select-Object -Property Name, Version, Author, CompanyName, Description, ExportedFunctions;

        Write-Host "$($script:TabString)Name: $($ModuleInfo.Name) v$($ModuleInfo.Version)";
        Write-Host "$($script:TabString)Author: $($ModuleInfo.Author), $($ModuleInfo.CompanyName)";
        Write-Host "$($script:TabString)Description: $($ModuleInfo.Description)`n";

        if ($IncludeFunctions.IsPresent)
        {
            $ExportedFunctions = ($ModuleInfo.ExportedFunctions.Keys | Sort-Object) -join "`n$($script:TabString * 2)";
            Write-Host "$($script:TabString)Functions:`n$($script:TabString * 2)$ExportedFunctions";
        }
    }
}


function Get-RunbookJobHistory
{
    [CmdletBinding(DefaultParameterSetName = 'All')]
    [OutputType([Collections.Generic.List[Microsoft.Azure.Commands.Automation.Model.Job]], ParameterSetName = 'All')]
    [OutputType([Collections.Generic.List[Microsoft.Azure.Commands.Automation.Model.Job]], ParameterSetName = 'ByName')]
    [OutputType([Microsoft.Azure.Commands.Automation.Model.Job], ParameterSetName = 'ById')]
    param (
        [Parameter(Mandatory, ParameterSetName = 'All')]
        [Parameter(Mandatory, ParameterSetName = 'ByName')]
        [Parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $ResourceGroupName,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [Parameter(Mandatory, ParameterSetName = 'ByName')]
        [Parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $AutomationAccountName,

        [Parameter(Mandatory, ParameterSetName = 'ByName')]
        [string] $RunbookName,

        [Parameter(Mandatory, ParameterSetName = 'ById')]
        [string] $JobId,

        [Parameter(ParameterSetName = 'All')]
        [Parameter(ParameterSetName = 'ByName')]
        [ValidateSet('Activating', 'Completed', 'Failed', 'Queued', 'Resuming',
            'Running', 'Starting', 'Stopped', 'Stopping', 'Suspended', 'Suspending')]
        [string] $Status,

        [Parameter(ParameterSetName = 'All')]
        [Parameter(ParameterSetName = 'ByName')]
        [ValidateScript({
            $ThirtyDaysAgo = [DateTimeOffset]::new((Get-Date).AddDays(-30));

            if ($ThirtyDaysAgo -gt $_)
            {
                throw ("StartDateTime must be greater than, or equal to, 30 days ago.`n" +
                    "Received: $($_.ToString()); Expected >=: $($ThirtyDaysAgo.ToString())");

                return $false;
            }

            return $true;
        })]
        [DateTimeOffset] $StartDateTime,

        [Parameter(ParameterSetName = 'All')]
        [Parameter(ParameterSetName = 'ByName')]
        [ValidateScript({
            $Now = [DateTimeOffset]::new((Get-Date));

            if ($Now -lt $_)
            {
                throw ("EndDateTime must be less than, or equal to, now.`n" +
                    "Received: $($_.ToString()); Expected >=: $($Now.ToString())");

                return $false;
            }

            return $true;
        })]
        [DateTimeOffset] $EndDateTime,

        [Parameter(ParameterSetName = 'All')]
        [Parameter(ParameterSetName = 'ByName')]
        [switch] $FullDetails,

        [Parameter(ParameterSetName = 'All')]
        [Parameter(ParameterSetName = 'ByName')]
        [switch] $ShowProgress
    );

    $RunbookJobs = [Collections.Generic.List[Microsoft.Azure.Commands.Automation.Model.Job]]::new();

    $BaseParameterKeys = @('ResourceGroupName', 'AutomationAccountName');

    $GetAzureRmAutomationJobParameters = @{
        ResourceGroupName = $ResourceGroupName;
        AutomationAccountName = $AutomationAccountName;
    };

    switch ($PSCmdlet.ParameterSetName)
    {
        'ById' `
        {
            $GetAzureRmAutomationJobParameters.Id = $JobId;

            $RunbookJobs = Get-AzureRmAutomationJob @GetAzureRmAutomationJobParameters;
        }

        'ByName' `
        {
            $GetAzureRmAutomationJobParameters.RunbookName = $RunbookName;

            if (-not [String]::IsNullOrEmpty($Status))
            {
                $GetAzureRmAutomationJobParameters.Status = $Status;
            }

            if ($null -ne $StartDateTime)
            {
                $GetAzureRmAutomationJobParameters.StartTime = $StartDateTime;
            }

            if ($null -ne $EndDateTime)
            {
                $GetAzureRmAutomationJobParameters.EndTime = $EndDateTime;
            }

            $RunbookJobs = Get-AzureRmAutomationJob @GetAzureRmAutomationJobParameters;
        }

        Default `
        {
            if (-not [String]::IsNullOrEmpty($Status))
            {
                $GetAzureRmAutomationJobParameters.Status = $Status;
            }

            if ($null -ne $StartDateTime)
            {
                $GetAzureRmAutomationJobParameters.StartTime = $StartDateTime;
            }

            if ($null -ne $EndDateTime)
            {
                $GetAzureRmAutomationJobParameters.EndTime = $EndDateTime;
            }

            $RunbookJobs = Get-AzureRmAutomationJob @GetAzureRmAutomationJobParameters;
        }
    }

    if ($FullDetails.IsPresent)
    {
        if ($ShowProgress.IsPresent)
        {
            $WriteProgressParameters = @{
                Activity = "Getting Runbook Job History Details...";
                StatusMessage = { "Querying '$($RunbookJobs[$RunbookJobIndex].RunbookName)' (Id: $($RunbookJobs[$RunbookJobIndex].JobId.Guid))"; };
                PercentComplete = { [Math]::Round((100 * ($RunbookJobIndex / $RunbookJobs.Count)), 2); };
            };
        }

        for ($RunbookJobIndex = 0; $RunbookJobIndex -lt $RunbookJobs.Count; $RunbookJobIndex++)
        {
            if ($ShowProgress.IsPresent)
            {
                Write-Progress `
                    -Activity $WriteProgressParameters.Activity `
                    -Status (& $WriteProgressParameters.StatusMessage) `
                    -PercentComplete (& $WriteProgressParameters.PercentComplete);
            }

            # TODO: Recurse this function instead of calling the base azurerm.automation cmdlet, again

            $GetAzureRmAutomationJobParameters.Keys.Where({ $BaseParameterKeys -notcontains $_ }) `
                | ForEach-Object { $GetAzureRmAutomationJobParameters.Remove($_) };

            $GetAzureRmAutomationJobParameters.Id = $RunbookJobs[$RunbookJobIndex].JobId;
            $RunbookJobs[$RunbookJobIndex] = Get-AzureRmAutomationJob @GetAzureRmAutomationJobParameters;
        }

        if ($ShowProgress.IsPresent)
        {
            Write-Progress `
                -Activity $WriteProgressParameters.Activity `
                -PercentComplete 100 -Completed;
        }
    }

    $RunbookJobs | ForEach-Object -Process `
    {
        if ($null -ne $_.EndTime -and $null -ne $_.StartTime)
        {
            Add-Member -InputObject $_ `
                -MemberType NoteProperty `
                -Name 'RunTime' `
                -Value ($_.EndTime - $_.StartTime);
        }
    };

    return [Collections.Generic.List[Microsoft.Azure.Commands.Automation.Model.Job]] $RunbookJobs;
}

function Get-RunbookJobOutput
{
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $ResourceGroupName,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $AutomationAccountName,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $JobId,

        [Parameter()]
        [switch] $IncludeVerboseStreams,

        [Parameter()]
        [switch] $ShowProgress
    );

    $RequestedStreams = @('Error', 'Output', 'Warning');

    #region Parameter Splatting(s)

    $GetJobStreamParameters = @{
        ResourceGroupName = $ResourceGroupName;
        AutomationAccountName = $AutomationAccountName;
        Id = $JobId;
    };

    if ($ShowProgress.IsPresent)
    {
        $WriteProgressParameters = @{
            Id = 1;
            Activity = 'Getting Job Output';
        }
    }

    #endregion Parameter Splatting(s)

    $Stopwatch = [Diagnostics.Stopwatch]::StartNew();
    $JobStreams = [Collections.Generic.List[Microsoft.Azure.Commands.Automation.Model.JobStream]]::new();
    $JobStreamRecords = [Collections.Generic.List[Microsoft.Azure.Commands.Automation.Model.JobStreamRecord]]::new();

    if ($IncludeVerboseStreams.IsPresent)
    {
        $RequestedStreams += 'Verbose';
        $StatusMessage = 'Fetching all job streams...';

        Write-Verbose $StatusMessage;

        if ($ShowProgress.IsPresent)
        {
            Write-Progress @WriteProgressParameters -Status $StatusMessage `
                -CurrentOperation "Time Elapsed: $(Format-TimeSpan $Stopwatch.Elapsed)";
        }

        $JobStreams_Temp = Get-AzureRmAutomationJobOutput `
            @GetJobStreamParameters `
            -Stream 'Any';

        if ($null -ne $JobStreams_Temp -and $JobStreams_Temp.Count -gt 0)
        {
            $JobStreams.AddRange(([Microsoft.Azure.Commands.Automation.Model.JobStream[]] $JobStreams_Temp));
        }
    }
    else
    {
        $TotalRequestedStreamCount = $RequestedStreams.Count;

        if ($ShowProgress.IsPresent)
        {
            $RequestedStreamsString = $RequestedStreams -Join ', ';
            $RequestedStreamsString = $RequestedStreamsString.Insert(
                $RequestedStreamsString.LastIndexOf(','), ' and');

            $StatusMessage = "Fetching $RequestedStreamsString job streams...";
        }

        for ($StreamIndex = 0; $StreamIndex -lt $TotalRequestedStreamCount; $StreamIndex++)
        {
            $OperationMessage = "Fetching $($RequestedStreams[$StreamIndex]) job streams...";

            Write-Verbose $OperationMessage;

            if ($ShowProgress.IsPresent)
            {
                Write-Progress @WriteProgressParameters -Status $StatusMessage `
                    -CurrentOperation ("Time Elapsed: $(Format-TimeSpan $Stopwatch.Elapsed); " +
                        "$($StreamIndex + 1) of ${TotalRequestedStreamCount}: $OperationMessage") `
                    -PercentComplete ([Math]::Round((($StreamIndex / $TotalRequestedStreamCount) * 100), 2));
            }

            $JobStreams_Temp = Get-AzureRmAutomationJobOutput `
                @GetJobStreamParameters -Stream $RequestedStreams[$StreamIndex];

            if ($null -ne $JobStreams_Temp -and $JobStreams_Temp.Count -gt 0)
            {
                $JobStreams.AddRange(([Microsoft.Azure.Commands.Automation.Model.JobStream[]] $JobStreams_Temp));
            }
        }
    }

    Remove-Variable 'JobStreams_Temp' -Scope 'Local' -Force -ErrorAction:SilentlyContinue;
    [GC]::Collect();

    $TotalJobStreamCount = $JobStreams.Count;
    $StatusMessage = "Fetching $TotalJobStreamCount job stream records...";

    Write-Verbose $StatusMessage;

    $GetJobStreamRecordParameters = $GetJobStreamParameters.Clone();
    $GetJobStreamRecordParameters.JobId = $GetJobStreamRecordParameters.Id;
    $CurrentOperationMessage = [String]::Empty;
    $MaxTotalJobStreamCountStringLength = $TotalJobStreamCount.ToString().Length;

    for ($JobStreamIndex = 0; $JobStreamIndex -lt $TotalJobStreamCount; $JobStreamIndex++)
    {
        $GetJobStreamRecordParameters.Id = $JobStreams[$JobStreamIndex].StreamRecordId;

        $CurrentOperationMessage = (
            "Time Elapsed: $(Format-TimeSpan $Stopwatch.Elapsed); " +
            "$(($JobStreamIndex + 1).ToString().PadLeft($MaxTotalJobStreamCountStringLength, '0')) " +
            "of ${TotalJobStreamCount}: Fetching job stream $($JobStreams[$JobStreamIndex].Type.ToLower()) " +
            "record Id: $($GetJobStreamRecordParameters.Id)"
        );

        Write-Verbose $CurrentOperationMessage;

        if ($ShowProgress.IsPresent)
        {
            Write-Progress @WriteProgressParameters -ParentId ($WriteProgressParameters.Id + 1) `
                -Status $StatusMessage -CurrentOperation $CurrentOperationMessage `
                -PercentComplete ([Math]::Round((($JobStreamIndex / $TotalJobStreamCount) * 100), 2));
        }

        $JobStreamRecords_Temp = Get-AzureRmAutomationJobOutputRecord `
            @GetJobStreamRecordParameters;

        if ($null -ne $JobStreamRecords_Temp)
        {
            $JobStreamRecords.Add($JobStreamRecords_Temp);
        }

        Remove-Variable 'JobStreamRecords_Temp' -Scope 'Local' -Force -ErrorAction:SilentlyContinue;
        [GC]::Collect();
    }

    if ($ShowProgress.IsPresent)
    {
        # Write-Progress @WriteProgressParameters `
        # -ParentId ($WriteProgressParameters.Id + 1) `
        # -PercentComplete 100 -Completed;

        Write-Progress @WriteProgressParameters `
            -PercentComplete 100 -Completed;
    }

    $RequestedJobStreamRecords = @{ };

    foreach ($RequestedStream in $RequestedStreams)
    {
        $RequestedJobStreamRecords.$RequestedStream =
            $JobStreamRecords.Where({ $_.Type -eq $RequestedStream });
    }

    Remove-Variable 'JobStreams' -Scope 'Local' -Force -ErrorAction:SilentlyContinue;
    Remove-Variable 'JobStreamRecords' -Scope 'Local' -Force -ErrorAction:SilentlyContinue;
    [GC]::Collect();

    return (New-Object -TypeName 'PSCustomObject' -Property $RequestedJobStreamRecords);
}

function Start-RunbookJob
{
    [CmdletBinding()]
    [OutputType([Microsoft.Azure.Commands.Automation.Model.Job])]
    param (
        [Parameter(Mandatory)]
        [string] $ResourceGroupName,

        [Parameter(Mandatory)]
        [string] $AutomationAccountName,

        [Parameter(Mandatory)]
        [string] $RunbookName,

        [Parameter()]
        [string] $HybridWorkerName,

        [Parameter()]
        [Hashtable] $RunbookArguments,

        [Parameter()]
        [int] $PollingInterval = 5,

        [Parameter()]
        [switch] $ShowProgress
    );

    #region Parameter Splatting(s)

    $GenericParameters = @{
        ResourceGroupName = $ResourceGroupName;
        AutomationAccountName = $AutomationAccountName;
    };

    $GetRunbookJobHistoryParameters = $GenericParameters.Clone();
    $GetRunbookJobHistoryParameters.RunbookName = $RunbookName;

    $StartRunbookJobParameters = $GenericParameters.Clone();
    $StartRunbookJobParameters.Name = $RunbookName;

    if (-not [String]::IsNullOrEmpty($HybridWorkerName))
    {
        $StartRunbookJobParameters.RunOn = $HybridWorkerName;
    }

    if ($null -ne $RunbookArguments)
    {
        $StartRunbookJobParameters.Parameters = $RunbookArguments;
    }

    #endregion Parameter Splatting(s)

    $JobHistory = Get-RunbookJobHistory `
        @GetRunbookJobHistoryParameters -Status 'Completed';

    if ($null -ne $JobHistory)
    {
        $AverageRunTime = ($JobHistory.RunTime `
            | Measure-Object -Average -Property TotalSeconds).Average;

        Write-Verbose ("Average RunTime for '$RunbookName'" +
            "is $([Math]::Round($AverageRunTime, 2))");
    }

    if ($ShowProgress.IsPresent)
    {
        $RemainingProgress = 100;
        $IndefiniteDenominator = 1.1;

        $WriteProgressParameters = @{
            Activity = "Executing Runbook: $RunbookName";
            StatusMessage = { ("JobId: $($RunbookJob.JobId); " +
                "Time Elapsed: $(Format-TimeSpan $Stopwatch.Elapsed)") };
            PercentComplete = { (100 - $RemainingProgress) };
        };
    }

    $Stopwatch = [Diagnostics.Stopwatch]::StartNew();
    $StartDateTime = [DateTimeOffset]::new((Get-Date).AddMinutes(-1));

    $RunbookJob = Start-AzureRmAutomationRunbook @StartRunbookJobParameters;

    do
    {
        Start-Sleep -Seconds $PollingInterval;

        if ($null -eq $RunbookJob.JobId)
        {
            $RetryCount = 1;
            $MaxRetryCount = 5

            do
            {
                Write-Warning ("Failed to query for the current" +
                    "Job. Retrying ($RetryIndex of $MaxRetryCount).");

                $RunbookJob = Get-RunbookJobHistory `
                        @GetRunbookJobHistoryParameters `
                        -StartDateTime $StartDateTime `
                    | Sort-Object CreationTime -Descending `
                    | Select-Object -First 1;

                $RetryCount++;
            }
            while (
                $null -ne $RunbookJob -or
                $RetryCount -le $MaxRetryCount
            );

            if ($null -eq $RunbookJob)
            {
                Write-Error ("An error has occurred, while" +
                    "retrieving the runbook job from Azure.");

                return $null;
            }
        }

        if ($ShowProgress.IsPresent)
        {
            Write-Progress `
                -Activity $WriteProgressParameters.Activity `
                -Status (& $WriteProgressParameters.StatusMessage) `
                -PercentComplete (& $WriteProgressParameters.PercentComplete);

            $RemainingProgress /= $IndefiniteDenominator;
        }

        $RunbookJob = Get-RunbookJobHistory @GenericParameters `
            -JobId $RunbookJob.JobId;
    }
    while (@('Suspended', 'Completed', 'Failed',
        'Stopped') -notcontains $RunbookJob.Status)

    $Stopwatch.Stop();

    if ($ShowProgress.IsPresent)
    {
        Write-Progress `
            -Activity $WriteProgressParameters.Activity `
            -PercentComplete 100 -Completed;
    }

    return $RunbookJob;
}

function Publish-Runbook
{
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([Microsoft.Azure.Commands.Automation.Model.Runbook])]
    param (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $ResourceGroupName,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $AutomationAccountName,

        [Parameter(Mandatory)]
        [ValidateScript({
            if (-not ($_ | Test-Path))
            {
                throw 'File does not exist';

                return $false;
            }
            elseif ($_ -notmatch "(\.ps1)")
            {
                throw 'If passing a file to the Path argument, it must be a powershell script (ps1).';

                return $false;
            }
            else
            {
                return $true;
            }
        })]
        [IO.FileInfo] $Path,

        [Parameter()]
        [string] $Name,

        [Parameter()]
        [string] $Description,

        [Parameter()]
        [ValidateSet('PowerShell', 'PowerShellWorkflow')]
        [string] $Type = 'PowerShell',

        [Parameter()]
        [switch] $LogVerbose,

        [Parameter()]
        [switch] $LogProgress,

        [Parameter()]
        [switch] $AsDraft,

        [Parameter()]
        [HashTable] $Tags,

        [Parameter()]
        [switch] $Force
    );

    begin
    {
        if (-not $PSBoundParameters.ContainsKey('Confirm'))
        {
            $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference');
        }

        if (-not $PSBoundParameters.ContainsKey('WhatIf'))
        {
            $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference');
        }
    }

    process
    {
        $ImportAzureRmAutomationRunbookParameters = @{
            ResourceGroupName = $ResourceGroupName;
            AutomationAccountName = $AutomationAccountName;
            Path = $Path.FullName;
            Name = $Name;
            Type = $Type;
            LogVerbose = $LogVerbose.IsPresent;
            LogProgress = $LogProgress.IsPresent;
            Published = (-not $AsDraft.IsPresent);
            Force = $Force.IsPresent;
        };

        if ([String]::IsNullOrEmpty($Name))
        {
            $ImportAzureRmAutomationRunbookParameters.Name = $Path.BaseName;
        }

        if (-not [String]::IsNullOrEmpty($Description))
        {
            $ImportAzureRmAutomationRunbookParameters.Description = $Description;
        }

        if ($null -ne $Tags)
        {
            $ImportAzureRmAutomationRunbookParameters.Tags = $Tags.Clone();
        }

        return (
            Import-AzureRmAutomationRunbook @ImportAzureRmAutomationRunbookParameters `
                -WhatIf:(-not ($Force -or $PSCmdlet.ShouldProcess("ShouldProcess?")))
        );
    }
}