Private/Submit-ScriptToVmAndExecute.ps1

function Global:Submit-ScriptToVmAndExecute {
    [CmdletBinding()]
    <#
    .SYNOPSIS
        ...
    .DESCRIPTION
        ...
        Do not pass any secure-objects in $RunParameter (like SecureString for Passwords); these will cause to run indefinitely
    #>

    param(
        [Parameter(Mandatory = $true)]
        [string]
        $ResourceGroupName,        
        [Parameter(Mandatory = $true)]
        [string]
        $ResourceLocation,
        [Parameter(Mandatory = $true)]
        [string]
        $VMName,
        [Parameter(Mandatory = $true)]
        [ValidateSet('InstallD365Module', 'InitVM', 'DownloadBC', 'InstallBC', 'GeneralizeVM', 'WriteProperties', 'CreateUpdateScheduledTask', 'InvokeAutoUpdate', 'UpdateNetworkSettings', 'EnableRemoting', 'WaitForNetwork', 'WriteDiagnoseInformation', 'ConfigureSqlServer', 'ConfigureSqlServerDomain', 'RestoreDemoDatabase')]
        [string]
        $ScriptBlockName,
        [Parameter(Mandatory = $false)]
        $RunParameter,
        [Parameter(Mandatory = $false)]
        [string]
        $MsgBeforeExecuting,
        [Parameter(Mandatory = $false)]
        [switch]
        $ScaleSetExecution = $false
    )
    process {
        # Load Scripts from separate file
        . $PSScriptRoot\Scriptblocks.ps1

        # Check tags on resource (not for Scale Sets / only for checks during Image creation)
        $tagName = "Command $ScriptBlockName"
        if (-not($ScaleSetExecution)) {
            $vm = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName
            if ($vm.Tags.ContainsKey($tagName)) {
                Write-CustomHost -Message "Command $ScriptBlockName was already executed."
                return
            }
        }
        if ($MsgBeforeExecuting) {
            Write-CustomHost -Message $MsgBeforeExecuting
        }
        $fullscriptpath = New-TemporaryFile | Rename-Item -NewName { $_ -replace 'tmp$', 'ps1' } -PassThru | Select-Object -ExpandProperty FullName
        $content = ""
        switch ($ScriptBlockName) {
            'InstallD365Module' { $content = $installD365Module }
            'InitVM' { $content = $initializeVm }
            'DownloadBC' { $content = $downloadBCDVD }
            'InstallBC' { $content = $installBC }
            'GeneralizeVM' { $content = $generalizeVM }
            'WriteProperties' { $content = $writeProperties }
            'CreateUpdateScheduledTask' { $content = $createUpdateScheduledTask }
            'InvokeAutoUpdate' { $content = $invokeAutoUpdate }
            'UpdateNetworkSettings' { $content = $setNetConnectionProfile }
            'WaitForNetwork' { $content = $waitForNetwork }
            'EnableRemoting' { $content = $enableRemoting }
            'WriteDiagnoseInformation' { $content = $saveDiagInformation }
            'ConfigureSqlServer' { $content = $configureSqlServer }
            'ConfigureSqlServerDomain' { $content = $configureSqlServerDomain }
            'RestoreDemoDatabase' { $content = $restoreSqlDemoDatabase }
        }
        Set-Content -Path $fullscriptpath -Value $content
        Write-CustomHost -Message "Updating tags on resource..."
        # Tag VM / VMSS
        $resource = Get-AzResource -ResourceGroupName $ResourceGroupName -Name $VMName
        $existingTags = $resource.Tags
        if ($existingTags.Count -ne 0) {
            if (-not($existingTags.ContainsKey($tagName))) {
                $existingTags.Add($tagName, "Started")
                Set-AzResource -ResourceId $resource.Id -Tag $existingTags -Force | Out-Null
            }            
            else {
                $existingTags.$tagName = "Started"
                Set-AzResource -ResourceId $resource.Id -Tag $existingTags -Force | Out-Null
            }
        }
        else {            
            Set-AzResource -ResourceId $resource.Id -Tag @{$tagName = "Started" } -Force | Out-Null
        }
        Write-CustomHost -Message "Running command '$ScriptBlockName' on VM $VMName..."
        if (-not($ScaleSetExecution)) {
            Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroupName -VMName $VMName -CommandId 'RunPowerShellScript' -ScriptPath $fullscriptpath -Parameter $RunParameter | Out-Null        
        }
        else {
            foreach ($instance in Get-AzVmssVM -ResourceGroupName $ResourceGroupName -VMScaleSetName $VMName) {
                $instanceId = $instance.InstanceId
            
            Write-CustomHost -Message "on Instance $instanceId..."
            Invoke-AzVmssVMRunCommand -ResourceGroupName $ResourceGroupName -VMScaleSetName $VMName -InstanceId $instanceId -CommandId 'RunPowerShellScript' -ScriptPath $fullscriptpath -Parameter $RunParameter | Out-Null
            }
        }
        Write-CustomHost -Message "Command completed."
        
        Write-CustomHost -Message "Updating tags on resource..."
        $resource = Get-AzResource -ResourceGroupName $ResourceGroupName -Name $VMName
        $existingTags = $resource.Tags
        $existingTags.$tagName = "Executed"
        Set-AzResource -ResourceId $resource.Id -Tag $existingTags -Force | Out-Null

        Remove-Item $fullscriptpath -Force
    }
}