diff --git a/REFERENCE.md b/REFERENCE.md
index 8f2a2319e..e8b419ea5 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -6,6 +6,8 @@
### Classes
+#### Public Classes
+
* [`puppet_agent`](#puppet_agent): Upgrades Puppet 4 and newer to the requested version.
* [`puppet_agent::configure`](#puppet_agent--configure): Uses $puppet_agent::config to manage settings in puppet.conf.
* [`puppet_agent::install`](#puppet_agent--install): This class is called from puppet_agent for install.
@@ -22,10 +24,13 @@
* [`puppet_agent::osfamily::windows`](#puppet_agent--osfamily--windows): Determines the puppet-agent package location for Windows OSes.
* [`puppet_agent::params`](#puppet_agent--params): Sets variables according to platform.
* [`puppet_agent::prepare`](#puppet_agent--prepare): This class is called from puppet_agent to prepare for the upgrade.
-* [`puppet_agent::prepare::package`](#puppet_agent--prepare--package): Ensures correct puppet-agent package is downloaded locally.
* [`puppet_agent::prepare::puppet_config`](#puppet_agent--prepare--puppet_config): Private class called from puppet_agent::prepare class.
* [`puppet_agent::service`](#puppet_agent--service): Ensures that managed services are running.
+#### Private Classes
+
+* `puppet_agent::prepare::package`: Ensures correct puppet-agent package is downloaded locally.
+
### Resource types
* [`puppet_agent_end_run`](#puppet_agent_end_run): Stops the current Puppet run if a puppet-agent upgrade was performed. Used on platforms that manage the Puppet Agent upgrade with a package r
@@ -614,24 +619,6 @@ The puppet-agent version to install.
Default value: `undef`
-### `puppet_agent::prepare::package`
-
-for installation. This is used on platforms without package managers capable of
-working with a remote https repository.
-
-#### Parameters
-
-The following parameters are available in the `puppet_agent::prepare::package` class:
-
-* [`source`](#-puppet_agent--prepare--package--source)
-
-##### `source`
-
-Data type: `Variant[String, Array]`
-
-The source file for the puppet-agent package. Can use any of the data types
-and protocols that the File resource's source attribute can.
-
### `puppet_agent::prepare::puppet_config`
Private class called from puppet_agent::prepare class.
@@ -993,6 +980,18 @@ Data type: `Optional[Integer]`
The number of retries in case of network connectivity failures
+##### `username`
+
+Data type: `Optional[String[1]]`
+
+The username to use when downloading from a source location requiring authentication
+
+##### `password`
+
+Data type: `Optional[Sensitive[String[1]]]`
+
+The password to use when downloading from a source location requiring authentication
+
### `install_shell`
Install the Puppet agent package
@@ -1063,13 +1062,13 @@ The number of retries in case of network connectivity failures
##### `username`
-Data type: `Optional[String]`
+Data type: `Optional[String[1]]`
The username to use when downloading from a source location requiring authentication
##### `password`
-Data type: `Optional[String]`
+Data type: `Optional[Sensitive[String[1]]]`
The password to use when downloading from a source location requiring authentication
diff --git a/manifests/init.pp b/manifests/init.pp
index 9fad7b6b5..502f19533 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -3,7 +3,9 @@
# @param arch
# The package architecture. Defaults to the architecture fact.
# @param collection
-# The Puppet Collection to track. Defaults to 'PC1'.
+# The Puppet Collection to track. Defaults to 'PC1'. Valid values are puppet7,
+# puppet8, puppet, puppet7-nightly, puppet8-nightly, puppet-nightly,
+# puppetcore7, puppetcore8.
# @param is_pe
# Install from Puppet Enterprise repos. Enabled if communicating with a PE master.
# @param manage_pki_dir
diff --git a/manifests/osfamily/windows.pp b/manifests/osfamily/windows.pp
index 8114ddeb0..98ed62ae8 100644
--- a/manifests/osfamily/windows.pp
+++ b/manifests/osfamily/windows.pp
@@ -23,13 +23,22 @@
} else {
if $puppet_agent::collection == 'PC1' {
$source = "${puppet_agent::windows_source}/windows/${puppet_agent::package_name}-${puppet_agent::prepare::package_version}-${puppet_agent::arch}.msi"
+ } elsif $puppet_agent::collection =~ /core/ {
+ $source = 'https://artifacts-puppetcore.puppet.com/v1/download'
} else {
$source = "${puppet_agent::windows_source}/windows/${puppet_agent::collection}/${puppet_agent::package_name}-${puppet_agent::prepare::package_version}-${puppet_agent::arch}.msi"
}
}
+ $destination_name = if $puppet_agent::collection =~ /core/ {
+ "${puppet_agent::package_name}-${puppet_agent::prepare::package_version}-${puppet_agent::arch}.msi"
+ } else {
+ undef
+ }
+
class { 'puppet_agent::prepare::package':
- source => $source,
+ source => $source,
+ destination_name => $destination_name,
}
contain puppet_agent::prepare::package
diff --git a/manifests/prepare/package.pp b/manifests/prepare/package.pp
index dee0afa29..a1b1e549f 100644
--- a/manifests/prepare/package.pp
+++ b/manifests/prepare/package.pp
@@ -2,11 +2,10 @@
# for installation. This is used on platforms without package managers capable of
# working with a remote https repository.
#
-# @param source
-# The source file for the puppet-agent package. Can use any of the data types
-# and protocols that the File resource's source attribute can.
+# @api private
class puppet_agent::prepare::package (
Variant[String, Array] $source,
+ Optional[String[1]] $destination_name = undef
) {
assert_private()
@@ -14,12 +13,17 @@
ensure => directory,
}
- # In order for the 'basename' function to work correctly we need to change
- # any \s to /s (even for windows UNC paths) so that it will correctly pull off
- # the filename. Since this operation is only grabbing the base filename and not
- # any part of the path this should be safe, since the source will simply remain
- # what it was before and we can still pull off the filename.
- $package_file_name = basename(regsubst($source, "\\\\", '/', 'G'))
+ $package_file_name = if $destination_name {
+ $destination_name
+ } else {
+ # In order for the 'basename' function to work correctly we need to change
+ # any \s to /s (even for windows UNC paths) so that it will correctly pull off
+ # the filename. Since this operation is only grabbing the base filename and not
+ # any part of the path this should be safe, since the source will simply remain
+ # what it was before and we can still pull off the filename.
+ basename(regsubst($source, "\\\\", '/', 'G'))
+ }
+
if $facts['os']['family'] =~ /windows/ {
$local_package_file_path = windows_native_path("${puppet_agent::params::local_packages_dir}/${package_file_name}")
$mode = undef
@@ -28,12 +32,38 @@
$mode = '0644'
}
- file { $local_package_file_path:
- ensure => file,
- owner => $puppet_agent::params::user,
- group => $puppet_agent::params::group,
- mode => $mode,
- source => $source,
- require => File[$puppet_agent::params::local_packages_dir],
+ if $puppet_agent::collection =~ /core/ and $facts['os']['family'] =~ /windows/ {
+ $download_username = getvar('puppet_agent::username', 'forge-key')
+ $download_password = unwrap(getvar('puppet_agent::password'))
+ $dev = count(split($puppet_agent::prepare::package_version, '\.')) > 3
+
+ $_download_puppet = windows_native_path("${facts['env_temp_variable']}/download_puppet.ps1")
+ file { $_download_puppet:
+ ensure => file,
+ content => Sensitive(epp('puppet_agent/download_puppet.ps1.epp')),
+ }
+
+ exec { 'Download Puppet Agent':
+ command => [
+ "${facts['os']['windows']['system32']}\\WindowsPowerShell\\v1.0\\powershell.exe",
+ '-ExecutionPolicy',
+ 'Bypass',
+ '-NoProfile',
+ '-NoLogo',
+ '-NonInteractive',
+ $_download_puppet
+ ],
+ creates => $local_package_file_path,
+ require => File[$puppet_agent::params::local_packages_dir],
+ }
+ } else {
+ file { $local_package_file_path:
+ ensure => file,
+ owner => $puppet_agent::params::user,
+ group => $puppet_agent::params::group,
+ mode => $mode,
+ source => $source,
+ require => File[$puppet_agent::params::local_packages_dir],
+ }
}
}
diff --git a/metadata.json b/metadata.json
index d3e2f5152..f49b300a2 100644
--- a/metadata.json
+++ b/metadata.json
@@ -23,6 +23,10 @@
{
"name": "puppetlabs-facts",
"version_requirement": ">= 0.5.0 < 2.0.0"
+ },
+ {
+ "name": "puppetlabs-powershell",
+ "version_requirement": ">= 6.0.2 < 7.0.0"
}
],
"operatingsystem_support": [
diff --git a/tasks/install_powershell.json b/tasks/install_powershell.json
index 868a95c65..65b0c8bc2 100644
--- a/tasks/install_powershell.json
+++ b/tasks/install_powershell.json
@@ -42,6 +42,14 @@
"description": "The number of retries in case of network connectivity failures",
"type": "Optional[Integer]",
"default": 5
+ },
+ "username": {
+ "description": "The username to use when downloading from a source location requiring authentication",
+ "type": "Optional[String[1]]"
+ },
+ "password": {
+ "description": "The password to use when downloading from a source location requiring authentication",
+ "type": "Optional[Sensitive[String[1]]]"
}
},
"supports_noop": true
diff --git a/tasks/install_powershell.ps1 b/tasks/install_powershell.ps1
index 679baa07b..ce67f2eec 100644
--- a/tasks/install_powershell.ps1
+++ b/tasks/install_powershell.ps1
@@ -7,11 +7,21 @@ Param(
[String]$install_options = 'REINSTALLMODE="amus"',
[Bool]$stop_service = $False,
[Int]$retry = 5,
- [Bool]$_noop = $False
+ [Bool]$_noop = $False,
+ [String]$username = 'forge-key',
+ [String]$password
)
# If an error is encountered, the script will stop instead of the default of "Continue"
$ErrorActionPreference = "Stop"
+try {
+ $os_version = (Get-WmiObject Win32_OperatingSystem).Version
+}
+catch [System.Management.Automation.CommandNotFoundException] {
+ $os_version = (Get-CimInstance -ClassName win32_OperatingSystem).Version
+}
+$major_os_version = ($os_version -split '\.')[0]
+
try {
if ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -match '^32') {
$arch = "x86"
@@ -27,9 +37,19 @@ catch [System.Management.Automation.CommandNotFoundException] {
}
}
+$fips = 'false'
+try {
+ if ((Get-ItemPropertyValue -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy' -Name Enabled) -ne 0) {
+ $fips = 'true'
+ }
+}
+catch {
+ Write-Output "Failed to lookup FIPS mode, assuming it is disabled"
+}
+
function Test-PuppetInstalled {
$rootPath = 'HKLM:\SOFTWARE\Puppet Labs\Puppet'
- try {
+ try {
if (Get-ItemProperty -Path $rootPath) { RETURN $true }
}
catch {
@@ -98,12 +118,21 @@ if (Test-RunningServices) {
# Change windows_source only if the collection is a nightly build, and the source was not explicitly specified.
if (($collection -like '*nightly*') -And -Not ($PSBoundParameters.ContainsKey('windows_source'))) {
$windows_source = 'https://nightlies.puppet.com/downloads'
+} elseif (($collection -like '*puppetcore*') -And -Not ($PSBoundParameters.ContainsKey('windows_source'))) {
+ $windows_source = 'https://artifacts-puppetcore.puppet.com/v1/download'
}
if ($absolute_source) {
$msi_source = "$absolute_source"
-}
-else {
+} elseif ($collection -like '*puppetcore*') {
+ # dev param is case-sensitive, so don't use $True
+ if (($version -split '\.').count -gt 3) {
+ $dev = '&dev=true'
+ } else {
+ $dev = ''
+ }
+ $msi_source = "${windows_source}?version=${version}&os_name=windows&os_version=${major_os_version}&os_arch=${arch}&fips=${fips}${dev}"
+} else {
$msi_source = "$windows_source/windows/${collection}/${msi_name}"
}
@@ -125,15 +154,19 @@ function Set-Tls12 {
}
function DownloadPuppet {
- Write-Output "Downloading the Puppet Agent installer on $env:COMPUTERNAME..."
+ Write-Output "Downloading the Puppet Agent installer on $env:COMPUTERNAME from ${msi_source}"
Set-Tls12
$webclient = New-Object system.net.webclient
-
+ if ($password) {
+ $credentials = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("${username}:${password}"))
+ $webclient.Headers.Add("Authorization", "Basic ${credentials}")
+ }
try {
$webclient.DownloadFile($msi_source,$msi_dest)
}
catch [System.Net.WebException] {
+ Write-Host "Download exception: $($_.Exception.Message)"
For ($attempt_number = 1; $attempt_number -le $retry; $attempt_number++) {
try {
Write-Output "Retrying... [$attempt_number/$retry]"
@@ -141,6 +174,7 @@ function DownloadPuppet {
break
}
catch [System.Net.WebException] {
+ Write-Host "Download exception: $($_.Exception.Message)"
if($attempt_number -eq $retry) {
# If we can't find the msi, then we may not be configured correctly
if($_.Exception.Response.StatusCode -eq [system.net.httpstatuscode]::NotFound) {
diff --git a/tasks/install_shell.json b/tasks/install_shell.json
index 0fc475e06..b31139ab4 100644
--- a/tasks/install_shell.json
+++ b/tasks/install_shell.json
@@ -46,11 +46,11 @@
},
"username": {
"description": "The username to use when downloading from a source location requiring authentication",
- "type": "Optional[String]"
+ "type": "Optional[String[1]]"
},
"password": {
"description": "The password to use when downloading from a source location requiring authentication",
- "type": "Optional[String]"
+ "type": "Optional[Sensitive[String[1]]]"
}
},
"files": ["facts/tasks/bash.sh"],
diff --git a/templates/download_puppet.ps1.epp b/templates/download_puppet.ps1.epp
new file mode 100644
index 000000000..43a04c3df
--- /dev/null
+++ b/templates/download_puppet.ps1.epp
@@ -0,0 +1,20 @@
+$body = @{
+ "version" = "<%= $puppet_agent::prepare::package_version %>"
+ "dev" = "<%= $puppet_agent::prepare::package::dev %>"
+ "os_name" = "<%= $facts['os']['family'] %>"
+ "os_version" = "<%= $facts['os']['release']['major'] %>"
+ "os_arch" = "<%= $facts['os']['architecture'] %>"
+ "fips" = "<%= $facts['fips_enabled'] %>"
+}
+$username = "<%= $puppet_agent::prepare::package::download_username %>"
+$password = ConvertTo-SecureString "<%= $puppet_agent::prepare::package::download_password %>" -AsPlainText -Force
+$credential = New-Object System.Management.Automation.PSCredential($username, $password)
+try {
+ Invoke-WebRequest -Uri "<%= $puppet_agent::prepare::package::source %>" `
+ -Body $body `
+ -Credential $credential `
+ -OutFile "<%= $puppet_agent::prepare::package::local_package_file_path %>"
+} catch [System.Net.WebException] {
+ Write-Host "Network-related error: $($_.Exception.Message)"
+ exit 1
+}