Artifacts/windows-deploymentgroup/InstallAndConfigureDeploymentGroupAgent.ps1 (282 lines of code) (raw):

param ( [Parameter(Mandatory=$true)] $accountUrl, [Parameter(Mandatory=$true)] $projectName, [Parameter(Mandatory=$true)] $deploymentGroupName, [Parameter(Mandatory=$true)] $personalAccessToken, [Parameter(Mandatory=$true)][Boolean] $runAsAutoLogon, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $deploymentAgentTags, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $agentInstallPath, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $agentName, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $windowsLogonAccount, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $windowsLogonPassword ) # Ensure we force use of TLS 1.2 for all downloads. [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 function Test-InstallPrerequisites { If(-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Host "Insufficient privileges. Run command in Administrator PowerShell Prompt" Exit -1 } } function New-AgentPath { [CmdletBinding()] param([Parameter(Mandatory=$false)] [String] [AllowEmptyString()]$agentInstallPath) If ([string]::IsNullOrEmpty($agentInstallPath)) { $agentInstallPath = "$env:SystemDrive\vstsagent" } If (-NOT (Test-Path "$agentInstallPath")) { New-Item -ItemType Directory -Path $agentInstallPath -Force | Out-Null #Wait Until directory reflects in file system $Stoploop = $false [int]$Retrycount = 0 do { try { pushd $agentInstallPath popd $Stoploop = $true } catch { if ($Retrycount -gt 3) { $Stoploop = $true Write-Host "Cannot find directory: $agentInstallPath" Exit -1 } else { Write-Verbose "Wait for directory creation. Retrying in 30 seconds..." Start-Sleep -Seconds 30 $Retrycount = $Retrycount + 1 } } } While ($Stoploop -eq $false) } Write-Host "Created deployment agent install directory: $agentInstallPath" return $agentInstallPath } function Get-DeploymentAgentDownloadUrl { [CmdletBinding()] param( [Parameter(Mandatory=$true)]$accountUrl, [Parameter(Mandatory=$true)]$personalAccessToken ) $platform = "win7-x64" $downloadAPIVersion = "3.0-preview.2" $userName = "AzureDevTestLabs" [string] $restCallUrl = $accountUrl + ("/_apis/distributedtask/packages/agent/{0}?top=1&api-version={1}" -f $platform,$downloadAPIVersion) Write-Host "Agent Download REST url: $restCallUrl" $basicAuth = ("{0}:{1}" -f $userName, $personalAccessToken) $basicAuth = [System.Text.Encoding]::UTF8.GetBytes($basicAuth) $basicAuth = [System.Convert]::ToBase64String($basicAuth) $headers = @{Authorization=("Basic {0}" -f $basicAuth)} $response = Invoke-RestMethod -Uri $restCallUrl -headers $headers -Method Get -ContentType 'application/json' Write-Host "Agent Download REST response: $response" $downloadUrl = $response.Value[0].downloadUrl Write-Host "Deployment Agent download url: $downloadUrl" return $downloadUrl } function Download-DeploymentGroupAgent { [CmdletBinding()] param( [Parameter(Mandatory=$true)]$accountUrl, [Parameter(Mandatory=$true)]$personalAccessToken, [Parameter(Mandatory=$true)]$agentInstallPath, [Parameter(Mandatory=$true)]$agentZipFile ) $agentZip="$agentInstallPath\$agentZipFile"; $downloadUrl = Get-DeploymentAgentDownloadUrl $accountUrl $personalAccessToken pushd $agentInstallPath #download Write-Host "Begin Agent download" (New-Object Net.WebClient).DownloadFile($downloadUrl, $agentZip); Write-Host "Agent download complete" #extract Add-Type -AssemblyName System.IO.Compression.FileSystem;[System.IO.Compression.ZipFile]::ExtractToDirectory($agentZip, "$agentInstallPath"); Write-Host "Agent zip exacted in install directory: $agentInstallPath" popd } function Prep-MachineForAutoLogon { [CmdletBinding()] param( [Parameter(Mandatory=$false)][String][AllowEmptyString()] $windowsLogonAccount, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $windowsLogonPassword ) $ErrorActionPreference = "Stop" if ([string]::IsNullOrWhiteSpace($windowsLogonPassword)) { Write-Error "Windows logon password was not provided. Please retry by providing a valid windows logon password to enable autologon." } Write-Host "Disable Privacy Experience For auto logon after reboot" reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\OOBE" /v "DisablePrivacyExperience" /t "REG_DWORD" /d 1 /f # Create a PS session for the user to trigger the creation of the registry entries required for autologon $computerName = "localhost" $password = ConvertTo-SecureString $windowsLogonPassword -AsPlainText -Force if ($windowsLogonAccount.Split("\").Count -eq 2) { $domain = $windowsLogonAccount.Split("\")[0] $userName = $windowsLogonAccount.Split('\')[1] } else { $domain = $Env:ComputerName $userName = $windowsLogonAccount } $credentials = New-Object System.Management.Automation.PSCredential("$domain\\$userName", $password) Enter-PSSession -ComputerName $computerName -Credential $credentials Exit-PSSession try { # Check if the HKU drive already exists Get-PSDrive -PSProvider Registry -Name HKU | Out-Null $canCheckRegistry = $true } catch [System.Management.Automation.DriveNotFoundException] { try { # Create the HKU drive New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS | Out-Null $canCheckRegistry = $true } catch { # Ignore the failure to create the drive and go ahead with trying to set the agent up Write-Warning "Moving ahead with agent setup as the script failed to create HKU drive necessary for checking if the registry entry for the user's SId exists.\n$_" } } # 120 seconds timeout $timeout = 120 # Check if the registry key required for enabling autologon is present on the machine, if not wait for 120 seconds in case the user profile is still getting created while ($timeout -ge 0 -and $canCheckRegistry) { $objUser = New-Object System.Security.Principal.NTAccount($windowsLogonAccount) $securityId = $objUser.Translate([System.Security.Principal.SecurityIdentifier]) $securityId = $securityId.Value if (Test-Path "HKU:\\$securityId") { if (!(Test-Path "HKU:\\$securityId\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { New-Item -Path "HKU:\\$securityId\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run" -Force Write-Host "Created the registry entry path required to enable autologon." } break } else { $timeout -= 10 Start-Sleep(10) } } if ($timeout -lt 0) { Write-Warning "Failed to find the registry entry for the SId of the user, this is required to enable autologon. Trying to start the agent anyway." } } function Configure-DeploymentGroupAgent { [CmdletBinding()] param( [Parameter(Mandatory=$true)] $accountUrl, [Parameter(Mandatory=$true)] $projectName, [Parameter(Mandatory=$true)] $deploymentGroupName, [Parameter(Mandatory=$true)] $personalAccessToken, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $agentName, [Parameter(Mandatory=$true)] $agentInstallPath, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $deploymentAgentTags, [Parameter(Mandatory=$true)][Boolean] $runAsAutoLogon, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $windowsLogonAccount, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $windowsLogonPassword ) If ([string]::IsNullOrEmpty($agentName)) { $agentName = "$env:COMPUTERNAME" } Write-Host "Configuring Agent: $agentName" pushd $agentInstallPath if ($runAsAutoLogon) { Prep-MachineForAutoLogon -windowsLogonAccount $windowsLogonAccount -windowsLogonPassword $windowsLogonPassword # Arguements to run agent with autologon enabled $agentConfigArgs = "--unattended", "--url", $accountUrl, "--auth", "PAT", "--token", $personalAccessToken, "--deploymentgroup", "--projectname", $projectName, "--deploymentgroupname", $deploymentGroupName, "--agent", $agentName, "--replace", "--runAsAutoLogon", "--overwriteAutoLogon", "--windowslogonaccount", $windowsLogonAccount, "--work", "_work" } else { # Arguements to run agent as a service $agentConfigArgs = "--unattended", "--url", $accountUrl, "--auth", "PAT", "--token", $personalAccessToken, "--deploymentgroup", "--projectname", $projectName, "--deploymentgroupname", $deploymentGroupName, "--agent", $agentName, "--runasservice", "--windowslogonaccount", $windowsLogonAccount, "--work", "_work" } if (-not [string]::IsNullOrWhiteSpace($windowsLogonPassword)) { $agentConfigArgs += "--windowslogonpassword", $windowsLogonPassword } if(-not [string]::IsNullOrWhiteSpace($deploymentAgentTags)) { $agentConfigArgs += "--adddeploymentgrouptags", "--deploymentgrouptags", $deploymentAgentTags } & .\config.cmd $agentConfigArgs if (! $?) { Write-Host "Deployment agent configuration failed." Exit 1 } popd } function Remove-InstallFiles { [CmdletBinding()] param( [Parameter(Mandatory=$true)]$agentInstallPath, [Parameter(Mandatory=$true)]$agentZipFile ) pushd $agentInstallPath $agentZip="$agentInstallPath\$agentZipFile"; Remove-Item $agentZip; popd } function Install-DeploymentGroupAgent { [CmdletBinding()] param( [Parameter(Mandatory=$true)] $accountUrl, [Parameter(Mandatory=$true)] $projectName, [Parameter(Mandatory=$true)] $deploymentGroupName, [Parameter(Mandatory=$true)] $personalAccessToken, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $deploymentAgentTags, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $agentInstallPath, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $agentName, [Parameter(Mandatory=$true)][Boolean] $runAsAutoLogon, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $windowsLogonAccount, [Parameter(Mandatory=$false)][String][AllowEmptyString()] $windowsLogonPassword ) $ErrorActionPreference="Stop"; $agentZipFile = "agent.zip" try { #Initial checks Test-InstallPrerequisites #Create agent directory $agentInstallPath = New-AgentPath $agentInstallPath #Download Agent Download-DeploymentGroupAgent $accountUrl $personalAccessToken $agentInstallPath $agentZipFile #Configure Agent Configure-DeploymentGroupAgent $accountUrl $projectName $deploymentGroupName $personalAccessToken $agentName $agentInstallPath $deploymentAgentTags $runAsAutoLogon $windowsLogonAccount $windowsLogonPassword #Cleanup Remove-InstallFiles $agentInstallPath $agentZipFile } catch { if (($null -ne $Error[0]) -and ($null -ne $Error[0].Exception)) { if ($null -ne $Error[0].Exception.Message) { Write-Host $Error[0].Exception.Message } else { Write-Host $Error[0].Exception } } Exit -1 } } Install-DeploymentGroupAgent $accountUrl $projectName $deploymentGroupName $personalAccessToken $deploymentAgentTags $agentInstallPath $agentName $runAsAutoLogon $windowsLogonAccount $windowsLogonPassword