Artifacts/windows-docker/Artifactfile.ps1 (292 lines of code) (raw):

# # Optional parameters to this script file. # [CmdletBinding()] param( [Parameter(ParameterSetName='CustomUser')] [string] $UserName = 'artifactInstaller', [Parameter(ParameterSetName='CustomUser')] [string] $Password, [int] $PSVersionRequired = 3 ) ################################################################################################### # # PowerShell configurations # # NOTE: Because the $ErrorActionPreference is "Stop", this script will stop on first failure. # This is necessary to ensure we capture errors inside the try-catch-finally block. $ErrorActionPreference = "Stop" # Hide any progress bars, due to downloads and installs of remote components. $ProgressPreference = "SilentlyContinue" # Ensure we force use of TLS 1.2 for all downloads. [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Discard any collected errors from a previous execution. $Error.Clear() # Allow certian operations, like downloading files, to execute. Set-ExecutionPolicy Bypass -Scope Process -Force ################################################################################################### # # Functions used in this script. # function Handle-LastError { [CmdletBinding()] param( ) $message = $error[0].Exception.Message if ($message) { Write-Host -Object "ERROR: $message" -ForegroundColor Red } # IMPORTANT NOTE: Throwing a terminating error (using $ErrorActionPreference = "Stop") still # returns exit code zero from the PowerShell script when using -File. The workaround is to # NOT use -File when calling this script and leverage the try-catch-finally block and return # a non-zero exit code from the catch block. exit -1 } function Validate-Environment { [CmdletBinding()] param( ) $minVersionServer = [System.Version]::Parse("10.0.14393.0") $minVersionClient = [System.Version]::Parse("10.0.14393.222") $productType = Get-CimInstance Win32_OperatingSystem | select -ExpandProperty ProductType # Product Type Values # 1 - Work Station # 2 - Domain Controller # 3 - Server $curVersion = [System.Environment]::OSVersion.Version $minVersion = [System.Version]::Parse("0.0.0.0") if ($productType -eq 1) { $minVersion = $minVersionClient } else { $minVersion = $minVersionServer } if ($curVersion -lt $minVersion) { throw "OS version must at least Windows 10 ($minVersionClient) or Windows Server 2016 ($minVersionServer)." } } function Validate-Params { [CmdletBinding()] param( ) if ($PsCmdlet.ParameterSetName -eq 'CustomUser') { if ([string]::IsNullOrEmpty($UserName)) { throw 'UserName parameter is required when Password is specified.' } if ([string]::IsNullOrEmpty($Password)) { throw 'Password parameter is required when UserName is specified.' } } } function Ensure-PowerShell { [CmdletBinding()] param( [int] $Version ) if ($PSVersionTable.PSVersion.Major -lt $Version) { throw "The current version of PowerShell is $($PSVersionTable.PSVersion.Major). Prior to running this artifact, ensure you have PowerShell $Version or higher installed." } } function Get-VMSize { [CmdletBinding()] param ( ) $vmSize = Invoke-RestMethod -Headers @{"Metadata"="true"} -URI "http://169.254.169.254/metadata/instance/compute/vmSize?api-version=2017-04-02&format=text" -Method Get return $vmSize } function Test-NestedVirtualizationSupport { [CmdletBinding()] param ( ) $vmSize = Get-VMSize # CAUTION !!! # There's no reliable way other than using the VMSize to identify support for nested virtualization yet! return [bool] ($vmSize -match "Standard_[D|E]{1}\d{1,2}[d]?[s]?_v[3|4]") } function Get-TempPassword { [CmdletBinding()] param( [int] $length = 43 ) $sourceData = $null 33..126 | % { $sourceData +=,[char][byte]$_ } 1..$length | % { $tempPassword += ($sourceData | Get-Random) } return $tempPassword } function Add-LocalAdminUser { [CmdletBinding()] param( [string] $UserName, [string] $Password, [string] $Description = 'DevTestLab artifact installer', [switch] $Overwrite = $true ) if ($Overwrite) { Remove-LocalAdminUser -UserName $UserName } $computer = [ADSI]"WinNT://$env:ComputerName" $user = $computer.Create("User", $UserName) $user.SetPassword($Password) $user.Put("Description", $Description) $user.SetInfo() $group = [ADSI]"WinNT://$env:ComputerName/Administrators,group" $group.add("WinNT://$env:ComputerName/$UserName") return $user } function Remove-LocalAdminUser { [CmdletBinding()] param( [string] $UserName ) if ([ADSI]::Exists('WinNT://./' + $UserName)) { $computer = [ADSI]"WinNT://$env:ComputerName" $computer.Delete('User', $UserName) try { gwmi win32_userprofile | ? { $_.LocalPath -like "*$UserName*" -and -not $_.Loaded } | % { $_.Delete() | Out-Null } } catch { # Ignore any errors, specially with locked folders/files. It will get cleaned up at a later time, when another artifact is installed. } } } function Set-LocalAccountTokenFilterPolicy { [CmdletBinding()] param( [int] $Value = 1 ) $oldValue = 0 $regPath ='HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System' $policy = Get-ItemProperty -Path $regPath -Name LocalAccountTokenFilterPolicy -ErrorAction SilentlyContinue if ($policy) { $oldValue = $policy.LocalAccountTokenFilterPolicy } if ($oldValue -ne $Value) { Set-ItemProperty -Path $regPath -Name LocalAccountTokenFilterPolicy -Value $Value } return $oldValue } function Invoke-ChocolateyPackageInstaller { [CmdletBinding()] param( [string] $UserName, [string] $Password, [string] $PackageList ) $secPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force $credential = New-Object System.Management.Automation.PSCredential("$env:COMPUTERNAME\$($UserName)", $secPassword) $command = "$PSScriptRoot\ChocolateyPackageInstaller.ps1" $oldPolicyValue = Set-LocalAccountTokenFilterPolicy try { Invoke-Command -ComputerName $env:COMPUTERNAME -Credential $credential -FilePath $command -ArgumentList $PackageList } finally { Set-LocalAccountTokenFilterPolicy -Value $oldPolicyValue | Out-Null } } ################################################################################################### # # Main execution block. # try { Validate-Params Validate-Environment Ensure-PowerShell -Version $PSVersionRequired Install-PackageProvider -Name NuGet -Force -ErrorAction SilentlyContinue | Out-Null Install-Module -Name DockerMsftProvider -Repository PSGallery -Force -ErrorAction SilentlyContinue | Out-Null Install-Package -Name docker -ProviderName DockerMsftProvider -Force -ErrorAction SilentlyContinue | Out-Null Enable-PSRemoting -Force -SkipNetworkProfileCheck if ($PsCmdlet.ParameterSetName -ne 'CustomUser') { $Password = Get-TempPassword Add-LocalAdminUser -UserName $UserName -Password $password | Out-Null } if (Test-NestedVirtualizationSupport) { $productType = Get-CimInstance Win32_OperatingSystem | select -ExpandProperty ProductType # Product Type Values # 1 - Work Station # 2 - Domain Controller # 3 - Server if ($productType -eq 1) { # Windows 10 if ((Get-WindowsOptionalFeature -Online -FeatureName containers | select -ExpandProperty State) -eq "Disabled") { Enable-WindowsOptionalFeature -Online -FeatureName containers -All -NoRestart } if ((Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V | select -ExpandProperty State) -eq "Disabled") { Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All -NoRestart } } else { # Windows Server 2016 if ((Get-WindowsFeature -Name Hyper-V | select -ExpandProperty InstallState) -eq "Available") { Install-WindowsFeature -Name Hyper-V -IncludeManagementTools | Out-Null } } # use the GA version of docker for windows and disable checksum checks (checksum is always outdated in the nuget package) Invoke-ChocolateyPackageInstaller -UserName $UserName -Password $Password -PackageList "docker-desktop --ignore-checksums" $dockerPath = Join-Path $env:programfiles "docker" if (Test-Path -Path $dockerPath -PathType Container) { Invoke-ChocolateyPackageInstaller -UserName $UserName -Password $Password -PackageList "docker-kitematic" $kitematicPath = Join-Path $env:programdata "chocolatey\lib\docker-kitematic\tools" if (Test-Path -Path $kitematicPath -PathType Container) { $dockerKitematicPath = Join-Path $dockerPath "Kitematic" if (Test-Path -Path $dockerKitematicPath) { Remove-Item $dockerKitematicPath -Recurse -Force } # redirect default kitematic folder under docker to the chocolatey package folder New-Item -Path $dockerKitematicPath -ItemType SymbolicLink -Target $kitematicPath | Out-Null } $dockerGroup = ([ADSI]"WinNT://$env:ComputerName/docker-users,group") if ($dockerGroup) { # grant local users to docker-desktop ([ADSI]"WinNT://$env:ComputerName").Children | ? { $_.SchemaClassName -eq 'user' } | % { try { $dockerGroup.add($_.Path) } catch {} } } # ensure docker 4 windows autostart for the very first login Set-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce" -Name "Docker for Windows" -Value (Join-Path $dockerPath "Docker\Docker for Windows.exe") } else { $message = "Could not find Docker installation path '$dockerPath'." throw $message } } } catch { Handle-LastError } finally { if ($PsCmdlet.ParameterSetName -ne 'CustomUser') { Remove-LocalAdminUser -UserName $UserName } }