Skip to content

Update module to support puppetcore on Windows #766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 20 additions & 21 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -614,24 +619,6 @@ The puppet-agent version to install.

Default value: `undef`

### <a name="puppet_agent--prepare--package"></a>`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)

##### <a name="-puppet_agent--prepare--package--source"></a>`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.

### <a name="puppet_agent--prepare--puppet_config"></a>`puppet_agent::prepare::puppet_config`

Private class called from puppet_agent::prepare class.
Expand Down Expand Up @@ -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

### <a name="install_shell"></a>`install_shell`

Install the Puppet agent package
Expand Down Expand Up @@ -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

Expand Down
4 changes: 3 additions & 1 deletion manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion manifests/osfamily/windows.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 46 additions & 16 deletions manifests/prepare/package.pp
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@
# 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()

file { $puppet_agent::params::local_packages_dir:
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
Expand All @@ -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')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you use getvar, instead of accessing the variable directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was mainly following the getvar convention used in this module.

$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],
}
}
}
4 changes: 4 additions & 0 deletions metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
8 changes: 8 additions & 0 deletions tasks/install_powershell.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 40 additions & 6 deletions tasks/install_powershell.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -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}"
}

Expand All @@ -125,22 +154,27 @@ 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]"
$webclient.DownloadFile($msi_source,$msi_dest)
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) {
Expand Down
4 changes: 2 additions & 2 deletions tasks/install_shell.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
20 changes: 20 additions & 0 deletions templates/download_puppet.ps1.epp
Original file line number Diff line number Diff line change
@@ -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
}
Loading