<PathAndCredentials wcm:action="add" wcm:keyValue="1">
<Path>E:\NetKVM\2k19\amd64\</Path>
</PathAndCredentials>
- <PathAndCredentials wcm:action="add" wcm:keyValue="3">
+ <PathAndCredentials wcm:action="add" wcm:keyValue="2">
<Path>E:\viostor\2k19\amd64\</Path>
</PathAndCredentials>
+ <PathAndCredentials wcm:action="add" wcm:keyValue="3">
+ <Path>E:\vioserial\2k19\amd64\</Path>
+ </PathAndCredentials>
</DriverPaths>
</component>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
- <CommandLine>%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell -NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -File A:\install-virtio-guest-tools.ps1</CommandLine>
+ <CommandLine>%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -File A:\first-logon.ps1</CommandLine>
<Order>1</Order>
</SynchronousCommand>
- <SynchronousCommand wcm:action="add">
- <CommandLine>%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell -NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -File A:\install-openssh-server.ps1</CommandLine>
- <Order>2</Order>
- </SynchronousCommand>
-
</FirstLogonCommands>
</component>
ssh-keygen -y -f $SSH_PRIVATE_KEY > ${BUILD_DIR}/id_rsa.pub
sudo cp \
${BUILD_DIR}/autounattend.xml \
- ${BUILD_DIR}/install-virtio-guest-tools.ps1 \
- ${BUILD_DIR}/install-openssh-server.ps1 \
+ ${BUILD_DIR}/first-logon.ps1 \
+ ${BUILD_DIR}/utils.ps1 \
${BUILD_DIR}/id_rsa.pub \
${BUILD_DIR}/floppy/
sudo umount ${BUILD_DIR}/floppy
restart_windows_vm 1800
+scp_upload ${BUILD_DIR}/utils.ps1 /utils.ps1
scp_upload ${BUILD_DIR}/setup.ps1 /setup.ps1
SSH_TIMEOUT=1h ssh_exec powershell.exe -File /setup.ps1
-ssh_exec powershell.exe Remove-Item -Force /setup.ps1
+ssh_exec powershell.exe Remove-Item -Force /setup.ps1, /utils.ps1
restart_windows_vm
--- /dev/null
+$ErrorActionPreference = "Stop"
+
+. "${PSScriptRoot}\utils.ps1"
+
+$VIRTIO_WIN_PATH = "E:\"
+
+# Install QEMU quest agent
+Write-Output "Installing QEMU guest agent"
+$p = Start-Process -FilePath "msiexec.exe" -ArgumentList @("/i", "${VIRTIO_WIN_PATH}\guest-agent\qemu-ga-x86_64.msi", "/qn") -NoNewWindow -PassThru -Wait
+if($p.ExitCode) {
+ Throw "The QEMU guest agent installation failed. Exit code: $($p.ExitCode)"
+}
+Write-Output "Successfully installed QEMU guest agent"
+
+# Install OpenSSH server
+Start-ExecuteWithRetry {
+ Get-WindowsCapability -Online -Name OpenSSH* | Add-WindowsCapability -Online
+}
+Set-Service -Name "sshd" -StartupType Automatic
+Stop-Service -Name "sshd"
+
+# Create SSH firewall rule
+New-NetFirewallRule -Name "sshd" -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
+
+# Authorize the SSH key
+$authorizedKeysFile = Join-Path $env:ProgramData "ssh\administrators_authorized_keys"
+New-Item -ItemType File -Path $authorizedKeysFile -Force
+Set-Content -Path $authorizedKeysFile -Value (Get-Content "${PSScriptRoot}\id_rsa.pub") -Encoding ascii
+$acl = Get-Acl $authorizedKeysFile
+$acl.SetAccessRuleProtection($true, $false)
+$administratorsRule = New-Object system.security.accesscontrol.filesystemaccessrule("Administrators", "FullControl", "Allow")
+$systemRule = New-Object system.security.accesscontrol.filesystemaccessrule("SYSTEM", "FullControl", "Allow")
+$acl.SetAccessRule($administratorsRule)
+$acl.SetAccessRule($systemRule)
+$acl | Set-Acl
+
+# Reboot the machine to complete first logon process
+Restart-Computer -Force -Confirm:$false
+++ /dev/null
-$ErrorActionPreference = "Stop"
-
-Get-WindowsCapability -Online -Name OpenSSH* | Add-WindowsCapability -Online
-
-Set-Service -Name "sshd" -StartupType Automatic
-Start-Service -Name "sshd"
-
-New-NetFirewallRule -Name "sshd" -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
-
-# Authorize the SSH key
-$authorizedKeysFile = Join-Path $env:ProgramData "ssh\administrators_authorized_keys"
-Set-Content -Path $authorizedKeysFile -Value (Get-Content "${PSScriptRoot}\id_rsa.pub") -Encoding ascii
-$acl = Get-Acl $authorizedKeysFile
-$acl.SetAccessRuleProtection($true, $false)
-$administratorsRule = New-Object system.security.accesscontrol.filesystemaccessrule("Administrators", "FullControl", "Allow")
-$systemRule = New-Object system.security.accesscontrol.filesystemaccessrule("SYSTEM", "FullControl", "Allow")
-$acl.SetAccessRule($administratorsRule)
-$acl.SetAccessRule($systemRule)
-$acl | Set-Acl
+++ /dev/null
-$ErrorActionPreference = "Stop"
-
-$VIRTIO_WIN_PATH = "E:\"
-
-Write-Output "Installing virtio-win guest tools"
-
-# Trust driver certs
-$certStore = Get-Item "cert:\LocalMachine\TrustedPublisher"
-$certStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
-$driverPath = Get-Item "${VIRTIO_WIN_PATH}\*\2k19\amd64"
-Get-ChildItem -Recurse -Path $driverPath -Filter "*.cat" | ForEach-Object {
- $cert = (Get-AuthenticodeSignature $_.FullName).SignerCertificate
- $certStore.Add($cert)
-}
-$certStore.Close()
-
-# Install QEMU quest tools
-$p = Start-Process -FilePath "${VIRTIO_WIN_PATH}\virtio-win-guest-tools.exe" -ArgumentList @("/install", "/quiet", "/norestart") -NoNewWindow -PassThru -Wait
-if($p.ExitCode) {
- Throw "The virtio-win guest tools installation failed. Exit code: $($p.ExitCode)"
-}
-
-Write-Output "Successfully installed virtio-win guest tools"
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
+. "${PSScriptRoot}\utils.ps1"
+
$VS_2019_BUILD_TOOLS_URL = "https://aka.ms/vs/16/release/vs_buildtools.exe"
$WDK_URL = "https://download.microsoft.com/download/7/d/6/7d602355-8ae9-414c-ae36-109ece2aade6/wdk/wdksetup.exe" # Windows 11 WDK (22000.1). It can be used to develop drivers for previous OS releases.
$PYTHON3_URL = "https://www.python.org/ftp/python/3.10.1/python-3.10.1-amd64.exe"
return $table
}
-function Invoke-CommandLine {
- Param(
- [Parameter(Mandatory=$true)]
- [String]$Command,
- [String]$Arguments,
- [Int[]]$AllowedExitCodes=@(0)
- )
- & $Command $Arguments.Split(" ")
- if($LASTEXITCODE -notin $AllowedExitCodes) {
- Throw "$Command $Arguments returned a non zero exit code ${LASTEXITCODE}."
- }
-}
-
-function Start-ExecuteWithRetry {
- Param(
- [Parameter(Mandatory=$true)]
- [ScriptBlock]$ScriptBlock,
- [Int]$MaxRetryCount=10,
- [Int]$RetryInterval=3,
- [String]$RetryMessage,
- [Array]$ArgumentList=@()
- )
- $currentErrorActionPreference = $ErrorActionPreference
- $ErrorActionPreference = "Continue"
- $retryCount = 0
- while ($true) {
- try {
- $res = Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
- $ErrorActionPreference = $currentErrorActionPreference
- return $res
- } catch [System.Exception] {
- $retryCount++
- if ($retryCount -gt $MaxRetryCount) {
- $ErrorActionPreference = $currentErrorActionPreference
- Throw $_
- } else {
- $prefixMsg = "Retry(${retryCount}/${MaxRetryCount})"
- if($RetryMessage) {
- Write-Host "${prefixMsg} - $RetryMessage"
- } elseif($_) {
- Write-Host "${prefixMsg} - $($_.ToString())"
- }
- Start-Sleep $RetryInterval
- }
- }
- }
-}
-
-function Start-FileDownload {
- Param(
- [Parameter(Mandatory=$true)]
- [String]$URL,
- [Parameter(Mandatory=$true)]
- [String]$Destination,
- [Int]$RetryCount=10
- )
- Write-Output "Downloading $URL to $Destination"
- Start-ExecuteWithRetry `
- -ScriptBlock { Invoke-CommandLine -Command "curl.exe" -Arguments "-L -s -o $Destination $URL" } `
- -MaxRetryCount $RetryCount `
- -RetryMessage "Failed to download '${URL}'. Retrying"
- Write-Output "Successfully downloaded."
-}
-
-function Add-ToPathEnvVar {
- Param(
- [Parameter(Mandatory=$true)]
- [String[]]$Path,
- [Parameter(Mandatory=$false)]
- [ValidateSet([System.EnvironmentVariableTarget]::User, [System.EnvironmentVariableTarget]::Machine)]
- [System.EnvironmentVariableTarget]$Target=[System.EnvironmentVariableTarget]::Machine
- )
- $pathEnvVar = [Environment]::GetEnvironmentVariable("PATH", $Target).Split(';')
- $currentSessionPath = $env:PATH.Split(';')
- foreach($p in $Path) {
- if($p -notin $pathEnvVar) {
- $pathEnvVar += $p
- }
- if($p -notin $currentSessionPath) {
- $currentSessionPath += $p
- }
- }
- $env:PATH = $currentSessionPath -join ';'
- $newPathEnvVar = $pathEnvVar -join ';'
- [Environment]::SetEnvironmentVariable("PATH", $newPathEnvVar, $Target)
-}
-
-function Install-Tool {
- [CmdletBinding(DefaultParameterSetName = "URL")]
- Param(
- [Parameter(Mandatory=$true, ParameterSetName = "URL")]
- [String]$URL,
- [Parameter(Mandatory=$true, ParameterSetName = "LocalPath")]
- [String]$LocalPath,
- [Parameter(ParameterSetName = "URL")]
- [Parameter(ParameterSetName = "LocalPath")]
- [String[]]$Params=@(),
- [Parameter(ParameterSetName = "URL")]
- [Parameter(ParameterSetName = "LocalPath")]
- [Int[]]$AllowedExitCodes=@(0)
- )
- PROCESS {
- $installerPath = $LocalPath
- if($PSCmdlet.ParameterSetName -eq "URL") {
- $installerPath = Join-Path $env:TEMP $URL.Split('/')[-1]
- Start-FileDownload -URL $URL -Destination $installerPath
- }
- Write-Output "Installing ${installerPath}"
- $p = Start-Process -FilePath $installerPath -ArgumentList $Params -NoNewWindow -PassThru -Wait
- if($p.ExitCode -notin $AllowedExitCodes) {
- Throw "Installation failed. Exit code: $($p.ExitCode)"
- }
- if($PSCmdlet.ParameterSetName -eq "URL") {
- Start-ExecuteWithRetry `
- -ScriptBlock { Remove-Item -Force -Path $installerPath -ErrorAction Stop } `
- -RetryMessage "Failed to remove ${installerPath}. Retrying"
- }
- }
-}
-
function Get-GitHubReleaseAssets {
Param(
[Parameter(Mandatory=$true)]
--- /dev/null
+function Invoke-CommandLine {
+ Param(
+ [Parameter(Mandatory=$true)]
+ [String]$Command,
+ [String]$Arguments,
+ [Int[]]$AllowedExitCodes=@(0)
+ )
+ & $Command $Arguments.Split(" ")
+ if($LASTEXITCODE -notin $AllowedExitCodes) {
+ Throw "$Command $Arguments returned a non zero exit code ${LASTEXITCODE}."
+ }
+}
+
+function Start-ExecuteWithRetry {
+ Param(
+ [Parameter(Mandatory=$true)]
+ [ScriptBlock]$ScriptBlock,
+ [Int]$MaxRetryCount=10,
+ [Int]$RetryInterval=3,
+ [String]$RetryMessage,
+ [Array]$ArgumentList=@()
+ )
+ $currentErrorActionPreference = $ErrorActionPreference
+ $ErrorActionPreference = "Continue"
+ $retryCount = 0
+ while ($true) {
+ try {
+ $res = Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
+ $ErrorActionPreference = $currentErrorActionPreference
+ return $res
+ } catch [System.Exception] {
+ $retryCount++
+ if ($retryCount -gt $MaxRetryCount) {
+ $ErrorActionPreference = $currentErrorActionPreference
+ Throw $_
+ } else {
+ $prefixMsg = "Retry(${retryCount}/${MaxRetryCount})"
+ if($RetryMessage) {
+ Write-Host "${prefixMsg} - $RetryMessage"
+ } elseif($_) {
+ Write-Host "${prefixMsg} - $($_.ToString())"
+ }
+ Start-Sleep $RetryInterval
+ }
+ }
+ }
+}
+
+function Start-FileDownload {
+ Param(
+ [Parameter(Mandatory=$true)]
+ [String]$URL,
+ [Parameter(Mandatory=$true)]
+ [String]$Destination,
+ [Int]$RetryCount=10
+ )
+ Write-Output "Downloading $URL to $Destination"
+ Start-ExecuteWithRetry `
+ -ScriptBlock { Invoke-CommandLine -Command "curl.exe" -Arguments "-L -s -o $Destination $URL" } `
+ -MaxRetryCount $RetryCount `
+ -RetryMessage "Failed to download '${URL}'. Retrying"
+ Write-Output "Successfully downloaded."
+}
+
+function Add-ToPathEnvVar {
+ Param(
+ [Parameter(Mandatory=$true)]
+ [String[]]$Path,
+ [Parameter(Mandatory=$false)]
+ [ValidateSet([System.EnvironmentVariableTarget]::User, [System.EnvironmentVariableTarget]::Machine)]
+ [System.EnvironmentVariableTarget]$Target=[System.EnvironmentVariableTarget]::Machine
+ )
+ $pathEnvVar = [Environment]::GetEnvironmentVariable("PATH", $Target).Split(';')
+ $currentSessionPath = $env:PATH.Split(';')
+ foreach($p in $Path) {
+ if($p -notin $pathEnvVar) {
+ $pathEnvVar += $p
+ }
+ if($p -notin $currentSessionPath) {
+ $currentSessionPath += $p
+ }
+ }
+ $env:PATH = $currentSessionPath -join ';'
+ $newPathEnvVar = $pathEnvVar -join ';'
+ [Environment]::SetEnvironmentVariable("PATH", $newPathEnvVar, $Target)
+}
+
+function Install-Tool {
+ [CmdletBinding(DefaultParameterSetName = "URL")]
+ Param(
+ [Parameter(Mandatory=$true, ParameterSetName = "URL")]
+ [String]$URL,
+ [Parameter(Mandatory=$true, ParameterSetName = "LocalPath")]
+ [String]$LocalPath,
+ [Parameter(ParameterSetName = "URL")]
+ [Parameter(ParameterSetName = "LocalPath")]
+ [String[]]$Params=@(),
+ [Parameter(ParameterSetName = "URL")]
+ [Parameter(ParameterSetName = "LocalPath")]
+ [Int[]]$AllowedExitCodes=@(0)
+ )
+ PROCESS {
+ $installerPath = $LocalPath
+ if($PSCmdlet.ParameterSetName -eq "URL") {
+ $installerPath = Join-Path $env:TEMP $URL.Split('/')[-1]
+ Start-FileDownload -URL $URL -Destination $installerPath
+ }
+ Write-Output "Installing ${installerPath}"
+ $kwargs = @{
+ "FilePath" = $installerPath
+ "ArgumentList" = $Params
+ "NoNewWindow" = $true
+ "PassThru" = $true
+ "Wait" = $true
+ }
+ if((Get-ChildItem $installerPath).Extension -eq '.msi') {
+ $kwargs["FilePath"] = "msiexec.exe"
+ $kwargs["ArgumentList"] = @("/i", $installerPath) + $Params
+ }
+ $p = Start-Process @kwargs
+ if($p.ExitCode -notin $AllowedExitCodes) {
+ Throw "Installation failed. Exit code: $($p.ExitCode)"
+ }
+ if($PSCmdlet.ParameterSetName -eq "URL") {
+ Start-ExecuteWithRetry `
+ -ScriptBlock { Remove-Item -Force -Path $installerPath -ErrorAction Stop } `
+ -RetryMessage "Failed to remove ${installerPath}. Retrying"
+ }
+ }
+}