forked from Ed-Fi-Exchange-OSS/Suite-3-Performance-Testing
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAzureTestLab.ps1
More file actions
336 lines (278 loc) · 16.1 KB
/
Copy pathAzureTestLab.ps1
File metadata and controls
336 lines (278 loc) · 16.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# SPDX-License-Identifier: Apache-2.0
# Licensed to the Ed-Fi Alliance under one or more agreements.
# The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
# See the LICENSE and NOTICES files in the project root for more information.
$ErrorActionPreference = "Stop"
$resourceGroup = "ods-3-performance"
$applicationDisplayName = "ODS 3 Performance Tests"
$testRunnerServer = "ods-3-perf-test"
$databaseServer = "ods-3-perf-db"
$webServer = "ods-3-perf-web"
$virtualMachines = $testRunnerServer, $databaseServer, $webServer
# Start an Azure management session within the current PowerShell session, assuming the following environment
# variables. Useful when scripting Azure management from TeamCity. This is necessary before other *-AzureRm*
# commands can be executed.
# Source is added for call from GitHub and uses the module Az
#
# Relies on environment variables:
# $env:AzureSubscriptionId
# $env:AzureTenantId
# $env:AzureADApplicationId
# $env:AzureADServicePrincipalPassword
# $env:Source
function Start-AzureManagementSession {
$securePassword = $env:AzureADServicePrincipalPassword | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:AzureADApplicationId, $securePassword
if ([string]::IsNullOrWhiteSpace($env:Source)){
Connect-AzureRmAccount -ServicePrincipal -Credential $credential -TenantId $env:AzureTenantId -SubscriptionId $env:AzureSubscriptionId
}else {
# Az is not supperted by TeamCity.
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $env:AzureTenantId -Subscription $env:AzureSubscriptionId
}
}
# Given a list of PowerShell job objects, wait for them all to complete,
# and demand that all jobs completed successfully. If any failed, output
# diagnostic information and then exit the process with a failure exit
# code.
function Wait-RequiredJobs($jobs) {
$failed = $false
foreach ($job in $jobs) {
Wait-Job $job | Out-Null
if ($job.State -ne "Completed") {
Receive-Job $job
$failed = $true
}
}
Get-Job | Format-List
Get-Job | Remove-Job
if ($failed) {
exit 1
}
}
# As parallel background jobs, initiate the stopping and deallocating of several Azure
# VMs, demanding that all jobs complete successfully.
function Stop-AzureVmsInParallel() {
Write-Host "================================================================================================"
Write-Host "Stopping and deallocating $($virtualMachines.Length) Azure VMs. This can take several minutes..."
Write-Host "================================================================================================"
$jobs = @()
foreach ($virtualMachine in $virtualMachines) {
if ([string]::IsNullOrWhiteSpace($env:Source)){
$jobs += Stop-AzureRmVM -ResourceGroupName $resourceGroup -Name $virtualMachine -Force -AsJob |
Add-Member -MemberType NoteProperty -Name VmName -Value $virtualMachine -PassThru
}else{
# Az is not supperted by TeamCity
$jobs += Stop-AzVM -ResourceGroupName $resourceGroup -Name $virtualMachine -Force -AsJob |
Add-Member -MemberType NoteProperty -Name VmName -Value $virtualMachine -PassThru
}
}
Wait-RequiredJobs $jobs
}
# As parallel background jobs, initiate the starting of several Azure
# VMs, demanding that all jobs complete successfully.
function Start-AzureVmsInParallel() {
Write-Host "==============================================================================="
Write-Host "Starting $($virtualMachines.Length) Azure VMs. This can take several minutes..."
Write-Host "==============================================================================="
$jobs = @()
foreach ($virtualMachine in $virtualMachines) {
if ([string]::IsNullOrWhiteSpace($env:Source)){
$jobs += Start-AzureRmVM -ResourceGroupName $resourceGroup -Name $virtualMachine -AsJob |
Add-Member -MemberType NoteProperty -Name VmName -Value $virtualMachine -PassThru
}else{
# Az is not supperted by TeamCity
$jobs += Start-AzVM -ResourceGroupName $resourceGroup -Name $virtualMachine -AsJob |
Add-Member -MemberType NoteProperty -Name VmName -Value $virtualMachine -PassThru
}
}
Wait-RequiredJobs $jobs
Write-Host "Waiting 30s to allow services to start up."
Start-Sleep -Seconds 30
}
# Run this one time, in an Azure RM session, to create an Azure Active Directory application and service principal for automating access to performance testing resources.
# The password will use the default expiration of 1 year.
function Register-PerformanceTestingServicePrincipal([string]$subscriptionId, [string]$tenantId) {
if ([string]::IsNullOrWhiteSpace($env:Source)){
Set-AzureRMContext -SubscriptionId $subscriptionId -TenantId $tenantId
}else{
Set-AzContext -Subscription $subscriptionId -Tenant $tenantId
}
$applicationUri = "http://ODS-3-Performance-Tests"
Write-Host "This command will create a new Azure Active Directory Application named '$applicationDisplayName', an associated Azure Active Directory Service Principal for use in automating Azure operations, and grant it rights to the '$resourceGroup' Resource Group"
$securePassword = Read-Host "Password for new Azure Active Directory Application" -AsSecureString
# Create an Octopus Deploy Application in Active Directory
Write-Output "Creating Azure Active Directory application '$applicationDisplayName'..."
if ([string]::IsNullOrWhiteSpace($env:Source)){
$application = New-AzureRmADApplication -DisplayName $applicationDisplayName -HomePage $applicationUri -IdentifierUris $applicationUri -Password $securePassword
}else{
$application = New-AzADApplication -DisplayName $applicationDisplayName -HomePage $applicationUri -IdentifierUri $applicationUri -PasswordCredentials $securePassword
}
$application | Format-Table
Write-Output "Creating Azure Active Directory service principal for ApplicationId '$($application.ApplicationId)'..."
if ([string]::IsNullOrWhiteSpace($env:Source)){
$servicePrincipal = New-AzureRmADServicePrincipal -ApplicationId $application.ApplicationId
}else{
$servicePrincipal = New-AzADServicePrincipal -ApplicationId $application.ApplicationId
}
$servicePrincipal | Format-Table
Write-Output "Sleeping for 30s to give the service principal a chance to finish creating..."
Start-Sleep -Seconds 30
Write-Output "Assigning the Contributor role to the service principal for resource group '$resourceGroup'..."
if ([string]::IsNullOrWhiteSpace($env:Source)){
New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $application.ApplicationId -ResourceGroupName $resourceGroup
}else{
New-AzRoleAssignment -RoleDefinitionName Contributor ApplicationId $application.ApplicationId -ResourceGroupName $resourceGroup
}
Write-Output "Connect to Azure using the following Application ID and the given password: $($application.ApplicationId)"
}
# Assigns a new password to the service principal set up by Register-PerformanceTestingServicePrincipal.
# These passwords expire every year.
function Update-PerformanceTestingServicePrincipalPassword([string]$subscriptionId, [string]$tenantId) {
if ([string]::IsNullOrWhiteSpace($env:Source)){
Set-AzureRMContext -SubscriptionId $subscriptionId -TenantId $tenantId
}else{
Set-AzContext -Subscription $subscriptionId -Tenant $tenantId
}
$securePassword = Read-Host "New password for new Azure Active Directory Application '$applicationDisplayName'" -AsSecureString
if ([string]::IsNullOrWhiteSpace($env:Source)){
New-AzureRmADAppCredential -DisplayName $applicationDisplayName -Password $securePassword
}else{
New-AzADAppCredential -DisplayName $applicationDisplayName -PasswordCredentials $securePassword
}
}
# This command exposes this computer for remote PowerShell execution over HTTPS.
# Run this directly on an Azure VM, for instance, to allow subsequent automation of that VM.
# Proceed with caution.
#
# Usage:
# Enable-PowerShellRemotingOverHttps -WhatIf
# Displays a detailed description of the work to be performed, without performing any work.
#
# Enable-PowerShellRemotingOverHttps
# Displays a detailed description of the work to be performed, asking the user to confirm or reject the entire operation.
# If the user confirms the operation, it proceeds in full.
function Enable-PowerShellRemotingOverHttps() {
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')]
param()
$verboseDescription = @"
This command exposes this computer for remote PowerShell execution over HTTPS. It performs the following changes:
0. Enables PowerShell remoting.
1. Creates a firewall rule, 'Allow WinRM HTTPS', allowing inbound traffic on the standard WinRM HTTPS port, 5986.
2. Creates a self-signed certificate for use by a WinRM HTTPS listener.
3. Creates the WinRM HTTPS listener using that certificate.
You may wish to perform these or similar steps manually, if you want more control over the effect of running
this command.
If running this against an Azure VM, be sure to create a similar inbound Networking rule to allow TCP traffic on port 5986
"@
if ($PSCmdlet.ShouldProcess($verboseDescription, $verboseDescription + "`r`n" + "Are you sure you want to perform this action?", "Confirm")) {
# Based on the Azure "EnableRemotePS" Run Command script, but with an extended certificate expiration.
Enable-PSRemoting -Force
New-NetFirewallRule -Name "Allow WinRM HTTPS" -DisplayName "WinRM HTTPS" -Enabled True -Profile Any -Action Allow -Direction Inbound -LocalPort 5986 -Protocol TCP
$thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My -NotAfter (Get-Date).AddYears(5)).Thumbprint
$command = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname=""$env:computername""; CertificateThumbprint=""$thumbprint""}"
cmd.exe /C $command
}
}
# Collect and encrypt the credentials needed to establish remote PowerShell commands against the performance testing database and web servers,
# allowing them to be used during non-interactive test runs for which a login dialog would not be an option. Run this from the server which will
# generate API requests, and again whenever the test runner, database, or web server remoting accounts undergo password changes. This uses the same encryption
# scheme used by built-in PowerShell remoting commands. The encrypted files can only be decrypted by the same user and on the same machine
# as they are created on. This credentials persistence scheme is the intended use case for the built-in Export-CliXml command, which uses the
# Windows standard Data Protection API.
#
# In order to avoid the PowerShell remoting "second hop" problem, this also places the credentials for the test runner server into a PowerShell
# Session Configuration named SecondHopConfiguration.
function Register-Credentials {
$registerCredential = {
param($server)
$folderPath = (Join-Path $env:LOCALAPPDATA "Suite-3-Performance-Testing")
if (!(Test-Path -PathType Container $folderPath)) {
New-Item -ItemType Directory -Force -Path $folderPath | Out-Null
}
$path = Join-Path $folderPath "credentials-$server.xml"
$credential = Get-Credential -Message "Credentials for $server"
$credential | Export-CliXml -Path $path
Write-Host "Saved to $path"
return $credential
}
$testRunnerCredential = Invoke-Command -ScriptBlock $registerCredential -ArgumentList $testRunnerServer
$databaseCredential = Invoke-Command -ScriptBlock $registerCredential -ArgumentList $databaseServer
$webCredential = Invoke-Command -ScriptBlock $registerCredential -ArgumentList $webServer
$configuration = Get-PSSessionConfiguration | Where-Object { $_.Name -eq "SecondHopConfiguration" }
if ($null -eq $configuration) {
Write-Host "Registering new PowerShell Session Configuration 'SecondHopConfiguration'"
Register-PSSessionConfiguration -Name SecondHopConfiguration -RunAsCredential $testRunnerCredential -MaximumReceivedDataSizePerCommandMB 1000 -MaximumReceivedObjectSizeMB 1000
} else {
Write-Host "Updating PowerShell Session Configuration 'SecondHopConfiguration'"
Set-PSSessionConfiguration -Name SecondHopConfiguration -RunAsCredential $testRunnerCredential -MaximumReceivedDataSizePerCommandMB 1000 -MaximumReceivedObjectSizeMB 1000
}
}
# Run tests in the Azure Test Lab, assuming the following environment variables, write test results to an artifacts folder.
#
# Relies on environment variables:
# $env:AzureTestVmPassword
# $env:AzureTestVmUsername
function Invoke-TestRunnerFromTeamCity($testType) {
if (!(Test-Path artifacts)) { New-Item -ItemType Directory -Force -Path artifacts | Out-Null }
$securePassword = $env:AzureTestVmPassword | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:AzureTestVmUsername, $securePassword
$sessionOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$session = New-PSSession -UseSSL -Port 5986 -ComputerName 'edfi-perf-test.southcentralus.cloudapp.azure.com' -Credential $credential -SessionOption $sessionOptions -ConfigurationName SecondHopConfiguration
# Set $testRunnerPath locally and remotely.
$testRunnerPath = Invoke-Command -Session $session {
$testRunnerPath = Get-Content "C:\Users\edFiAdmin\deployed-test-runner-path.txt" -Raw
$testRunnerPath
}
# Set $testResultsPath locally and remotely.
$testResultsPath = Invoke-Command -Session $session {
$testResultsPath = Join-Path $testRunnerPath "TestResults"
$testResultsPath
}
# Set $zipPath locally and remotely.
$zipPath = Invoke-Command -Session $session {
$zipPath = Join-Path $testRunnerPath "TestResults.zip"
$zipPath
}
# Set $zipReportPath locally and remotely for Report.
$zipReportPath = Invoke-Command -Session $session {
$zipReportPath = Join-Path $testRunnerPath "TestReport.zip"
$zipReportPath
}
Invoke-Command -Session $session -ArgumentList $testType, $testResultsPath {
param(
[string] $testType,
[string] $testResultsPath
)
C:\Users\edFiAdmin\run-deployed-tests.bat $testType $testResultsPath
$latest = Get-ChildItem $testResultsPath | Where-Object { $_.PSIsContainer } | Sort-Object CreationTime -desc | Select-Object -f 1
$testResultsPath = Join-Path $testResultsPath $latest
Add-Type -Assembly System.IO.Compression.FileSystem
[System.IO.File]::Delete($zipPath)
[System.IO.Compression.ZipFile]::CreateFromDirectory($testResultsPath, $zipPath, [System.IO.Compression.CompressionLevel]::Optimal, $false)
# Create Zip file for the report
$reportName = $testType + " Test Analysis.html"
$reportPath = Join-Path $testRunnerPath $reportName
$reportPath
if (Test-Path $reportPath -PathType Leaf) {
$compress = @{
Path = $reportPath
CompressionLevel = "Optimal"
DestinationPath = $zipReportPath
}
[System.IO.File]::Delete($zipReportPath)
Compress-Archive @compress
}
}
Write-Output "Uploading test results"
Copy-Item $zipPath -Destination artifacts -FromSession $session -Recurse
$reportExist = Invoke-Command -Session $session -ArgumentList $zipReportPath {
if (Test-Path $zipReportPath -PathType Leaf) {
$true
}
}
if (-not $reportExist){
exit
}
Write-Output "Uploading test reports"
Copy-Item $zipReportPath -Destination artifacts -FromSession $session -Recurse
}