| <# |
| .SYNOPSIS |
| A simple Powershell script to download and install a salt minion on windows. |
| |
| .DESCRIPTION |
| The script will download the official salt package from saltstack. It will |
| install a specific package version and accept parameters for the master and |
| minion ids. Finally, it can stop and set the windows service to "manual" for |
| local testing. |
| |
| .EXAMPLE |
| ./bootstrap-salt.ps1 |
| Runs without any parameters. Uses all the default values/settings. |
| |
| .EXAMPLE |
| ./bootstrap-salt.ps1 -version 2015.4.1-3 |
| Specifies a particular version of the installer. |
| |
| .EXAMPLE |
| ./bootstrap-salt.ps1 -runservice false |
| Specifies the salt-minion service to stop and be set to manual. Useful for |
| testing locally from the command line with the --local switch |
| |
| .EXAMPLE |
| ./bootstrap-salt.ps1 -minion minion-box -master master-box |
| Specifies the minion and master ids in the minion config. Defaults to the |
| installer values of host name for the minion id and "salt" for the master. |
| |
| .EXAMPLE |
| ./bootstrap-salt.ps1 -minion minion-box -master master-box -version 2015.5.2 -runservice false |
| Specifies all the optional parameters in no particular order. |
| |
| .PARAMETER version |
| Default version defined in this script. |
| |
| .PARAMETER runservice |
| Boolean flag to start or stop the minion service. True will start the minion |
| service. False will stop the minion service and set it to "manual". The |
| installer starts it by default. |
| |
| .PARAMETER minion |
| Name of the minion being installed on this host. Installer defaults to the |
| host name. |
| |
| .PARAMETER master |
| Name or IP of the master server. Installer defaults to "salt". |
| |
| .PARAMETER repourl |
| URL to the windows packages. Default is "https://repo.saltstack.com/windows" |
| |
| .NOTES |
| All of the parameters are optional. The default should be the latest |
| version. The architecture is dynamically determined by the script. |
| |
| .LINK |
| Bootstrap GitHub Project (script home) - https://github.com/saltstack/salt-windows-bootstrap |
| Original Vagrant Provisioner Project -https://github.com/saltstack/salty-vagrant |
| Vagrant Project (utilizes this script) - https://github.com/mitchellh/vagrant |
| SaltStack Download Location - https://repo.saltstack.com/windows/ |
| #> |
| |
| #=============================================================================== |
| # Commandlet Binding |
| #=============================================================================== |
| [CmdletBinding()] |
| Param( |
| [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| # Doesn't support versions prior to "YYYY.M.R-B" |
| [ValidatePattern('^201\d\.\d{1,2}\.\d{1,2}(\-\d{1})?|(rc\d)$')] |
| [string]$version = '', |
| |
| [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| [ValidateSet("true","false")] |
| [string]$runservice = "true", |
| |
| [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| [string]$minion = "not-specified", |
| |
| [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| [string]$master = "not-specified", |
| |
| [Parameter(Mandatory=$false,ValueFromPipeline=$true)] |
| [string]$repourl= "https://repo.saltstack.com/windows" |
| ) |
| |
| #=============================================================================== |
| # Script Functions |
| #=============================================================================== |
| function Get-IsAdministrator |
| { |
| $Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() |
| $Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity) |
| $Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) |
| } |
| |
| function Get-IsUacEnabled |
| { |
| (Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System).EnableLua -ne 0 |
| } |
| |
| #=============================================================================== |
| # Check for Elevated Privileges |
| #=============================================================================== |
| If (!(Get-IsAdministrator)) { |
| If (Get-IsUacEnabled) { |
| # We are not running "as Administrator" - so relaunch as administrator |
| # Create a new process object that starts PowerShell |
| $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; |
| |
| # Specify the current script path and name as a parameter` |
| $parameters = "" |
| If($minion -ne "not-specified") {$parameters = "-minion $minion"} |
| If($master -ne "not-specified") {$parameters = "$parameters -master $master"} |
| If($runservice -eq $false) {$parameters = "$parameters -runservice false"} |
| If($version -ne '') {$parameters = "$parameters -version $version"} |
| $newProcess.Arguments = $myInvocation.MyCommand.Definition, $parameters |
| |
| # Specify the current working directory |
| $newProcess.WorkingDirectory = "$script_path" |
| |
| # Indicate that the process should be elevated |
| $newProcess.Verb = "runas"; |
| |
| # Start the new process |
| [System.Diagnostics.Process]::Start($newProcess); |
| |
| # Exit from the current, unelevated, process |
| Exit |
| } |
| Else { |
| Throw "You must be administrator to run this script" |
| } |
| } |
| |
| #=============================================================================== |
| # Verify Parameters |
| #=============================================================================== |
| Write-Verbose "Parameters passed in:" |
| Write-Verbose "version: $version" |
| Write-Verbose "runservice: $runservice" |
| Write-Verbose "master: $master" |
| Write-Verbose "minion: $minion" |
| Write-Verbose "repourl: $repourl" |
| |
| If ($runservice.ToLower() -eq "true") { |
| Write-Verbose "Windows service will be set to run" |
| [bool]$runservice = $True |
| } |
| ElseIf ($runservice.ToLower() -eq "false") { |
| Write-Verbose "Windows service will be stopped and set to manual" |
| [bool]$runservice = $False |
| } |
| Else { |
| # Param passed in wasn't clear so defaulting to true. |
| Write-Verbose "Windows service defaulting to run automatically" |
| [bool]$runservice = $True |
| } |
| |
| #=============================================================================== |
| # Ensure Directories are present, copy Vagrant Configs if found |
| #=============================================================================== |
| # Create C:\tmp\ |
| New-Item C:\tmp\ -ItemType directory -Force | Out-Null |
| |
| # Copy Vagrant Files to their proper location. Vagrant files will be placed |
| # in C:\tmp |
| # Check if minion keys have been uploaded, copy to correct location |
| If (Test-Path C:\tmp\minion.pem) { |
| New-Item C:\salt\conf\pki\minion\ -ItemType Directory -Force | Out-Null |
| # Copy minion keys & config to correct location |
| cp C:\tmp\minion.pem C:\salt\conf\pki\minion\ |
| cp C:\tmp\minion.pub C:\salt\conf\pki\minion\ |
| } |
| |
| # Check if minion config has been uploaded |
| # This should be done before the installer is run so that it can be updated with |
| # id: and master: settings when the installer runs |
| If (Test-Path C:\tmp\minion) { |
| New-Item C:\salt\conf\ -ItemType Directory -Force | Out-Null |
| Copy-Item -Path C:\tmp\minion -Destination C:\salt\conf\ -Force | Out-Null |
| } |
| |
| #=============================================================================== |
| # Detect architecture |
| #=============================================================================== |
| If ([IntPtr]::Size -eq 4) { |
| $arch = "x86" |
| } |
| Else { |
| $arch = "AMD64" |
| } |
| |
| #=============================================================================== |
| # Figure out the latest version if no version is passed |
| #=============================================================================== |
| # If version isn't supplied, use latest. |
| If (!$version) { |
| # Find latest version of Salt Minion |
| $repo = Invoke-Restmethod "$repourl" |
| $regex = "<\s*a\s*[^>]*?href\s*=\s*[`"']*([^`"'>]+)[^>]*?>" |
| $returnMatches = New-Object System.Collections.ArrayList |
| $resultingMatches = [Regex]::Matches($repo, $regex, "IgnoreCase") |
| foreach($match in $resultingMatches) { |
| $cleanedMatch = $match.Groups[1].Value.Trim() |
| [void] $returnMatches.Add($cleanedMatch) |
| } |
| If ($arch -eq 'x86') { |
| $returnMatches = $returnMatches | Where {$_ -like "Salt-Minion*x86-Setup.exe"} |
| } |
| Else { |
| $returnMatches = $returnMatches | Where {$_ -like "Salt-Minion*AMD64-Setup.exe"} |
| } |
| |
| $version = $(($returnMatches | Sort-Object -Descending)[0]).Split(("n-","-A","-x"),([System.StringSplitOptions]::RemoveEmptyEntries))[1] |
| } |
| |
| #=============================================================================== |
| # Download minion setup file |
| #=============================================================================== |
| $saltExe = "Salt-Minion-$version-$arch-Setup.exe" |
| Write-Output "Downloading Salt minion installer $saltExe" |
| $webclient = New-Object System.Net.WebClient |
| $url = "$repourl/$saltExe" |
| $file = "C:\Windows\Temp\$saltExe" |
| $webclient.DownloadFile($url, $file) |
| |
| #=============================================================================== |
| # Set the parameters for the installer |
| #=============================================================================== |
| # Unless specified, use the installer defaults |
| # - id: <hostname> |
| # - master: salt |
| # - Start the service |
| $parameters = "" |
| If($minion -ne "not-specified") {$parameters = "/minion-name=$minion"} |
| If($master -ne "not-specified") {$parameters = "$parameters /master=$master"} |
| If($runservice -eq $false) {$parameters = "$parameters /start-service=0"} |
| |
| #=============================================================================== |
| # Install minion silently |
| #=============================================================================== |
| #Wait for process to exit before continuing. |
| Write-Output "Installing Salt minion" |
| Start-Process C:\Windows\Temp\$saltExe -ArgumentList "/S $parameters" -Wait -NoNewWindow -PassThru | Out-Null |
| |
| #=============================================================================== |
| # Configure the minion service |
| #=============================================================================== |
| # Wait for salt-minion service to be registered before trying to start it |
| $service = Get-Service salt-minion -ErrorAction SilentlyContinue |
| While (!$service) { |
| Start-Sleep -s 2 |
| $service = Get-Service salt-minion -ErrorAction SilentlyContinue |
| } |
| |
| If($runservice) { |
| # Start service |
| Start-Service -Name "salt-minion" -ErrorAction SilentlyContinue |
| |
| # Check if service is started, otherwise retry starting the |
| # service 4 times. |
| $try = 0 |
| While (($service.Status -ne "Running") -and ($try -ne 4)) { |
| Start-Service -Name "salt-minion" -ErrorAction SilentlyContinue |
| $service = Get-Service salt-minion -ErrorAction SilentlyContinue |
| Start-Sleep -s 2 |
| $try += 1 |
| } |
| |
| # If the salt-minion service is still not running, something probably |
| # went wrong and user intervention is required - report failure. |
| If ($service.Status -eq "Stopped") { |
| Write-Output -NoNewline "Failed to start salt minion" |
| exit 1 |
| } |
| } |
| Else { |
| Write-Output -NoNewline "Stopping salt minion and setting it to 'Manual'" |
| Set-Service "salt-minion" -StartupType "Manual" |
| Stop-Service "salt-minion" |
| } |
| |
| #=============================================================================== |
| # Script Complete |
| #=============================================================================== |
| Write-Output "Salt minion successfully installed" |