Automation/LaunchVaaSTests.ps1
##----------------------------------------------------------------------- ## <copyright file="LaunchVaaSTests.ps1" company="Microsoft Corp."> ## Copyright (c) Microsoft Corp. All rights reserved. ## </copyright> ##----------------------------------------------------------------------- <# .SYNOPSIS Launches a VaaS Test Pass based off of the supplied parameters. .DESCRIPTION Launches a VaaS Test Pass based off of the supplied parameters using the AzureStackVaaS PowerShell cmdlets. .PARAMETER VaaSSolutionName VaaSSolutionName .PARAMETER VaaSTestPassName VaaSTestPassName .PARAMETER VaaSTestCategories VaaSTestCategories - Integration, Functional, and Reliability .PARAMETER VaaSTestNames List of VaaS Test Names ex: CloudSimulation .PARAMETER VaaSOnPremAgentName Name of the agent for executing tests on premise. If not specified, the local machine name will be used. .PARAMETER VaaSAccountCreds VaaSAccountCreds - PSCredential type .PARAMETER VaaSApplicationId VaaSApplicationId .PARAMETER VaaSAccountTenantId VaaSAccountTenantId .PARAMETER VaaSPackageUri Optional. Overrides default VaaS package storage location .PARAMETER VaaSPortalUri Optional. Specifies VaaS Portal Endpoint. .PARAMETER VaaSApplicationUri Optional. Specifies Application Endpoint. .PARAMETER VaaSBaseServiceUri Optional. Specifies VaaS Service Endpoint. .PARAMETER VaaSServiceResourceId Optional. Specifies VaaS Service Resource Id. .PARAMETER AadTenantId Optional. Sets the value for AadTenantId in cases where this value cannot be gathered. .PARAMETER ServiceAdminCreds ServiceAdminCreds .PARAMETER TenantAdminCreds TenantAdminCreds .PARAMETER ExternalFqdn Optional. Sets the value for ExternalFqdn in cases where this value cannot be gathered. .PARAMETER Region Optional. Sets the value for region in cases where this value cannot be gathered. .PARAMETER NumberOfNodes Optional. Sets the value for NumberOfNodes in cases where this value cannot be gathered. .PARAMETER DiagnosticsStorageConnection Optional. Used by Cloud Simulation Engine .PARAMETER LogFilePath Path to log files from tests. If the DoNotWait switch is specified, use cmdlet Get-AzureStackVaaSTestLaunchLogs to obtain log files. .PARAMETER MaxDurationInHrs Specifies how long execution of this script should take before cancelling any pending tests and returning the failure exit code (0) .PARAMETER DoNotWait The DoNotWait switch bypasses the test execution monitor loop that normally executes once tests are scheduled. .PARAMETER UseVaaSSPNAuth The UseVaaSSPNAuth switch specifies that SPN credentials are supplied via parameter VaaSAccountCreds. Note that parameters VaaSApplicationId and VaaSApplicationUri are not applicable when specifying the UseVaaSSPNAuth switch. .EXAMPLE Create account credentials object: $VaaSAccountCreds = New-Object System.Management.Automation.PSCredential (“vaasUserId@vaasTenant.onmicrosoft.com”, ConvertTo-SecureString “Password” -AsPlainText -Force) Launch VaaS Test Pass using account credentials: .\LaunchVaaSTests.ps1 -VaaSTestCategory Functional -VaaSAccountCreds $VaaSAccountCreds -VaaSAccountTenantId <VaaSAccountTenantId> -ServiceAdminUserName <ServiceAdminUserName> -ServiceAdminUserPassword <ServiceAdminUserPassword> -TenantAdminUserName <TenantAdminUserName> -TenantAdminUserPassword <TenantAdminUserPassword> -CloudAdminUserName <CloudAdminUserName> -CloudAdminUserPassword <CloudAdminUserPassword> -DiagnosticsStorageConnection <DiagnosticsStorageSASURL> -MaxDurationInHrs 48 .LINK https://www.powershellgallery.com/packages/AzureStackVaaS #> param( #VaaS Service Params [Parameter(Mandatory=$true)][ValidateNotNull()][PSCredential]$VaaSAccountCreds, [Parameter(Mandatory=$true)][ValidateNotNull()][string]$VaaSAccountTenantId, [Parameter(Mandatory=$false)][string]$VaaSSolutionName=$null, [Parameter(Mandatory=$false)][string]$VaaSTestPassName=$null, [Parameter(Mandatory=$false)][ValidateSet("Invalid","Integration","Functional","Reliability")][string[]]$VaaSTestCategories="Invalid", [Parameter(Mandatory=$false)][string[]]$VaaSTestNames, [Parameter(Mandatory=$false)][hashtable]$VaaSTestParameterHashTable=@{}, [Parameter(Mandatory=$false)][string]$VaaSApplicationId=$null, [Parameter(Mandatory=$false)][switch]$UseVaaSSPNAuth, [Parameter(Mandatory=$false)][string]$VaaSPackageUri=$null, [Parameter(Mandatory=$false)][string]$VaaSPortalUri=$null, [Parameter(Mandatory=$false)][string]$VaaSApplicationUri=$null, [Parameter(Mandatory=$false)][string]$VaaSBaseServiceUri=$null, [Parameter(Mandatory=$false)][string]$VaaSServiceResourceId=$null, [Parameter(Mandatory=$false)][string]$VaaSOnPremAgentName, [Parameter(Mandatory=$false)][string]$VaaSAgentVersion='latest', [Parameter(Mandatory=$false)][string]$VaaSPSCmdletsVersion='latest', #Azure Stack Common Params [Parameter(Mandatory=$true)][string]$ServiceAdminUserName=$null, [Parameter(Mandatory=$true)][string]$ServiceAdminUserPassword=$null, [Parameter(Mandatory=$true)][string]$TenantAdminUserName=$null, [Parameter(Mandatory=$true)][string]$TenantAdminUserPassword=$null, [Parameter(Mandatory=$true)][string]$CloudAdminUserName=$null, [Parameter(Mandatory=$true)][string]$CloudAdminUserPassword=$null, [Parameter(Mandatory=$true)][string]$DiagnosticsStorageConnection=$null, [Parameter(Mandatory=$false)][string]$LogFilePath=$null, [Parameter(Mandatory=$true)][ValidateRange(2,720)][int]$MaxScriptWaitTimeInHours=12, [Parameter(Mandatory=$false)][switch]$DoNotWait, [Parameter(Mandatory=$false)][int]$NumberOfIterations=1 ) Add-Type -AssemblyName System.Web.Extensions # for JavaScriptSerializer class [System.Reflection.Assembly]::LoadWithPartialName("mscorlib") | Out-Null try { Add-Type -TypeDefinition @" public struct LaunchData { public System.DateTime durationTimeout; public string[] logPaths; public string onPremAgentDirectory; public int testResult; public int titleCounter; public string vaaSPackageDirectory; } "@ } catch { Write-Host "Add-Type may already be added for this PowerShell Session ..." } finally { $launcherData = New-Object LaunchData } Add-Type -TypeDefinition @" public struct EnvInfo { public string DeploymentID; public string StampVersion; public string Prefix; public string CompanyName; public string ServerSku; public string Topology; public string Timezone; public string HardwareOEM; public string RegionName; public string DomainNetBIOSName; public string DomainFQDN; public string Timeserver; public int NumberOfNodes; public string ExternalFqdn; public string AdminResourceManager; public string TenantResourceManager; public string AadTenantId; public string CloudId; public string IdentitySystem; public string OemVersion; public string OemSignature; public string HardwareInfo; } "@ $envInfo = New-Object EnvInfo New-Variable -Name BaseURIVaaSStorage -Value "https://vaastestpacksprodeastus.blob.core.windows.net" -Option Constant New-Variable -Name BaseURIVaaSPortal -Value "https://azurestackvalidation.com" -Option Constant New-Variable -Name GetStampInfoUri -Value "https://ASAppGateway:4443/ServiceTypeId/4dde37cc-6ee0-4d75-9444-7061e156507f/CloudDefinition/GetStampInformation" -Option Constant New-Variable -Name IsProductionRun -Value $true New-Variable -Name DefaultRetryCount -Value 3 -Option Constant New-Variable -Name DefaultRetryPeriod -Value 60 -Option Constant New-Variable -Name ProjectType -Value "TestPasses" -Option Constant function CreateVaaSDirectory { if (!(Test-Path -Path "$($launcherData.vaaSPackageDirectory)" -PathType Container)) { Write-Verbose "Creating vaaSPackageDirectory ..." New-Item -ItemType Directory -Path $launcherData.vaaSPackageDirectory | Out-Null } else { Write-Warning "Path $($launcherData.vaaSPackageDirectory) already exists ..." } } function CreateVaaSLogDirectory { if (!(Test-Path -Path "$LogFilePath" -PathType Container)) { Write-Verbose "Creating VaaS Log Directory ..." New-Item -ItemType Directory -Path $LogFilePath | Out-Null } } function CreateVaaSSolution { DisplayTitle -Title "Creating VaaS Solution" Write-Host "Creating VaaS Solution with name $VaaSSolutionName ..." return (New-AzureStackVaaSSolution -Name $VaaSSolutionName -Force -ErrorAction Stop) } function CreateVaaSTestPass { DisplayTitle -Title "Creating VaaS Test Pass" Write-Host "Creating VaaS Test Pass with name $VaaSTestPassName ..." $tpParams = @{'SolutionName' = $VaaSSolutionName;'Name' = $VaaSTestPassName} if($envInfo.StampVersion -ne "") { $tpParams.Add('Build',$envInfo.StampVersion) } if($envInfo.ServerSku -ne "") { $tpParams.Add('ServerSku',$envInfo.ServerSku) } if($envInfo.Topology -ne "") { $tpParams.Add('Topology',$envInfo.Topology) } if($envInfo.RegionName -ne "") { $tpParams.Add('RegionName',$envInfo.RegionName) } if($envInfo.NumberOfNodes -ne "") { $tpParams.Add('NumberOfNodes',$envInfo.NumberOfNodes) } if($envInfo.DeploymentID -ne "") { $tpParams.Add('DeploymentId',$envInfo.DeploymentID) } if($envInfo.CloudId -ne "") { $tpParams.Add('CloudId',$envInfo.CloudId) } if($envInfo.IdentitySystem -ne "") { $tpParams.Add('IdentitySystem',$envInfo.IdentitySystem) } if($envInfo.OemVersion -ne "") { $tpParams.Add('OemVersion',$envInfo.OemVersion) } if($envInfo.OemSignature -ne "") { $tpParams.Add('OemSignature',$envInfo.OemSignature) } if($envInfo.HardwareInfo -ne "") { $tpParams.Add('HardwareInfo',$envInfo.HardwareInfo) } return (New-AzureStackVaaSTestPass @tpParams -Force -ErrorAction Stop) } function InstallVaaSPSModules { DisplayTitle -Title "Installing Azure Stack VaaS PowerShell Module" if($VaaSPackageUri -imatch "$BaseURIVaaSStorage/packages") { Write-Host "Installing from PowerShell Gallery ..." Install-Module -Name AzureStackVaaS -Scope AllUsers -AllowClobber -Force -ErrorVariable InstallError -ErrorAction SilentlyContinue if($InstallError) { Write-Warning "Couldn't install Azure Stack VaaS PowerShell module..." } return } $vaasPSModulesUri = "$VaaSPackageUri/AzureStackVaaS.$VaaSPSCmdletsVersion.nupkg" Write-Host "Downloading latest Azure Stack VaaS PowerShell Module from:" Write-Host $vaasPSModulesUri try { Invoke-WebRequest -Uri $vaasPSModulesUri -outfile "$($launcherData.vaaSPackageDirectory)\AzureStackVaaS.zip" } catch { Write-Warning "Unable to download file from $vaasPSModulesUri" Write-Warning "Attempting retry in 30 seconds ..." Start-Sleep -s 30 | Out-Null try { Invoke-WebRequest -Uri $vaasPSModulesUri -outfile "$($launcherData.vaaSPackageDirectory)\AzureStackVaaS.zip" } catch { Write-Warning "Unable to download file on retry from:`r`n$vaasPSModulesUri" return } } $psModulePath = $env:ProgramFiles + '\WindowsPowerShell\Modules\AzureStackVaaS' Write-Host "Extracting latest Azure Stack VaaS PowerShell Module to:`r`n$psModulePath" Expand-Archive -Path "$($launcherData.vaaSPackageDirectory)\AzureStackVaaS.zip" -DestinationPath "$psModulePath" -Force -ErrorAction SilentlyContinue -ErrorVariable ExpandError if($ExpandError) { if($ExpandError[0].CategoryInfo.Reason -imatch 'UnauthorizedAccessException') { Write-Warning "A PowerShell lock currently exists on the Azure Stack VaaS PowerShell." Write-Warning "To update binaries, please close the applicable PowerShell window or ISE." } } } function InstallVaaSOnPremAgent { DisplayTitle -Title "Installing VaaS OnPrem Agent" Write-Host "Stopping any running VaaS OnPrem Agent(s) ..." StopVaaSOnPremAgentProcess Write-Host "Downloading latest VaaS OnPrem Agent from:" $vaasOnPremAgentUri = "$VaaSPackageUri/Microsoft.VaaSOnPrem.TaskEngineHost.$VaaSAgentVersion.nupkg" Write-Host $vaasOnPremAgentUri New-Item -ItemType Directory -Force -Path $launcherData.vaaSPackageDirectory | Out-Null try { Invoke-WebRequest -Uri $vaasOnPremAgentUri -outfile "$($launcherData.vaaSPackageDirectory)\OnPremAgent.zip" } catch { Write-Warning "Unable to download file from $vaasOnPremAgentUri" Write-Warning "Attempting retry in 30 seconds ..." Start-Sleep -s 30 | Out-Null Invoke-WebRequest -Uri $vaasOnPremAgentUri -outfile "$($launcherData.vaaSPackageDirectory)\OnPremAgent.zip" } Write-Host "Extracting latest VaaS OnPrem Agent to:" Write-Host $launcherData.vaaSPackageDirectory Expand-Archive -Path "$($launcherData.vaaSPackageDirectory)\OnPremAgent.zip" -DestinationPath "$($launcherData.vaaSPackageDirectory)\OnPremAgent" -Force } function InstallVaaSPreReq { DisplayTitle -Title "Installing VaaS Prerequisites" Push-Location $launcherData.onPremAgentDirectory try { Import-Module .\VaaSPreReqs.psm1 -Force Set-VaaSPreReqContext -LogFilePath $LogFilePath $secServiceAdminUserPassword = ConvertTo-SecureString -AsPlainText $ServiceAdminUserPassword -Force $ServiceAdminCreds = New-Object System.Management.Automation.PSCredential($ServiceAdminUserName,$secServiceAdminUserPassword) Install-VaaSPrerequisites -AadTenantId $($envInfo.AadTenantId) -ServiceAdminCreds $ServiceAdminCreds -ArmEndpoint $($envInfo.AdminResourceManager) -Region $($envInfo.RegionName) } catch { Pop-Location Write-Error "VaaS Prerequisite installation failed. Check logs for details." TerminateScript $_.Exception.Message } Write-Host "VaaS Prerequisite installation succeeded. Check logs for details." Pop-Location } function InitializeLaunchData { $launcherData.vaaSPackageDirectory = $env:Temp + "\VaaS" $launcherData.onPremAgentDirectory = $launcherData.vaaSPackageDirectory + "\OnPremAgent\lib\net46" $launcherData.durationTimeout = (Get-Date).AddHours($MaxScriptWaitTimeInHours) $launcherData.logPaths = @() $launcherData.testResult = 0 $launcherData.titleCounter = 0 } function ValidateAndResolveAuthMethodParameters { if(($VaaSPortalUri -eq "") -and ($VaaSBaseServiceUri -eq "") -and ($VaaSServiceResourceId -eq "")) { $script:IsProductionRun = $true } elseif(($VaaSPortalUri -ne "") -and ($VaaSBaseServiceUri -ne "") -and ($VaaSServiceResourceId -ne "")) { $script:IsProductionRun = $false } else { throw [System.ArgumentException] "Parameters VaaSPortalUri, VaaSBaseServiceUri, and VaaSServiceResourceId must be either all specified, or none specified ..." } if(($IsProductionRun) -or ($UseVaaSSPNAuth)) { if(($VaaSApplicationId -ne "") -or ($VaaSApplicationUri -ne "")) { throw [System.ArgumentException] "Parameters VaaSApplicationId and/or VaaSApplicationUri should not be specified when launching a production level run or when using SPN credentials ..." } } elseif(!$UseVaaSSPNAuth) { Write-Host "Verifying that parameters VaaSApplicationId and VaaSApplicationUri are present as`r`nthe UseVaaSSPNAuth switch was not specified..." if(($VaaSApplicationId -eq "") -or ($VaaSApplicationUri -eq "")) { throw [System.ArgumentException] "Parameters VaaSApplicationId and VaaSApplicationUri must both be specified when using non-SPN credentials are supplied ..." } } } function ValidateAndResolveTestParameters { if($VaaSPackageUri -eq "") { $script:VaaSPackageUri = "$BaseURIVaaSStorage/packages" Write-Host "VaaS Package endpoint updated to:`r`n$VaaSPackageUri" } if($VaaSPortalUri -eq "") { $script:VaaSPortalUri = $BaseURIVaaSPortal Write-Host "VaaS Portal endpoint updated to:`r`n$VaaSPortalUri" } } function ValidateAndResolveEnvironmentParameters { if($VaaSOnPremAgentName -eq "") { $script:VaaSOnPremAgentName = (Get-WmiObject win32_computersystem).DNSHostName + "." + (Get-WmiObject win32_computersystem).Domain Write-Host "Defaulting to VaaS OnPrem agent name $VaaSOnPremAgentName, as an agent name was not provided ..." } GetStampInformation if($VaaSTestPassName -eq "") { $script:VaaSTestPassName = "TP" + $(Get-Date -Format yyMMddHHmm) Write-Host "Defaulting to VaaS test pass name $VaaSTestPassName, as a test pass name was not provided ..." } if($VaaSSolutionName -eq "") { $script:VaaSSolutionName = "AzureStack-VaaS" Write-Host "Defaulting to VaaS solution name $VaaSSolutionName, as a solution name was not provided ..." } } function ValidateAndResolveParameters { DisplayTitle -Title "Validating and Resolving Parameters" ValidateAndResolveAuthMethodParameters ValidateAndResolveTestParameters ValidateAndResolveEnvironmentParameters } function AddDefaultValueToTestParameterHashTable { param( [string]$key, [string]$value ) if (-not $VaaSTestParameterHashTable.ContainsKey($key)) { Write-Host "Add default value for test parameter [$key]=[$value]" $VaaSTestParameterHashTable[$key] = $value } } function AddDefaultValuesToTestParameterHashTable { AddDefaultValueToTestParameterHashTable "ExternalFqdn" $envInfo.ExternalFqdn AddDefaultValueToTestParameterHashTable "TenantId" $envInfo.AadTenantId AddDefaultValueToTestParameterHashTable "AdminArmEndpoint" $envInfo.AdminResourceManager AddDefaultValueToTestParameterHashTable "TenantArmEndpoint" $envInfo.TenantResourceManager AddDefaultValueToTestParameterHashTable "ServiceAdminUser" $ServiceAdminUserName AddDefaultValueToTestParameterHashTable "ServiceAdminPassword" $ServiceAdminUserPassword AddDefaultValueToTestParameterHashTable "TenantAdminUser" $TenantAdminUserName AddDefaultValueToTestParameterHashTable "TenantAdminPassword" $TenantAdminUserPassword AddDefaultValueToTestParameterHashTable "CloudAdminUser" $CloudAdminUserName AddDefaultValueToTestParameterHashTable "CloudAdminPassword" $CloudAdminUserPassword AddDefaultValueToTestParameterHashTable "NumberOfNodes" $envInfo.NumberOfNodes.ToString() AddDefaultValueToTestParameterHashTable "Region" $envInfo.RegionName if($DiagnosticsStorageConnection -ne "") { if($envInfo.DeploymentID -eq "") { $envInfo.DeploymentID = (New-Guid).ToString().ToLowerInvariant() Write-Warning "Generating value for DiagnosticsContainerName ($($envInfo.DeploymentID)) as environment information is not available ..." } AddDefaultValueToTestParameterHashTable "DiagnosticsContainerName" $envInfo.DeploymentID AddDefaultValueToTestParameterHashTable "DiagnosticsStorageConnection" $DiagnosticsStorageConnection } } function CancelActiveTests { param( [parameter(Mandatory=$true)][Object[]]$testLaunchManifests ) foreach($testLaunchManifest in $testLaunchManifests) { Write-Host "Getting information for test: $($testLaunchManifest.TestLaunchName) ..." $testManifest = Get-AzureStackVaaSTestLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -Name $testLaunchManifest.TestLaunchName -ErrorAction Ignore -ErrorVariable GetTestError Write-Host "Test state: $($testManifest.State) ..." if (($testManifest.State -eq 'Accepted') -or ($testManifest.State -eq 'Running')) { Write-Host "Attempting to stop test $($testLaunchManifest.TestLaunchName) ..." Stop-AzureStackVaaSTestLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -Name $testLaunchManifest.TestLaunchName -Force -ErrorAction Ignore -ErrorVariable StopTestError if($StopTestError) { Write-Warning "Unable to Stop Test $($testLaunchManifest.TestLaunchName) due to $($StopTestError[0].CategoryInfo.Reason). Retrying ..." Start-Sleep -s 5 | Out-Null Stop-AzureStackVaaSTestLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -Name $testLaunchManifest.TestLaunchName -Force -ErrorAction Continue } } } } function StartVaaSOnPremAgent { DisplayTitle -Title "Starting VaaS OnPrem Agent" Write-Host "Verifying that 'login.windows.net' can be resolved ..." Resolve-DnsName login.windows.net -ErrorVariable ResolutionError -ErrorAction SilentlyContinue | Out-Null if($ResolutionError) { Write-Error "Terminating script as 'login.windows.net' cannot be resolved ..." throw $ResolutionError[0].Exception } $agentArgs = @("-t $VaaSAccountTenantId","-p $($VaaSAccountCreds.GetNetworkCredential().password)","-n $VaaSOnPremAgentName") if($UseVaaSSPNAuth) { $agentArgs += "-c $(($VaaSAccountCreds).UserName)" } else { $agentArgs += "-u $(($VaaSAccountCreds).UserName)" if($VaaSApplicationId -ne "") { $agentArgs += "-c $VaaSApplicationId" } } if($VaaSBaseServiceUri -ne "") { $agentArgs += "-l $VaaSBaseServiceUri" } if($VaaSServiceResourceId -ne "") { $agentArgs += "-r $VaaSServiceResourceId" } if($VaaSApplicationUri -ne "") { $agentArgs += "-z $VaaSApplicationUri" } Write-Host "Checking to see if Agent $VaaSOnPremAgentName already exists ..." $agentInstance = Get-AzureStackVaaSOnPremAgent -Name $VaaSOnPremAgentName -ErrorAction SilentlyContinue -ErrorVariable GetAgentError | Out-Null if($GetAgentError) { Write-Host "Did not find Agent $VaaSOnPremAgentName. Starting agent ..." } else { RemoveVaaSOnPremAgent -agentName $VaaSOnPremAgentName } Push-Location $launcherData.onPremAgentDirectory Start-Process -FilePath "Microsoft.VaaSOnPrem.TaskEngineHost.exe" -ArgumentList $agentArgs -WindowStyle Minimized -Verb runas $agentArgs = [string]::Empty Pop-Location Write-Host "VaaS OnPrem Agent started ..." Write-Host "Waiting for OnPrem Agent to register ..." Write-Host "Verifying OnPrem Agent status ..." VerifyVaaSOnPremAgentStatus -agentName $VaaSOnPremAgentName return $VaaSOnPremAgentName } function VerifyVaaSOnPremAgentStatus { param( [parameter(Mandatory=$true)][ValidateNotNull()][string]$agentName ) Start-Sleep -s 10 | Out-Null $agentInstance = Get-AzureStackVaaSOnPremAgent -Name $agentName -ErrorAction Ignore -ErrorVariable GetAgentError if($GetAgentError) { Write-Verbose "Unable to get agent $VaaSOnPremAgentName, retrying after 30 seconds ..." Start-Sleep -s 30 | Out-Null $agentInstance = Get-AzureStackVaaSOnPremAgent -Name $agentName -ErrorAction Ignore -ErrorVariable GetAgentError if($GetAgentError) { $launcherData.testResult = 1 $launcherData.testResult ShutdownVaaSOnPremAgent RemoveVaaSOnPremAgent exit $launcherData.testResult } } if($agentInstance.ApproximatePendingTests -gt 0) { Write-Warning "Agent has $($agentInstance.ApproximatePendingTests) pending tests ..." } Write-Host "Agent ready for launching tests ..." } function ShutdownVaaSOnPremAgent { $agentInstance = Get-AzureStackVaaSOnPremAgent -Name $VaaSOnPremAgentName -ErrorAction Ignore -ErrorVariable ShutdownAgentError if($ShutdownAgentError) { Write-Verbose "Unable to get agent $VaaSOnPremAgentName" return } [DateTime]$shutdownTimeout = (Get-Date).AddMinutes(5) while($agentInstance.ApproximatePendingTests -gt 0) { if((Get-Date) -gt $shutdownTimeout) { Write-Warning "Tests are still being processed by agent after 5 minutes, forcing Agent shutdown ..." break } Start-Sleep -Seconds 20 | Out-Null $agentInstance = Get-AzureStackVaaSOnPremAgent -Name $VaaSOnPremAgentName Write-Host "Waiting for $($agentInstance.ApproximatePendingTests) test(s) to complete before shutting down Agent ..." } StopVaaSOnPremAgentProcess } function StopVaaSOnPremAgentProcess { $process = Get-Process -ProcessName "Microsoft.VaaSOnPrem.TaskEngineHost" -ErrorAction Ignore foreach ($proc in $process) { Stop-Process $proc -Force -ErrorAction Ignore } } function RemoveVaaSOnPremAgent { param( [parameter(Mandatory=$true)][ValidateNotNull()][string]$agentName ) Write-Host "Attempting to remove Agent $agentName ..." Remove-AzureStackVaaSOnPremAgent -Name $agentName -ErrorAction SilentlyContinue -ErrorVariable RemoveAgentError -Force | Out-Null if($RemoveAgentError) { Write-Warning "Unable to remove Agent $agentName due to ..." $errorMsg = "" if ($RemoveAgentError.Exception.InnerException.Message -ne $null) { $errorMsg = $RemoveAgentError.Exception.InnerException.Message.ToString() } else { $errorMsg = $RemoveAgentError.Exception.ToString() } Write-Warning $errorMsg } else { Write-Host "Agent $agentName was removed successfully ..." } } function GetMemberOrDefault { param( [parameter(Mandatory=$true)][ValidateNotNull()][PSCustomObject]$object, [parameter(Mandatory=$true)][ValidateNotNull()][string]$memberName ) if(Get-Member -InputObject $object -Name $memberName) { return $object.$memberName } return ""; } function GetStampInformation { $params = $null $retryCount = $DefaultRetryCount while($retryCount -gt 0) { try { Write-Host "Attempting to get stamp information via:`r`n$GetStampInfoUri" $params = Invoke-RestMethod -Method Get -Uri $GetStampInfoUri break } catch { Write-Warning "Exception thrown while attempting to get stamp information:`r`n$($_.Exception.Message)" } $retryCount-- if ($retryCount -eq 0) { Write-Warning "Retries exhausted. Unable to get stamp infomation ..." } Write-Host "Attempting to get stamp information again in $DefaultRetryPeriod seconds" Start-Sleep -seconds $DefaultRetryPeriod | Out-Null } if($params) { $envInfo.DeploymentID = $params.DeploymentID $envInfo.StampVersion = $params.StampVersion $envInfo.Prefix = $params.Prefix $envInfo.CompanyName = $params.CompanyName $envInfo.ServerSku = $params.ServerSku $envInfo.Topology = $params.Topology $envInfo.Timezone = $params.Timezone $envInfo.HardwareOEM = $params.HardwareOEM $envInfo.DomainNetBIOSName = $params.DomainNetBIOSName $envInfo.DomainFQDN = $params.DomainFQDN $envInfo.Timeserver = $params.Timeserver $envInfo.NumberOfNodes = $params.NumberOfNodes $envInfo.AadTenantId = $params.AADTenantID $envInfo.RegionName = $params.RegionName $envInfo.CloudId = $params.CloudId $envInfo.IdentitySystem = $params.IdentitySystem $envInfo.OemVersion = $params.OemVersion $envInfo.OemSignature = GetMemberOrDefault -Object $params -MemberName "OemSignature" $envInfo.HardwareInfo = GetMemberOrDefault -Object $params -MemberName "HardwareInfo" $envInfo.ExternalFqdn = $params.ExternalDNSFQDN01.Substring($params.ExternalDNSFQDN01.IndexOf(".")+1) if($envInfo.ExternalFqdn -eq "") { Write-Warning "ExternalFqdn appears to be invalid. Specify the expected ExternalFqdn via corresponding script parameter ..." } $envInfo.AdminResourceManager = $params.AdminExternalEndpoints.AdminResourceManager $envInfo.TenantResourceManager = $params.TenantExternalEndpoints.TenantResourceManager } } function GenerateVaaSReport { Write-Host "Generating VaaS Report" Write-Host "Current directory: $(Get-Location)" Write-Host "Setting directory to $PSScriptRoot" Set-Location $PSScriptRoot Write-Host "Current directory now set to: $(Get-Location)" $retryCount = $DefaultRetryCount while($retryCount -gt 0) { try { New-AzureStackVaaSTestPassReport -SolutionName $VaaSSolutionName -Name $VaaSTestPassName -Force break } catch { Write-Warning "Exception thrown while attempting to call New-AzureStackVaaSTestPassReport:`r`n$($_.Exception.Message)" } $retryCount-- if ($retryCount -eq 0) { Write-Error "Retries exhausted. Unable to generate test pass report ..." return } Write-Host "Attempting to generate report again in 5 seconds" $Logfile Start-Sleep -seconds 5 | Out-Null } } function DownloadLogs { param( [parameter(Mandatory=$true)][string]$testLaunchName, [parameter(Mandatory=$true)][string]$testName ) [string]$logFullName = $LogFilePath + "\" + $testLaunchName + ".zip" Start-Sleep -s 2 | Out-Null Get-AzureStackVaaSTestLaunchLogs -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -TestLaunchName $testLaunchName -Path $logFullName -ErrorAction SilentlyContinue -ErrorVariable downloadError if($downloadError) { if($downloadError[0].CategoryInfo.Reason -imatch 'AggregateException') { Write-Warning "Unable to download logs for test $testLaunchName to $logFullName" Write-Warning "Command used to download logs:" Write-Warning "Get-AzureStackVaaSTestLaunchLogs -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -TestLaunchName $testLaunchName -Path $logFullName" Write-Host "" Start-Sleep -s 2 | Out-Null return } } $launcherData.logPaths += "Logs for test $testName [$testLaunchName] downloaded to $logFullName" Write-Host $launcherData.logPaths[-1] Write-Host "" } function TestMonitor { param( [parameter(Mandatory=$false)][int]$refreshRate=20, [parameter(Mandatory=$false)][int]$refreshInterval=600, [parameter(Mandatory=$true)][Object[]]$testLaunchManifests ) DisplayTitle -title "Test Pass Monitor" Start-Sleep -Seconds 2 | Out-Null $launcherData.durationTimeout = (Get-Date).AddHours($MaxScriptWaitTimeInHours) $prevTestManifests = $null $nextInterval = (Get-Date).AddSeconds(-1) while($true) { if((Get-Date) -gt $launcherData.durationTimeout) { $launcherData.testResult = 1 Write-Warning "Cancelling all remaining tests as timeout has been reached ..." CancelActiveTests -testLaunchManifests $testLaunchManifests return } try { $testManifests = @() foreach($testLaunchManifest in $testLaunchManifests) { $testManifests += Get-AzureStackVaaSTestLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -Name $testLaunchManifest.TestLaunchName -ErrorAction SilentlyContinue -ErrorVariable GetTestError if($GetTestError) { Write-Warning "Could not get information for test $($testLaunchManifest.TestLaunchName); Reason - $($GetTestError[0].CategoryInfo.Reason)" } } if($testManifests.count -ne $testLaunchManifests.count) { Write-Warning "Attempting to gather test pass information in $refreshRate seconds..." Write-Warning "Number of tests launched: $($testLaunchManifests.count); Number of tests status was gathered: $($testManifests.count)" Start-Sleep -Seconds $refreshRate | Out-Null continue } $testPassActive = $false foreach ($testManifest in $testManifests) { if (($testManifest.State -eq 'Accepted') -or ($testManifest.State -eq 'Running')) { $testPassActive = $true break } } # Check to see if there are any updates if($prevTestManifests) { foreach ($prevTestManifest in $prevTestManifests) { #output table, reset nextInterval, and continue $testManifest = $testManifests | where {$_.TestLaunchName -eq $prevTestManifest.TestLaunchName} if($prevTestManifest.State -ne $testManifest.State) { Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] Checking Test Pass Status ..." Write-Host "Test Pass URL $VaaSPortalUri/$VaaSAccountTenantId/en-US/TestPass/Manage/Summary?solutionName=$VaaSSolutionName&projectName=$VaaSTestPassName" Find-AzureStackVaaSTestLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -ErrorAction SilentlyContinue -ErrorVariable GetTestError | ft -ErrorAction Ignore @{Expression={$_.TestName};Label="Test Name"},@{Expression={$_.CreatedTimeUtc};Label="Created"},@{Expression={if($_.StartedTimeUtc -eq "1/1/0001 12:00:00 AM"){""}else{$_.StartedTimeUtc}};Label="Started"},State,AgentName if($GetTestError) { Write-Warning "Could not get test pass information; Reason - $($GetTestError[0].CategoryInfo.Reason)" } $nextInterval = (Get-Date).AddSeconds($refreshInterval) $prevTestManifests = $testManifests if( ($testManifest.State -eq "Succeeded") -or ($testManifest.State -eq "Failed") -or ($testManifest.State -eq "Cancelled") ) { DownloadLogs -testLaunchName $testManifest.TestLaunchName -testName $testManifest.TestName } continue } } } else { Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] Checking Test Pass Status ..." Write-Host "Test Pass URL $VaaSPortalUri/$VaaSAccountTenantId/en-US/TestPass/Manage/Summary?solutionName=$VaaSSolutionName&projectName=$VaaSTestPassName" Find-AzureStackVaaSTestLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -ErrorAction SilentlyContinue -ErrorVariable GetTestError | ft -ErrorAction Ignore @{Expression={$_.TestName};Label="Test Name"},@{Expression={$_.CreatedTimeUtc};Label="Created"},@{Expression={if($_.StartedTimeUtc -eq "1/1/0001 12:00:00 AM"){""}else{$_.StartedTimeUtc}};Label="Started"},State,AgentName if($GetTestError) { Write-Warning "Could not get test pass information; Reason - $($GetTestError[0].CategoryInfo.Reason)" } $prevTestManifests = $testManifests } if (!$testPassActive) { DisplayTitle -title "Test pass completed. Final Result Summary" $statusTable = $testManifests | ft @{Expression={$_.TestName};Label="Test Name"},@{Expression={$_.CreatedTimeUtc};Label="Created"},@{Expression={if($_.StartedTimeUtc -eq "1/1/0001 12:00:00 AM"){""}else{$_.StartedTimeUtc}};Label="Started"},State,AgentName | Out-String Write-Host "Test Pass Status:" Write-Host "$statusTable" foreach($logPath in $launcherData.logPaths) { Write-Host $logPath } foreach ($testManifest in $testManifests) { if( ($testManifest.State -eq "Failed") -or ($testManifest.State -eq "Cancelled") ) { $launcherData.testResult = 1 break } } return } # If the time interval has been reached, output table, and reset nextInterval if($nextInterval -lt (Get-Date)) { Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] Checking Test Pass Status ..." $nextInterval = (Get-Date).AddSeconds($refreshInterval) } $prevTestManifests = $testManifests } catch { Write-Warning "Exception occurred while calling Find-AzureStackVaaSTestLaunch. Retrying ..." } Start-Sleep -Seconds $refreshRate | Out-Null } } function DisplaySessionInfo { Write-Host "Current User: $([Security.Principal.WindowsIdentity]::GetCurrent().Name)" $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if ($currentPrincipal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator )) { Write-Host "Current session is running as an Administrator ..." } else { Write-Warning "Current session is running is not running as an Administrator ..." } } function DisplayTitle { param( [parameter(Mandatory=$true)][string]$title, [parameter(Mandatory=$false)][int]$seconds=1 ) $launcherData.titleCounter++ Write-Host "" Write-Host "----------------------------------------------------" -ForegroundColor Green Write-Host " $($launcherData.titleCounter). $title" -ForegroundColor Green Write-Host "----------------------------------------------------" -ForegroundColor Green Start-Sleep -s $seconds | Out-Null } Function TerminateScript { Param( [Parameter(Mandatory=$True)][String]$Message ) Write-Error $Message $lastExitCode = 1 $lastExitCode exit } function Main { DisplayTitle -title "Adding AzureStack VaaS Account" if($IsProductionRun) { Write-Host "Production Run ..." if($UseVaaSSPNAuth) { Write-Host "Using SPN Authentication" Add-AzureStackVaaSAccount -Credential $VaaSAccountCreds -ServicePrincipal -TenantId $VaaSAccountTenantId -ErrorAction Stop } else { Write-Host "Using User Authentication" Add-AzureStackVaaSAccount -Credential $VaaSAccountCreds -TenantId $VaaSAccountTenantId -ErrorAction Stop } } else { if($UseVaaSSPNAuth) { Write-Host "Using SPN Authentication" Add-AzureStackVaaSAccount -Credential $VaaSAccountCreds -ServicePrincipal -TenantId $VaaSAccountTenantId -TenantServiceBaseUri $VaaSBaseServiceUri -TenantServiceResourceId $VaaSServiceResourceId -ErrorAction Stop } else { Write-Host "Using User Authentication" Add-AzureStackVaaSAccount -Credential $VaaSAccountCreds -ApplicationId $VaaSApplicationId -ApplicationUri $VaaSApplicationUri -TenantId $VaaSAccountTenantId -TenantServiceBaseUri $VaaSBaseServiceUri -TenantServiceResourceId $VaaSServiceResourceId -ErrorAction Stop } } StartVaaSOnPremAgent $vaasSolution = CreateVaaSSolution $vaasTestPass = CreateVaaSTestPass AddDefaultValuesToTestParameterHashTable DisplayTitle -Title "VaaS test pass launch" Write-Host "Launching test pass $VaaSTestPassName on Solution $VaaSSolutionName against Agent $VaaSOnPremAgentName" for($i=0; $i -lt $NumberOfIterations; $i++) { Write-Host "=== Current interation number is $i ===" $mergedtestManifests = @() try { if($VaaSTestCategories -ne "Invalid") { Write-Host "Calling New-AzureStackVaaSTestGroupLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -AgentName $VaaSOnPremAgentName -TestFilter Category -TestFilterValue $VaaSTestCategories -ParameterObject $VaaSTestParameterHashTable -ErrorVariable LaunchError" $testLaunchManifests = New-AzureStackVaaSTestGroupLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -AgentName $VaaSOnPremAgentName -TestFilter Category -TestFilterValue $VaaSTestCategories -ParameterObject $VaaSTestParameterHashTable -ErrorVariable LaunchError } else { Write-Host "Calling New-AzureStackVaaSTestGroupLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -AgentName $VaaSOnPremAgentName -TestFilter Test -TestFilterValue $VaaSTestNames -ParameterObject $VaaSTestParameterHashTable -ErrorVariable LaunchError" $testLaunchManifests = New-AzureStackVaaSTestGroupLaunch -SolutionName $VaaSSolutionName -ProjectName $VaaSTestPassName -ProjectType $ProjectType -AgentName $VaaSOnPremAgentName -TestFilter Test -TestFilterValue $VaaSTestNames -ParameterObject $VaaSTestParameterHashTable -ErrorVariable LaunchError } } catch { Write-Warning "Unable to launch tests" Write-Error $_ } if(!$testLaunchManifests) { throw [System.AggregateException] "No test manifest data was returned after launching test group ..." } $mergedtestManifests += $testLaunchManifests Write-Host "You can also monitor your test pass using VaaS Portal at:" -ForegroundColor Green -BackgroundColor Black Write-Host "$VaaSPortalUri/$VaaSAccountTenantId/en-US/TestPass/Manage/Summary?solutionName=$VaaSSolutionName&projectName=$VaaSTestPassName" -ForegroundColor Green -BackgroundColor Black if($DoNotWait) { return } TestMonitor -testLaunchManifests $mergedtestManifests } GenerateVaaSReport } Set-PSDebug -Strict $windowTitle = $host.ui.RawUI.WindowTitle if($LogFilePath -eq "") { $script:LogFilePath = "$PSScriptRoot\Logs" Write-Host "Defaulting to VaaS log file path $LogFilePath, as a log file path was not provided ..." } CreateVaaSLogDirectory $logFileName = "$LogFilePath\LaunchVaaSTests_$(Get-Date -Format yyyyMMdd-HHmmss).log" Start-Transcript -Path $logFileName -IncludeInvocationHeader Write-Output "`r`n" DisplaySessionInfo try { Write-Output "PsBoundParameters: " foreach($keyValue in $PsBoundParameters.GetEnumerator()) { Write-Output "$($keyValue.Key)=$($keyValue.Value)" } Write-Output "" InitializeLaunchData ValidateAndResolveParameters InstallVaaSOnPremAgent InstallVaaSPreReq $host.ui.RawUI.WindowTitle = "VaaS" CreateVaaSDirectory InstallVaaSPSModules Import-Module AzureStackVaaS -Force -ErrorAction Stop Write-Host "`r`nAzureStackVaaS module version loaded: $((Get-Module -Name AzureStackVaaS).Version.ToString())`r`n" Import-Module 'AzureRM' Disable-AzureRmDataCollection -WarningAction Ignore Main } catch { $_ | Format-List * -Force | Out-Default; $_.InvocationInfo | Format-List * | Out-Default $Exception = $_.Exception for ($i = 0; $Exception; $i++, ($Exception = $_.InnerException)) { "$i" * 80 $Exception | Format-List * -Force | Out-Default } $launcherData.testResult = 1 } finally { ShutdownVaaSOnPremAgent RemoveVaaSOnPremAgent -agentName $VaaSOnPremAgentName Remove-Module AzureStackVaaS -Force -ErrorAction Ignore Stop-Transcript $host.ui.RawUI.WindowTitle = $windowTitle Pop-Location exit $launcherData.testResult } |