mirror of
https://github.com/microsoft/vcpkg.git
synced 2024-12-02 17:38:59 +08:00
459 lines
14 KiB
PowerShell
459 lines
14 KiB
PowerShell
|
# Copyright (c) Microsoft Corporation.
|
||
|
# SPDX-License-Identifier: MIT
|
||
|
#
|
||
|
#
|
||
|
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Creates a Windows virtual machine scale set, set up for vcpkg's CI.
|
||
|
|
||
|
.DESCRIPTION
|
||
|
create-vmss.ps1 creates an Azure Windows VM scale set, set up for vcpkg's CI
|
||
|
system. See https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/overview
|
||
|
for more information.
|
||
|
|
||
|
This script assumes you have installed Azure tools into PowerShell by following the instructions
|
||
|
at https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-3.6.1
|
||
|
or are running from Azure Cloud Shell.
|
||
|
#>
|
||
|
|
||
|
$Location = 'SouthCentralUS'
|
||
|
$Prefix = 'PrWin-' + (Get-Date -Format 'yyyy-MM-dd')
|
||
|
$VMSize = 'Standard_F16s_v2'
|
||
|
$ProtoVMName = 'PROTOTYPE'
|
||
|
$LiveVMPrefix = 'BUILD'
|
||
|
$WindowsServerSku = '2019-Datacenter'
|
||
|
$InstalledDiskSizeInGB = 1024
|
||
|
$ErrorActionPreference = 'Stop'
|
||
|
|
||
|
$ProgressActivity = 'Creating Scale Set'
|
||
|
$TotalProgress = 12
|
||
|
$CurrentProgress = 1
|
||
|
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Returns whether there's a name collision in the resource group.
|
||
|
|
||
|
.DESCRIPTION
|
||
|
Find-ResourceGroupNameCollision takes a list of resources, and checks if $Test
|
||
|
collides names with any of the resources.
|
||
|
|
||
|
.PARAMETER Test
|
||
|
The name to test.
|
||
|
|
||
|
.PARAMETER Resources
|
||
|
The list of resources.
|
||
|
#>
|
||
|
function Find-ResourceGroupNameCollision {
|
||
|
[CmdletBinding()]
|
||
|
Param([string]$Test, $Resources)
|
||
|
|
||
|
foreach ($resource in $Resources) {
|
||
|
if ($resource.ResourceGroupName -eq $Test) {
|
||
|
return $true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $false
|
||
|
}
|
||
|
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Attempts to find a name that does not collide with any resources in the resource group.
|
||
|
|
||
|
.DESCRIPTION
|
||
|
Find-ResourceGroupName takes a set of resources from Get-AzResourceGroup, and finds the
|
||
|
first name in {$Prefix, $Prefix-1, $Prefix-2, ...} such that the name doesn't collide with
|
||
|
any of the resources in the resource group.
|
||
|
|
||
|
.PARAMETER Prefix
|
||
|
The prefix of the final name; the returned name will be of the form "$Prefix(-[1-9][0-9]*)?"
|
||
|
#>
|
||
|
function Find-ResourceGroupName {
|
||
|
[CmdletBinding()]
|
||
|
Param([string] $Prefix)
|
||
|
|
||
|
$resources = Get-AzResourceGroup
|
||
|
$result = $Prefix
|
||
|
$suffix = 0
|
||
|
while (Find-ResourceGroupNameCollision -Test $result -Resources $resources) {
|
||
|
$suffix++
|
||
|
$result = "$Prefix-$suffix"
|
||
|
}
|
||
|
|
||
|
return $result
|
||
|
}
|
||
|
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Creates a randomly generated password.
|
||
|
|
||
|
.DESCRIPTION
|
||
|
New-Password generates a password, randomly, of length $Length, containing
|
||
|
only alphanumeric characters (both uppercase and lowercase).
|
||
|
|
||
|
.PARAMETER Length
|
||
|
The length of the returned password.
|
||
|
#>
|
||
|
function New-Password {
|
||
|
Param ([int] $Length = 32)
|
||
|
|
||
|
$Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||
|
$result = ''
|
||
|
for ($idx = 0; $idx -lt $Length; $idx++) {
|
||
|
# NOTE: this should probably use RNGCryptoServiceProvider
|
||
|
$result += $Chars[(Get-Random -Minimum 0 -Maximum $Chars.Length)]
|
||
|
}
|
||
|
|
||
|
return $result
|
||
|
}
|
||
|
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Waits for the shutdown of the specified resource.
|
||
|
|
||
|
.DESCRIPTION
|
||
|
Wait-Shutdown takes a VM, and checks if there's a 'PowerState/stopped'
|
||
|
code; if there is, it returns. If there isn't, it waits ten seconds and
|
||
|
tries again.
|
||
|
|
||
|
.PARAMETER ResourceGroupName
|
||
|
The name of the resource group to look up the VM in.
|
||
|
|
||
|
.PARAMETER Name
|
||
|
The name of the virtual machine to wait on.
|
||
|
#>
|
||
|
function Wait-Shutdown {
|
||
|
[CmdletBinding()]
|
||
|
Param([string]$ResourceGroupName, [string]$Name)
|
||
|
|
||
|
Write-Host "Waiting for $Name to stop..."
|
||
|
while ($true) {
|
||
|
$Vm = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $Name -Status
|
||
|
$highestStatus = $Vm.Statuses.Count
|
||
|
for ($idx = 0; $idx -lt $highestStatus; $idx++) {
|
||
|
if ($Vm.Statuses[$idx].Code -eq 'PowerState/stopped') {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Write-Host "... not stopped yet, sleeping for 10 seconds"
|
||
|
Start-Sleep -Seconds 10
|
||
|
}
|
||
|
}
|
||
|
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Sanitizes a name to be used in a storage account.
|
||
|
|
||
|
.DESCRIPTION
|
||
|
Sanitize-Name takes a string, and removes all of the '-'s and
|
||
|
lowercases the string, since storage account names must have no
|
||
|
'-'s and must be completely lowercase alphanumeric. It then makes
|
||
|
certain that the length of the string is not greater than 24,
|
||
|
since that is invalid.
|
||
|
|
||
|
.PARAMETER RawName
|
||
|
The name to sanitize.
|
||
|
#>
|
||
|
function Sanitize-Name {
|
||
|
[CmdletBinding()]
|
||
|
Param(
|
||
|
[string]$RawName
|
||
|
)
|
||
|
|
||
|
$result = $RawName.Replace('-', '').ToLowerInvariant()
|
||
|
if ($result.Length -gt 24) {
|
||
|
Write-Error 'Sanitized name for storage account $result was too long.'
|
||
|
}
|
||
|
|
||
|
return $result
|
||
|
}
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Creating resource group' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
$ResourceGroupName = Find-ResourceGroupName $Prefix
|
||
|
$AdminPW = New-Password
|
||
|
New-AzResourceGroup -Name $ResourceGroupName -Location $Location
|
||
|
$AdminPWSecure = ConvertTo-SecureString $AdminPW -AsPlainText -Force
|
||
|
$Credential = New-Object System.Management.Automation.PSCredential ("AdminUser", $AdminPWSecure)
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Creating virtual network' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
$allowHttp = New-AzNetworkSecurityRuleConfig `
|
||
|
-Name AllowHTTP `
|
||
|
-Description 'Allow HTTP(S)' `
|
||
|
-Access Allow `
|
||
|
-Protocol Tcp `
|
||
|
-Direction Outbound `
|
||
|
-Priority 1008 `
|
||
|
-SourceAddressPrefix * `
|
||
|
-SourcePortRange * `
|
||
|
-DestinationAddressPrefix * `
|
||
|
-DestinationPortRange @(80, 443)
|
||
|
|
||
|
$allowDns = New-AzNetworkSecurityRuleConfig `
|
||
|
-Name AllowDNS `
|
||
|
-Description 'Allow DNS' `
|
||
|
-Access Allow `
|
||
|
-Protocol * `
|
||
|
-Direction Outbound `
|
||
|
-Priority 1009 `
|
||
|
-SourceAddressPrefix * `
|
||
|
-SourcePortRange * `
|
||
|
-DestinationAddressPrefix * `
|
||
|
-DestinationPortRange 53
|
||
|
|
||
|
$allowStorage = New-AzNetworkSecurityRuleConfig `
|
||
|
-Name AllowStorage `
|
||
|
-Description 'Allow Storage' `
|
||
|
-Access Allow `
|
||
|
-Protocol * `
|
||
|
-Direction Outbound `
|
||
|
-Priority 1010 `
|
||
|
-SourceAddressPrefix VirtualNetwork `
|
||
|
-SourcePortRange * `
|
||
|
-DestinationAddressPrefix Storage `
|
||
|
-DestinationPortRange *
|
||
|
|
||
|
$denyEverythingElse = New-AzNetworkSecurityRuleConfig `
|
||
|
-Name DenyElse `
|
||
|
-Description 'Deny everything else' `
|
||
|
-Access Deny `
|
||
|
-Protocol * `
|
||
|
-Direction Outbound `
|
||
|
-Priority 1011 `
|
||
|
-SourceAddressPrefix * `
|
||
|
-SourcePortRange * `
|
||
|
-DestinationAddressPrefix * `
|
||
|
-DestinationPortRange *
|
||
|
|
||
|
$NetworkSecurityGroupName = $ResourceGroupName + 'NetworkSecurity'
|
||
|
$NetworkSecurityGroup = New-AzNetworkSecurityGroup `
|
||
|
-Name $NetworkSecurityGroupName `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Location $Location `
|
||
|
-SecurityRules @($allowHttp, $allowDns, $allowStorage, $denyEverythingElse)
|
||
|
|
||
|
$SubnetName = $ResourceGroupName + 'Subnet'
|
||
|
$Subnet = New-AzVirtualNetworkSubnetConfig `
|
||
|
-Name $SubnetName `
|
||
|
-AddressPrefix "10.0.0.0/16" `
|
||
|
-NetworkSecurityGroup $NetworkSecurityGroup
|
||
|
|
||
|
$VirtualNetworkName = $ResourceGroupName + 'Network'
|
||
|
$VirtualNetwork = New-AzVirtualNetwork `
|
||
|
-Name $VirtualNetworkName `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Location $Location `
|
||
|
-AddressPrefix "10.0.0.0/16" `
|
||
|
-Subnet $Subnet
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Creating archives storage account' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
$StorageAccountName = Sanitize-Name $ResourceGroupName
|
||
|
|
||
|
New-AzStorageAccount `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Location $Location `
|
||
|
-Name $StorageAccountName `
|
||
|
-SkuName 'Standard_LRS' `
|
||
|
-Kind StorageV2
|
||
|
|
||
|
$StorageAccountKeys = Get-AzStorageAccountKey `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Name $StorageAccountName
|
||
|
|
||
|
$StorageAccountKey = $StorageAccountKeys[0].Value
|
||
|
|
||
|
$StorageContext = New-AzStorageContext `
|
||
|
-StorageAccountName $StorageAccountName `
|
||
|
-StorageAccountKey $StorageAccountKey
|
||
|
|
||
|
$ArchivesFiles = New-AzStorageShare -Name 'archives' -Context $StorageContext
|
||
|
Set-AzStorageShareQuota -ShareName 'archives' -Context $StorageContext -Quota 5120
|
||
|
$LogFiles = New-AzStorageShare -Name 'logs' -Context $StorageContext
|
||
|
Set-AzStorageShareQuota -ShareName 'logs' -Context $StorageContext -Quota 64
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity 'Creating prototype VM' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
$NicName = $ResourceGroupName + 'NIC'
|
||
|
$Nic = New-AzNetworkInterface `
|
||
|
-Name $NicName `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Location $Location `
|
||
|
-Subnet $VirtualNetwork.Subnets[0]
|
||
|
|
||
|
$VM = New-AzVMConfig -Name $ProtoVMName -VMSize $VMSize
|
||
|
$VM = Set-AzVMOperatingSystem `
|
||
|
-VM $VM `
|
||
|
-Windows `
|
||
|
-ComputerName $ProtoVMName `
|
||
|
-Credential $Credential `
|
||
|
-ProvisionVMAgent `
|
||
|
-EnableAutoUpdate
|
||
|
|
||
|
$VM = Add-AzVMNetworkInterface -VM $VM -Id $Nic.Id
|
||
|
$VM = Set-AzVMSourceImage `
|
||
|
-VM $VM `
|
||
|
-PublisherName 'MicrosoftWindowsServer' `
|
||
|
-Offer 'WindowsServer' `
|
||
|
-Skus $WindowsServerSku `
|
||
|
-Version latest
|
||
|
|
||
|
$InstallDiskName = $ProtoVMName + "InstallDisk"
|
||
|
$VM = Add-AzVMDataDisk `
|
||
|
-Vm $VM `
|
||
|
-Name $InstallDiskName `
|
||
|
-Lun 0 `
|
||
|
-Caching ReadWrite `
|
||
|
-CreateOption Empty `
|
||
|
-DiskSizeInGB $InstalledDiskSizeInGB `
|
||
|
-StorageAccountType 'StandardSSD_LRS'
|
||
|
|
||
|
$VM = Set-AzVMBootDiagnostic -VM $VM -Disable
|
||
|
New-AzVm `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Location $Location `
|
||
|
-VM $VM
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Running provisioning script provision-image.ps1 in VM' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
Invoke-AzVMRunCommand `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-VMName $ProtoVMName `
|
||
|
-CommandId 'RunPowerShellScript' `
|
||
|
-ScriptPath "$PSScriptRoot\provision-image.ps1" `
|
||
|
-Parameter @{AdminUserPassword = $AdminPW; `
|
||
|
StorageAccountName=$StorageAccountName; `
|
||
|
StorageAccountKey=$StorageAccountKey;}
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Restarting VM' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
Restart-AzVM -ResourceGroupName $ResourceGroupName -Name $ProtoVMName
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Running provisioning script sysprep.ps1 in VM' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
Invoke-AzVMRunCommand `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-VMName $ProtoVMName `
|
||
|
-CommandId 'RunPowerShellScript' `
|
||
|
-ScriptPath "$PSScriptRoot\sysprep.ps1"
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Waiting for VM to shut down' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
Wait-Shutdown -ResourceGroupName $ResourceGroupName -Name $ProtoVMName
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Converting VM to Image' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
Stop-AzVM `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Name $ProtoVMName `
|
||
|
-Force
|
||
|
|
||
|
Set-AzVM `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Name $ProtoVMName `
|
||
|
-Generalized
|
||
|
|
||
|
$VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $ProtoVMName
|
||
|
$PrototypeOSDiskName = $VM.StorageProfile.OsDisk.Name
|
||
|
$ImageConfig = New-AzImageConfig -Location $Location -SourceVirtualMachineId $VM.ID
|
||
|
$Image = New-AzImage -Image $ImageConfig -ImageName $ProtoVMName -ResourceGroupName $ResourceGroupName
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Deleting unused VM and disk' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
Remove-AzVM -Id $VM.ID -Force
|
||
|
Remove-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $PrototypeOSDiskName -Force
|
||
|
Remove-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $InstallDiskName -Force
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress `
|
||
|
-Activity $ProgressActivity `
|
||
|
-Status 'Creating scale set' `
|
||
|
-PercentComplete (100 / $TotalProgress * $CurrentProgress++)
|
||
|
|
||
|
$VmssIpConfigName = $ResourceGroupName + 'VmssIpConfig'
|
||
|
$VmssIpConfig = New-AzVmssIpConfig -SubnetId $Nic.IpConfigurations[0].Subnet.Id -Primary -Name $VmssIpConfigName
|
||
|
$VmssName = $ResourceGroupName + 'Vmss'
|
||
|
$Vmss = New-AzVmssConfig `
|
||
|
-Location $Location `
|
||
|
-SkuCapacity 6 `
|
||
|
-SkuName $VMSize `
|
||
|
-SkuTier 'Standard' `
|
||
|
-Overprovision $false `
|
||
|
-UpgradePolicyMode Manual
|
||
|
|
||
|
$Vmss = Add-AzVmssNetworkInterfaceConfiguration `
|
||
|
-VirtualMachineScaleSet $Vmss `
|
||
|
-Primary $true `
|
||
|
-IpConfiguration $VmssIpConfig `
|
||
|
-NetworkSecurityGroupId $NetworkSecurityGroup.Id `
|
||
|
-Name $NicName
|
||
|
|
||
|
$Vmss = Set-AzVmssOsProfile `
|
||
|
-VirtualMachineScaleSet $Vmss `
|
||
|
-ComputerNamePrefix $LiveVMPrefix `
|
||
|
-AdminUsername 'AdminUser' `
|
||
|
-AdminPassword $AdminPW `
|
||
|
-WindowsConfigurationProvisionVMAgent $true `
|
||
|
-WindowsConfigurationEnableAutomaticUpdate $true
|
||
|
|
||
|
$Vmss = Set-AzVmssStorageProfile `
|
||
|
-VirtualMachineScaleSet $Vmss `
|
||
|
-OsDiskCreateOption 'FromImage' `
|
||
|
-OsDiskCaching ReadWrite `
|
||
|
-ImageReferenceId $Image.Id
|
||
|
|
||
|
New-AzVmss `
|
||
|
-ResourceGroupName $ResourceGroupName `
|
||
|
-Name $VmssName `
|
||
|
-VirtualMachineScaleSet $Vmss
|
||
|
|
||
|
####################################################################################################
|
||
|
Write-Progress -Activity $ProgressActivity -Completed
|
||
|
Write-Host "Location: $Location"
|
||
|
Write-Host "Resource group name: $ResourceGroupName"
|
||
|
Write-Host "User name: AdminUser"
|
||
|
Write-Host "Using generated password: $AdminPW"
|
||
|
Write-Host 'Finished!'
|