Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 1 | <# |
| 2 | .SYNOPSIS |
| 3 | A simple Powershell script to download and install a salt minion on windows. |
| 4 | |
| 5 | .DESCRIPTION |
| 6 | The script will download the official salt package from saltstack. It will |
| 7 | install a specific package version and accept parameters for the master and |
| 8 | minion ids. Finally, it can stop and set the windows service to "manual" for |
| 9 | local testing. |
| 10 | |
| 11 | .EXAMPLE |
| 12 | ./bootstrap-salt.ps1 |
| 13 | Runs without any parameters. Uses all the default values/settings. |
| 14 | |
| 15 | .EXAMPLE |
| 16 | ./bootstrap-salt.ps1 -version 2015.4.1-3 |
| 17 | Specifies a particular version of the installer. |
| 18 | |
| 19 | .EXAMPLE |
| 20 | ./bootstrap-salt.ps1 -runservice false |
| 21 | Specifies the salt-minion service to stop and be set to manual. Useful for |
| 22 | testing locally from the command line with the --local switch |
| 23 | |
| 24 | .EXAMPLE |
| 25 | ./bootstrap-salt.ps1 -minion minion-box -master master-box |
| 26 | Specifies the minion and master ids in the minion config. Defaults to the |
| 27 | installer values of host name for the minion id and "salt" for the master. |
| 28 | |
| 29 | .EXAMPLE |
| 30 | ./bootstrap-salt.ps1 -minion minion-box -master master-box -version 2015.5.2 -runservice false |
| 31 | Specifies all the optional parameters in no particular order. |
| 32 | |
| 33 | .PARAMETER version |
| 34 | Default version defined in this script. |
| 35 | |
| 36 | .PARAMETER runservice |
| 37 | Boolean flag to start or stop the minion service. True will start the minion |
| 38 | service. False will stop the minion service and set it to "manual". The |
| 39 | installer starts it by default. |
| 40 | |
| 41 | .PARAMETER minion |
| 42 | Name of the minion being installed on this host. Installer defaults to the |
| 43 | host name. |
| 44 | |
| 45 | .PARAMETER master |
| 46 | Name or IP of the master server. Installer defaults to "salt". |
| 47 | |
| 48 | .PARAMETER repourl |
| 49 | URL to the windows packages. Default is "https://repo.saltstack.com/windows" |
| 50 | |
| 51 | .NOTES |
| 52 | All of the parameters are optional. The default should be the latest |
| 53 | version. The architecture is dynamically determined by the script. |
| 54 | |
| 55 | .LINK |
| 56 | Bootstrap GitHub Project (script home) - https://github.com/saltstack/salt-windows-bootstrap |
| 57 | Original Vagrant Provisioner Project -https://github.com/saltstack/salty-vagrant |
| 58 | Vagrant Project (utilizes this script) - https://github.com/mitchellh/vagrant |
| 59 | SaltStack Download Location - https://repo.saltstack.com/windows/ |
| 60 | #> |
| 61 | |
| 62 | #=============================================================================== |
| 63 | # Commandlet Binding |
| 64 | #=============================================================================== |
| 65 | [CmdletBinding()] |
| 66 | Param( |
| 67 | [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| 68 | # Doesn't support versions prior to "YYYY.M.R-B" |
| 69 | [ValidatePattern('^201\d\.\d{1,2}\.\d{1,2}(\-\d{1})?|(rc\d)$')] |
| 70 | [string]$version = '', |
| 71 | |
| 72 | [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| 73 | [ValidateSet("true","false")] |
| 74 | [string]$runservice = "true", |
| 75 | |
| 76 | [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| 77 | [string]$minion = "not-specified", |
| 78 | |
| 79 | [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| 80 | [string]$master = "not-specified", |
| 81 | |
| 82 | [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| 83 | [string]$repourl= "https://repo.saltstack.com/windows" |
| 84 | ) |
| 85 | |
| 86 | #=============================================================================== |
| 87 | # Script Functions |
| 88 | #=============================================================================== |
| 89 | function Get-IsAdministrator |
| 90 | { |
| 91 | $Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() |
| 92 | $Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity) |
| 93 | $Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) |
| 94 | } |
| 95 | |
| 96 | function Get-IsUacEnabled |
| 97 | { |
| 98 | (Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System).EnableLua -ne 0 |
| 99 | } |
| 100 | |
| 101 | #=============================================================================== |
| 102 | # Check for Elevated Privileges |
| 103 | #=============================================================================== |
| 104 | If (!(Get-IsAdministrator)) { |
| 105 | If (Get-IsUacEnabled) { |
| 106 | # We are not running "as Administrator" - so relaunch as administrator |
| 107 | # Create a new process object that starts PowerShell |
| 108 | $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; |
| 109 | |
| 110 | # Specify the current script path and name as a parameter` |
| 111 | $parameters = "" |
| 112 | If($minion -ne "not-specified") {$parameters = "-minion $minion"} |
| 113 | If($master -ne "not-specified") {$parameters = "$parameters -master $master"} |
| 114 | If($runservice -eq $false) {$parameters = "$parameters -runservice false"} |
| 115 | If($version -ne '') {$parameters = "$parameters -version $version"} |
| 116 | $newProcess.Arguments = $myInvocation.MyCommand.Definition, $parameters |
| 117 | |
| 118 | # Specify the current working directory |
| 119 | $newProcess.WorkingDirectory = "$script_path" |
| 120 | |
| 121 | # Indicate that the process should be elevated |
| 122 | $newProcess.Verb = "runas"; |
| 123 | |
| 124 | # Start the new process |
| 125 | [System.Diagnostics.Process]::Start($newProcess); |
| 126 | |
| 127 | # Exit from the current, unelevated, process |
| 128 | Exit |
| 129 | } |
| 130 | Else { |
| 131 | Throw "You must be administrator to run this script" |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | #=============================================================================== |
| 136 | # Verify Parameters |
| 137 | #=============================================================================== |
| 138 | Write-Verbose "Parameters passed in:" |
| 139 | Write-Verbose "version: $version" |
| 140 | Write-Verbose "runservice: $runservice" |
| 141 | Write-Verbose "master: $master" |
| 142 | Write-Verbose "minion: $minion" |
| 143 | Write-Verbose "repourl: $repourl" |
| 144 | |
| 145 | If ($runservice.ToLower() -eq "true") { |
| 146 | Write-Verbose "Windows service will be set to run" |
| 147 | [bool]$runservice = $True |
| 148 | } |
| 149 | ElseIf ($runservice.ToLower() -eq "false") { |
| 150 | Write-Verbose "Windows service will be stopped and set to manual" |
| 151 | [bool]$runservice = $False |
| 152 | } |
| 153 | Else { |
| 154 | # Param passed in wasn't clear so defaulting to true. |
| 155 | Write-Verbose "Windows service defaulting to run automatically" |
| 156 | [bool]$runservice = $True |
| 157 | } |
| 158 | |
| 159 | #=============================================================================== |
| 160 | # Ensure Directories are present, copy Vagrant Configs if found |
| 161 | #=============================================================================== |
| 162 | # Create C:\tmp\ |
| 163 | New-Item C:\tmp\ -ItemType directory -Force | Out-Null |
| 164 | |
| 165 | # Copy Vagrant Files to their proper location. Vagrant files will be placed |
| 166 | # in C:\tmp |
| 167 | # Check if minion keys have been uploaded, copy to correct location |
| 168 | If (Test-Path C:\tmp\minion.pem) { |
| 169 | New-Item C:\salt\conf\pki\minion\ -ItemType Directory -Force | Out-Null |
| 170 | # Copy minion keys & config to correct location |
| 171 | cp C:\tmp\minion.pem C:\salt\conf\pki\minion\ |
| 172 | cp C:\tmp\minion.pub C:\salt\conf\pki\minion\ |
| 173 | } |
| 174 | |
| 175 | # Check if minion config has been uploaded |
| 176 | # This should be done before the installer is run so that it can be updated with |
| 177 | # id: and master: settings when the installer runs |
| 178 | If (Test-Path C:\tmp\minion) { |
| 179 | New-Item C:\salt\conf\ -ItemType Directory -Force | Out-Null |
| 180 | Copy-Item -Path C:\tmp\minion -Destination C:\salt\conf\ -Force | Out-Null |
| 181 | } |
| 182 | |
| 183 | #=============================================================================== |
| 184 | # Detect architecture |
| 185 | #=============================================================================== |
| 186 | If ([IntPtr]::Size -eq 4) { |
| 187 | $arch = "x86" |
| 188 | } |
| 189 | Else { |
| 190 | $arch = "AMD64" |
| 191 | } |
| 192 | |
| 193 | #=============================================================================== |
| 194 | # Figure out the latest version if no version is passed |
| 195 | #=============================================================================== |
| 196 | # If version isn't supplied, use latest. |
| 197 | If (!$version) { |
| 198 | # Find latest version of Salt Minion |
| 199 | $repo = Invoke-Restmethod "$repourl" |
| 200 | $regex = "<\s*a\s*[^>]*?href\s*=\s*[`"']*([^`"'>]+)[^>]*?>" |
| 201 | $returnMatches = New-Object System.Collections.ArrayList |
| 202 | $resultingMatches = [Regex]::Matches($repo, $regex, "IgnoreCase") |
| 203 | foreach($match in $resultingMatches) { |
| 204 | $cleanedMatch = $match.Groups[1].Value.Trim() |
| 205 | [void] $returnMatches.Add($cleanedMatch) |
| 206 | } |
| 207 | If ($arch -eq 'x86') { |
| 208 | $returnMatches = $returnMatches | Where {$_ -like "Salt-Minion*x86-Setup.exe"} |
| 209 | } |
| 210 | Else { |
| 211 | $returnMatches = $returnMatches | Where {$_ -like "Salt-Minion*AMD64-Setup.exe"} |
| 212 | } |
| 213 | |
| 214 | $version = $(($returnMatches | Sort-Object -Descending)[0]).Split(("n-","-A","-x"),([System.StringSplitOptions]::RemoveEmptyEntries))[1] |
| 215 | } |
| 216 | |
| 217 | #=============================================================================== |
| 218 | # Download minion setup file |
| 219 | #=============================================================================== |
| 220 | $saltExe = "Salt-Minion-$version-$arch-Setup.exe" |
| 221 | Write-Output "Downloading Salt minion installer $saltExe" |
| 222 | $webclient = New-Object System.Net.WebClient |
| 223 | $url = "$repourl/$saltExe" |
| 224 | $file = "C:\Windows\Temp\$saltExe" |
| 225 | $webclient.DownloadFile($url, $file) |
| 226 | |
| 227 | #=============================================================================== |
| 228 | # Set the parameters for the installer |
| 229 | #=============================================================================== |
| 230 | # Unless specified, use the installer defaults |
| 231 | # - id: <hostname> |
| 232 | # - master: salt |
| 233 | # - Start the service |
| 234 | $parameters = "" |
| 235 | If($minion -ne "not-specified") {$parameters = "/minion-name=$minion"} |
| 236 | If($master -ne "not-specified") {$parameters = "$parameters /master=$master"} |
| 237 | If($runservice -eq $false) {$parameters = "$parameters /start-service=0"} |
| 238 | |
| 239 | #=============================================================================== |
| 240 | # Install minion silently |
| 241 | #=============================================================================== |
| 242 | #Wait for process to exit before continuing. |
| 243 | Write-Output "Installing Salt minion" |
| 244 | Start-Process C:\Windows\Temp\$saltExe -ArgumentList "/S $parameters" -Wait -NoNewWindow -PassThru | Out-Null |
| 245 | |
| 246 | #=============================================================================== |
| 247 | # Configure the minion service |
| 248 | #=============================================================================== |
| 249 | # Wait for salt-minion service to be registered before trying to start it |
| 250 | $service = Get-Service salt-minion -ErrorAction SilentlyContinue |
| 251 | While (!$service) { |
| 252 | Start-Sleep -s 2 |
| 253 | $service = Get-Service salt-minion -ErrorAction SilentlyContinue |
| 254 | } |
| 255 | |
| 256 | If($runservice) { |
| 257 | # Start service |
| 258 | Start-Service -Name "salt-minion" -ErrorAction SilentlyContinue |
| 259 | |
| 260 | # Check if service is started, otherwise retry starting the |
| 261 | # service 4 times. |
| 262 | $try = 0 |
| 263 | While (($service.Status -ne "Running") -and ($try -ne 4)) { |
| 264 | Start-Service -Name "salt-minion" -ErrorAction SilentlyContinue |
| 265 | $service = Get-Service salt-minion -ErrorAction SilentlyContinue |
| 266 | Start-Sleep -s 2 |
| 267 | $try += 1 |
| 268 | } |
| 269 | |
| 270 | # If the salt-minion service is still not running, something probably |
| 271 | # went wrong and user intervention is required - report failure. |
| 272 | If ($service.Status -eq "Stopped") { |
| 273 | Write-Output -NoNewline "Failed to start salt minion" |
| 274 | exit 1 |
| 275 | } |
| 276 | } |
| 277 | Else { |
| 278 | Write-Output -NoNewline "Stopping salt minion and setting it to 'Manual'" |
| 279 | Set-Service "salt-minion" -StartupType "Manual" |
| 280 | Stop-Service "salt-minion" |
| 281 | } |
| 282 | |
| 283 | #=============================================================================== |
| 284 | # Script Complete |
| 285 | #=============================================================================== |
| 286 | Write-Output "Salt minion successfully installed" |