Remove Puppet components and docs

This commit is contained in:
Wilken Rivera 2021-04-20 12:29:36 -04:00
parent fd028b71b3
commit eb6527c8b6
12 changed files with 0 additions and 2212 deletions

View File

@ -54,8 +54,6 @@ import (
fileprovisioner "github.com/hashicorp/packer/provisioner/file"
inspecprovisioner "github.com/hashicorp/packer/provisioner/inspec"
powershellprovisioner "github.com/hashicorp/packer/provisioner/powershell"
puppetmasterlessprovisioner "github.com/hashicorp/packer/provisioner/puppet-masterless"
puppetserverprovisioner "github.com/hashicorp/packer/provisioner/puppet-server"
saltmasterlessprovisioner "github.com/hashicorp/packer/provisioner/salt-masterless"
shellprovisioner "github.com/hashicorp/packer/provisioner/shell"
shelllocalprovisioner "github.com/hashicorp/packer/provisioner/shell-local"
@ -102,8 +100,6 @@ var Provisioners = map[string]packersdk.Provisioner{
"file": new(fileprovisioner.Provisioner),
"inspec": new(inspecprovisioner.Provisioner),
"powershell": new(powershellprovisioner.Provisioner),
"puppet-masterless": new(puppetmasterlessprovisioner.Provisioner),
"puppet-server": new(puppetserverprovisioner.Provisioner),
"salt-masterless": new(saltmasterlessprovisioner.Provisioner),
"shell": new(shellprovisioner.Provisioner),
"shell-local": new(shelllocalprovisioner.Provisioner),

View File

@ -1,501 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
// Package puppetmasterless implements a provisioner for Packer that executes
// Puppet on the remote machine, configured to apply a local manifest
// versus connecting to a Puppet master.
package puppetmasterless
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/guestexec"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
ctx interpolate.Context
// If true, staging directory is removed after executing puppet.
CleanStagingDir bool `mapstructure:"clean_staging_directory"`
// The Guest OS Type (unix or windows)
GuestOSType string `mapstructure:"guest_os_type"`
// The command used to execute Puppet.
ExecuteCommand string `mapstructure:"execute_command"`
// Additional arguments to pass when executing Puppet
ExtraArguments []string `mapstructure:"extra_arguments"`
// Additional facts to set when executing Puppet
Facter map[string]string
// Path to a hiera configuration file to upload and use.
HieraConfigPath string `mapstructure:"hiera_config_path"`
// If true, packer will ignore all exit-codes from a puppet run
IgnoreExitCodes bool `mapstructure:"ignore_exit_codes"`
// An array of local paths of modules to upload.
ModulePaths []string `mapstructure:"module_paths"`
// The main manifest file to apply to kick off the entire thing.
ManifestFile string `mapstructure:"manifest_file"`
// A directory of manifest files that will be uploaded to the remote
// machine.
ManifestDir string `mapstructure:"manifest_dir"`
// If true, `sudo` will NOT be used to execute Puppet.
PreventSudo bool `mapstructure:"prevent_sudo"`
// The directory that contains the puppet binary.
// E.g. if it can't be found on the standard path.
PuppetBinDir string `mapstructure:"puppet_bin_dir"`
// The directory where files will be uploaded. Packer requires write
// permissions in this directory.
StagingDir string `mapstructure:"staging_directory"`
// The directory from which the command will be executed.
// Packer requires the directory to exist when running puppet.
WorkingDir string `mapstructure:"working_directory"`
// Instructs the communicator to run the remote script as a Windows
// scheduled task, effectively elevating the remote user by impersonating
// a logged-in user
ElevatedUser string `mapstructure:"elevated_user"`
ElevatedPassword string `mapstructure:"elevated_password"`
}
type guestOSTypeConfig struct {
executeCommand string
facterVarsFmt string
facterVarsJoiner string
modulePathJoiner string
stagingDir string
tempDir string
}
// FIXME assumes both Packer host and target are same OS
var guestOSTypeConfigs = map[string]guestOSTypeConfig{
guestexec.UnixOSType: {
tempDir: "/tmp",
stagingDir: "/tmp/packer-puppet-masterless",
executeCommand: "cd {{.WorkingDir}} && " +
`{{if ne .FacterVars ""}}{{.FacterVars}} {{end}}` +
"{{if .Sudo}}sudo -E {{end}}" +
`{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}` +
"puppet apply --detailed-exitcodes " +
"{{if .Debug}}--debug {{end}}" +
`{{if ne .ModulePath ""}}--modulepath='{{.ModulePath}}' {{end}}` +
`{{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}}` +
`{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}}` +
`{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}` +
"{{.ManifestFile}}",
facterVarsFmt: "FACTER_%s='%s'",
facterVarsJoiner: " ",
modulePathJoiner: ":",
},
guestexec.WindowsOSType: {
tempDir: filepath.ToSlash(os.Getenv("TEMP")),
stagingDir: filepath.ToSlash(os.Getenv("SYSTEMROOT")) + "/Temp/packer-puppet-masterless",
executeCommand: "cd {{.WorkingDir}} && " +
`{{if ne .FacterVars ""}}{{.FacterVars}} && {{end}}` +
`{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}` +
"puppet apply --detailed-exitcodes " +
"{{if .Debug}}--debug {{end}}" +
`{{if ne .ModulePath ""}}--modulepath='{{.ModulePath}}' {{end}}` +
`{{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}}` +
`{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}}` +
`{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}` +
"{{.ManifestFile}}",
facterVarsFmt: `SET "FACTER_%s=%s"`,
facterVarsJoiner: " & ",
modulePathJoiner: ";",
},
}
type Provisioner struct {
config Config
communicator packersdk.Communicator
guestOSTypeConfig guestOSTypeConfig
guestCommands *guestexec.GuestCommands
generatedData map[string]interface{}
}
type ExecuteTemplate struct {
Debug bool
ExtraArguments string
FacterVars string
HieraConfigPath string
ModulePath string
ModulePathJoiner string
ManifestFile string
ManifestDir string
PuppetBinDir string
Sudo bool
WorkingDir string
}
type EnvVarsTemplate struct {
WinRMPassword string
}
func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
func (p *Provisioner) Prepare(raws ...interface{}) error {
err := config.Decode(&p.config, &config.DecodeOpts{
PluginType: "puppet-masterless",
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"execute_command",
"extra_arguments",
},
},
}, raws...)
if err != nil {
return err
}
// Set some defaults
if p.config.GuestOSType == "" {
p.config.GuestOSType = guestexec.DefaultOSType
}
p.config.GuestOSType = strings.ToLower(p.config.GuestOSType)
var ok bool
p.guestOSTypeConfig, ok = guestOSTypeConfigs[p.config.GuestOSType]
if !ok {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
p.guestCommands, err = guestexec.NewGuestCommands(p.config.GuestOSType, !p.config.PreventSudo)
if err != nil {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
}
if p.config.StagingDir == "" {
p.config.StagingDir = p.guestOSTypeConfig.stagingDir
}
if p.config.WorkingDir == "" {
p.config.WorkingDir = p.config.StagingDir
}
if p.config.Facter == nil {
p.config.Facter = make(map[string]string)
}
p.config.Facter["packer_build_name"] = p.config.PackerBuildName
p.config.Facter["packer_builder_type"] = p.config.PackerBuilderType
// Validation
var errs *packersdk.MultiError
if p.config.HieraConfigPath != "" {
info, err := os.Stat(p.config.HieraConfigPath)
if err != nil {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("hiera_config_path is invalid: %s", err))
} else if info.IsDir() {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("hiera_config_path must point to a file"))
}
}
if p.config.ManifestDir != "" {
info, err := os.Stat(p.config.ManifestDir)
if err != nil {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("manifest_dir is invalid: %s", err))
} else if !info.IsDir() {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("manifest_dir must point to a directory"))
}
}
if p.config.ManifestFile == "" {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("A manifest_file must be specified."))
} else {
_, err := os.Stat(p.config.ManifestFile)
if err != nil {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("manifest_file is invalid: %s", err))
}
}
for i, path := range p.config.ModulePaths {
info, err := os.Stat(path)
if err != nil {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("module_path[%d] is invalid: %s", i, err))
} else if !info.IsDir() {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("module_path[%d] must point to a directory", i))
}
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, generatedData map[string]interface{}) error {
ui.Say("Provisioning with Puppet...")
p.communicator = comm
p.generatedData = generatedData
ui.Message("Creating Puppet staging directory...")
if err := p.createDir(ui, comm, p.config.StagingDir); err != nil {
return fmt.Errorf("Error creating staging directory: %s", err)
}
// Upload hiera config if set
remoteHieraConfigPath := ""
if p.config.HieraConfigPath != "" {
var err error
remoteHieraConfigPath, err = p.uploadHieraConfig(ui, comm)
if err != nil {
return fmt.Errorf("Error uploading hiera config: %s", err)
}
}
// Upload manifest dir if set
remoteManifestDir := ""
if p.config.ManifestDir != "" {
ui.Message(fmt.Sprintf(
"Uploading manifest directory from: %s", p.config.ManifestDir))
remoteManifestDir = fmt.Sprintf("%s/manifests", p.config.StagingDir)
err := p.uploadDirectory(ui, comm, remoteManifestDir, p.config.ManifestDir)
if err != nil {
return fmt.Errorf("Error uploading manifest dir: %s", err)
}
}
// Upload all modules
modulePaths := make([]string, 0, len(p.config.ModulePaths))
for i, path := range p.config.ModulePaths {
ui.Message(fmt.Sprintf("Uploading local modules from: %s", path))
targetPath := fmt.Sprintf("%s/module-%d", p.config.StagingDir, i)
if err := p.uploadDirectory(ui, comm, targetPath, path); err != nil {
return fmt.Errorf("Error uploading modules: %s", err)
}
modulePaths = append(modulePaths, targetPath)
}
// Upload manifests
remoteManifestFile, err := p.uploadManifests(ui, comm)
if err != nil {
return fmt.Errorf("Error uploading manifests: %s", err)
}
// Compile the facter variables
facterVars := make([]string, 0, len(p.config.Facter))
for k, v := range p.config.Facter {
facterVars = append(facterVars, fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, k, v))
}
data := ExecuteTemplate{
ExtraArguments: "",
FacterVars: strings.Join(facterVars, p.guestOSTypeConfig.facterVarsJoiner),
HieraConfigPath: remoteHieraConfigPath,
ManifestDir: remoteManifestDir,
ManifestFile: remoteManifestFile,
ModulePath: strings.Join(modulePaths, p.guestOSTypeConfig.modulePathJoiner),
ModulePathJoiner: p.guestOSTypeConfig.modulePathJoiner,
PuppetBinDir: p.config.PuppetBinDir,
Sudo: !p.config.PreventSudo,
WorkingDir: p.config.WorkingDir,
}
p.config.ctx.Data = &data
_ExtraArguments, err := interpolate.Render(strings.Join(p.config.ExtraArguments, " "), &p.config.ctx)
if err != nil {
return err
}
data.ExtraArguments = _ExtraArguments
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
return err
}
if p.config.ElevatedUser != "" {
command, err = guestexec.GenerateElevatedRunner(command, p)
if err != nil {
return err
}
}
cmd := &packersdk.RemoteCmd{
Command: command,
}
ui.Message(fmt.Sprintf("Running Puppet: %s", command))
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return fmt.Errorf("Got an error starting command: %s", err)
}
if cmd.ExitStatus() != 0 && cmd.ExitStatus() != 2 && !p.config.IgnoreExitCodes {
return fmt.Errorf("Puppet exited with a non-zero exit status: %d", cmd.ExitStatus())
}
if p.config.CleanStagingDir {
if err := p.removeDir(ui, comm, p.config.StagingDir); err != nil {
return fmt.Errorf("Error removing staging directory: %s", err)
}
}
return nil
}
func (p *Provisioner) uploadHieraConfig(ui packersdk.Ui, comm packersdk.Communicator) (string, error) {
ui.Message("Uploading hiera configuration...")
f, err := os.Open(p.config.HieraConfigPath)
if err != nil {
return "", err
}
defer f.Close()
path := fmt.Sprintf("%s/hiera.yaml", p.config.StagingDir)
if err := comm.Upload(path, f, nil); err != nil {
return "", err
}
return path, nil
}
func (p *Provisioner) uploadManifests(ui packersdk.Ui, comm packersdk.Communicator) (string, error) {
// Create the remote manifests directory...
ui.Message("Uploading manifests...")
remoteManifestsPath := fmt.Sprintf("%s/manifests", p.config.StagingDir)
if err := p.createDir(ui, comm, remoteManifestsPath); err != nil {
return "", fmt.Errorf("Error creating manifests directory: %s", err)
}
// NOTE! manifest_file may either be a directory or a file, as puppet apply
// now accepts either one.
fi, err := os.Stat(p.config.ManifestFile)
if err != nil {
return "", fmt.Errorf("Error inspecting manifest file: %s", err)
}
if fi.IsDir() {
// If manifest_file is a directory we'll upload the whole thing
ui.Message(fmt.Sprintf(
"Uploading manifest directory from: %s", p.config.ManifestFile))
remoteManifestDir := fmt.Sprintf("%s/manifests", p.config.StagingDir)
err := p.uploadDirectory(ui, comm, remoteManifestDir, p.config.ManifestFile)
if err != nil {
return "", fmt.Errorf("Error uploading manifest dir: %s", err)
}
return remoteManifestDir, nil
}
// Otherwise manifest_file is a file and we'll upload it
ui.Message(fmt.Sprintf(
"Uploading manifest file from: %s", p.config.ManifestFile))
f, err := os.Open(p.config.ManifestFile)
if err != nil {
return "", err
}
defer f.Close()
manifestFilename := filepath.Base(p.config.ManifestFile)
remoteManifestFile := fmt.Sprintf("%s/%s", remoteManifestsPath, manifestFilename)
if err := comm.Upload(remoteManifestFile, f, nil); err != nil {
return "", err
}
return remoteManifestFile, nil
}
func (p *Provisioner) createDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ui.Message(fmt.Sprintf("Creating directory: %s", dir))
cmd := &packersdk.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
ctx := context.TODO()
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.")
}
// Chmod the directory to 0777 just so that we can access it as our user
cmd = &packersdk.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.")
}
return nil
}
func (p *Provisioner) removeDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ctx := context.TODO()
cmd := &packersdk.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.")
}
return nil
}
func (p *Provisioner) uploadDirectory(ui packersdk.Ui, comm packersdk.Communicator, dst string, src string) error {
if err := p.createDir(ui, comm, dst); err != nil {
return err
}
// Make sure there is a trailing "/" so that the directory isn't
// created on the other side.
if src[len(src)-1] != '/' {
src = src + "/"
}
return comm.UploadDir(dst, src, nil)
}
func (p *Provisioner) Communicator() packersdk.Communicator {
return p.communicator
}
func (p *Provisioner) ElevatedUser() string {
return p.config.ElevatedUser
}
func (p *Provisioner) ElevatedPassword() string {
// Replace ElevatedPassword for winrm users who used this feature
p.config.ctx.Data = p.generatedData
elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx)
return elevatedPassword
}

View File

@ -1,77 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package puppetmasterless
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
CleanStagingDir *bool `mapstructure:"clean_staging_directory" cty:"clean_staging_directory" hcl:"clean_staging_directory"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type" hcl:"guest_os_type"`
ExecuteCommand *string `mapstructure:"execute_command" cty:"execute_command" hcl:"execute_command"`
ExtraArguments []string `mapstructure:"extra_arguments" cty:"extra_arguments" hcl:"extra_arguments"`
Facter map[string]string `cty:"facter" hcl:"facter"`
HieraConfigPath *string `mapstructure:"hiera_config_path" cty:"hiera_config_path" hcl:"hiera_config_path"`
IgnoreExitCodes *bool `mapstructure:"ignore_exit_codes" cty:"ignore_exit_codes" hcl:"ignore_exit_codes"`
ModulePaths []string `mapstructure:"module_paths" cty:"module_paths" hcl:"module_paths"`
ManifestFile *string `mapstructure:"manifest_file" cty:"manifest_file" hcl:"manifest_file"`
ManifestDir *string `mapstructure:"manifest_dir" cty:"manifest_dir" hcl:"manifest_dir"`
PreventSudo *bool `mapstructure:"prevent_sudo" cty:"prevent_sudo" hcl:"prevent_sudo"`
PuppetBinDir *string `mapstructure:"puppet_bin_dir" cty:"puppet_bin_dir" hcl:"puppet_bin_dir"`
StagingDir *string `mapstructure:"staging_directory" cty:"staging_directory" hcl:"staging_directory"`
WorkingDir *string `mapstructure:"working_directory" cty:"working_directory" hcl:"working_directory"`
ElevatedUser *string `mapstructure:"elevated_user" cty:"elevated_user" hcl:"elevated_user"`
ElevatedPassword *string `mapstructure:"elevated_password" cty:"elevated_password" hcl:"elevated_password"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"clean_staging_directory": &hcldec.AttrSpec{Name: "clean_staging_directory", Type: cty.Bool, Required: false},
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
"execute_command": &hcldec.AttrSpec{Name: "execute_command", Type: cty.String, Required: false},
"extra_arguments": &hcldec.AttrSpec{Name: "extra_arguments", Type: cty.List(cty.String), Required: false},
"facter": &hcldec.AttrSpec{Name: "facter", Type: cty.Map(cty.String), Required: false},
"hiera_config_path": &hcldec.AttrSpec{Name: "hiera_config_path", Type: cty.String, Required: false},
"ignore_exit_codes": &hcldec.AttrSpec{Name: "ignore_exit_codes", Type: cty.Bool, Required: false},
"module_paths": &hcldec.AttrSpec{Name: "module_paths", Type: cty.List(cty.String), Required: false},
"manifest_file": &hcldec.AttrSpec{Name: "manifest_file", Type: cty.String, Required: false},
"manifest_dir": &hcldec.AttrSpec{Name: "manifest_dir", Type: cty.String, Required: false},
"prevent_sudo": &hcldec.AttrSpec{Name: "prevent_sudo", Type: cty.Bool, Required: false},
"puppet_bin_dir": &hcldec.AttrSpec{Name: "puppet_bin_dir", Type: cty.String, Required: false},
"staging_directory": &hcldec.AttrSpec{Name: "staging_directory", Type: cty.String, Required: false},
"working_directory": &hcldec.AttrSpec{Name: "working_directory", Type: cty.String, Required: false},
"elevated_user": &hcldec.AttrSpec{Name: "elevated_user", Type: cty.String, Required: false},
"elevated_password": &hcldec.AttrSpec{Name: "elevated_password", Type: cty.String, Required: false},
}
return s
}

View File

@ -1,528 +0,0 @@
package puppetmasterless
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer/packer"
"github.com/stretchr/testify/assert"
)
func testConfig() (config map[string]interface{}, tf *os.File) {
tf, err := ioutil.TempFile("", "packer")
if err != nil {
panic(err)
}
config = map[string]interface{}{
"manifest_file": tf.Name(),
}
return config, tf
}
func TestProvisioner_Impl(t *testing.T) {
var raw interface{}
raw = &Provisioner{}
if _, ok := raw.(packersdk.Provisioner); !ok {
t.Fatalf("must be a Provisioner")
}
}
func TestGuestOSConfig_empty_unix(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Execute Puppet
p.config.ctx.Data = &ExecuteTemplate{
ManifestFile: "/r/m/f",
PuppetBinDir: p.config.PuppetBinDir,
Sudo: !p.config.PreventSudo,
WorkingDir: p.config.WorkingDir,
}
log.Println(p.config.ExecuteCommand)
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := "cd /tmp/packer-puppet-masterless && " +
"sudo -E puppet apply --detailed-exitcodes /r/m/f"
assert.Equal(t, expected, command)
}
func TestGuestOSConfig_full_unix(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
facterVars := []string{
fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, "lhs", "rhs"),
fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, "foo", "bar"),
}
modulePaths := []string{"/m/p", "/a/b"}
// Execute Puppet
p.config.ctx.Data = &ExecuteTemplate{
FacterVars: strings.Join(facterVars, p.guestOSTypeConfig.facterVarsJoiner),
HieraConfigPath: "/h/c/p",
ManifestDir: "/r/m/d",
ManifestFile: "/r/m/f",
ModulePath: strings.Join(modulePaths, p.guestOSTypeConfig.modulePathJoiner),
PuppetBinDir: p.config.PuppetBinDir,
Sudo: !p.config.PreventSudo,
WorkingDir: p.config.WorkingDir,
ExtraArguments: strings.Join(p.config.ExtraArguments, " "),
}
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := "cd /tmp/packer-puppet-masterless && FACTER_lhs='rhs' FACTER_foo='bar' " +
"sudo -E puppet apply " +
"--detailed-exitcodes --modulepath='/m/p:/a/b' --hiera_config='/h/c/p' " +
"--manifestdir='/r/m/d' /r/m/f"
assert.Equal(t, expected, command)
}
func TestGuestOSConfig_empty_windows(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
config["guest_os_type"] = "windows"
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Execute Puppet
p.config.ctx.Data = &ExecuteTemplate{
ManifestFile: "/r/m/f",
PuppetBinDir: p.config.PuppetBinDir,
Sudo: !p.config.PreventSudo,
WorkingDir: p.config.WorkingDir,
}
log.Println(p.config.ExecuteCommand)
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := "cd " + filepath.ToSlash(os.Getenv("SYSTEMROOT")) + "/Temp/packer-puppet-masterless && puppet apply --detailed-exitcodes /r/m/f"
assert.Equal(t, expected, command)
}
func TestGuestOSConfig_full_windows(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
config["guest_os_type"] = "windows"
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
facterVars := []string{
fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, "lhs", "rhs"),
fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, "foo", "bar"),
}
modulePaths := []string{"/m/p", "/a/b"}
// Execute Puppet
p.config.ctx.Data = &ExecuteTemplate{
FacterVars: strings.Join(facterVars, p.guestOSTypeConfig.facterVarsJoiner),
HieraConfigPath: "/h/c/p",
ManifestDir: "/r/m/d",
ManifestFile: "/r/m/f",
ModulePath: strings.Join(modulePaths, p.guestOSTypeConfig.modulePathJoiner),
PuppetBinDir: p.config.PuppetBinDir,
Sudo: !p.config.PreventSudo,
WorkingDir: p.config.WorkingDir,
ExtraArguments: strings.Join(p.config.ExtraArguments, " "),
}
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := "cd " + filepath.ToSlash(os.Getenv("SYSTEMROOT")) + "/Temp/packer-puppet-masterless && " +
"SET \"FACTER_lhs=rhs\" & SET \"FACTER_foo=bar\" && " +
"puppet apply --detailed-exitcodes --modulepath='/m/p;/a/b' --hiera_config='/h/c/p' " +
"--manifestdir='/r/m/d' /r/m/f"
assert.Equal(t, expected, command)
}
func TestProvisionerPrepare_puppetBinDir(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "puppet_bin_dir")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a good one
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("error tempfile: %s", err)
}
defer os.Remove(tf.Name())
config["puppet_bin_dir"] = tf.Name()
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_hieraConfigPath(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "hiera_config_path")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a good one
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("error tempfile: %s", err)
}
defer os.Remove(tf.Name())
config["hiera_config_path"] = tf.Name()
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_manifestFile(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "manifest_file")
p := new(Provisioner)
err := p.Prepare(config)
if err == nil {
t.Fatal("should be an error")
}
// Test with a good one
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("error tempfile: %s", err)
}
defer os.Remove(tf.Name())
config["manifest_file"] = tf.Name()
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_manifestDir(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "manifestdir")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a good one
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("error: %s", err)
}
defer os.RemoveAll(td)
config["manifest_dir"] = td
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_modulePaths(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "module_paths")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with bad paths
config["module_paths"] = []string{"i-should-not-exist"}
p = new(Provisioner)
err = p.Prepare(config)
if err == nil {
t.Fatal("should be an error")
}
// Test with a good one
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("error: %s", err)
}
defer os.RemoveAll(td)
config["module_paths"] = []string{td}
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_facterFacts(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "facter")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with malformed fact
config["facter"] = "fact=stringified"
p = new(Provisioner)
err = p.Prepare(config)
if err == nil {
t.Fatal("should be an error")
}
// Test with a good one
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("error: %s", err)
}
defer os.RemoveAll(td)
facts := make(map[string]string)
facts["fact_name"] = "fact_value"
config["facter"] = facts
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Make sure the default facts are present
delete(config, "facter")
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.Facter == nil {
t.Fatalf("err: Default facts are not set in the Puppet provisioner!")
}
if _, ok := p.config.Facter["packer_build_name"]; !ok {
t.Fatalf("err: packer_build_name fact not set in the Puppet provisioner!")
}
if _, ok := p.config.Facter["packer_builder_type"]; !ok {
t.Fatalf("err: packer_builder_type fact not set in the Puppet provisioner!")
}
}
func TestProvisionerPrepare_extraArguments(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
// Test with missing parameter
delete(config, "extra_arguments")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with malformed value
config["extra_arguments"] = "{{}}"
p = new(Provisioner)
err = p.Prepare(config)
if err == nil {
t.Fatal("should be an error")
}
// Test with valid values
config["extra_arguments"] = []string{
"arg",
}
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_stagingDir(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "staging_directory")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Make sure the default staging directory is correct
if p.config.StagingDir != "/tmp/packer-puppet-masterless" {
t.Fatalf("err: Default staging_directory is not set in the Puppet provisioner!")
}
// Make sure default staging directory can be overridden
config["staging_directory"] = "/tmp/override"
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.StagingDir != "/tmp/override" {
t.Fatalf("err: Overridden staging_directory is not set correctly in the Puppet provisioner!")
}
}
func TestProvisionerPrepare_workingDir(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "working_directory")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Make sure default working dir and staging dir are the same
if p.config.WorkingDir != p.config.StagingDir {
t.Fatalf("err: Default working_directory is not set to the same value as default staging_directory in the Puppet provisioner!")
}
// Make sure the default working directory is correct
if p.config.WorkingDir != "/tmp/packer-puppet-masterless" {
t.Fatalf("err: Default working_directory is not set in the Puppet provisioner!")
}
// Make sure default working directory can be overridden
config["working_directory"] = "/tmp/override"
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.WorkingDir != "/tmp/override" {
t.Fatalf("err: Overridden working_directory is not set correctly in the Puppet provisioner!")
}
}
func TestProvisionerProvision_extraArguments(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
ui := &packer.MachineReadableUi{
Writer: ioutil.Discard,
}
comm := new(packersdk.MockCommunicator)
extraArguments := []string{
"--some-arg=yup",
"--some-other-arg",
}
config["extra_arguments"] = extraArguments
// Test with valid values
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
if err != nil {
t.Fatalf("err: %s", err)
}
expectedArgs := strings.Join(extraArguments, " ")
if !strings.Contains(comm.StartCmd.Command, expectedArgs) {
t.Fatalf("Command %q doesn't contain the expected arguments %q", comm.StartCmd.Command, expectedArgs)
}
// Test with missing parameter
delete(config, "extra_arguments")
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
if err != nil {
t.Fatalf("err: %s", err)
}
// Check the expected `extra_arguments` position for an empty value
splitCommand := strings.Split(comm.StartCmd.Command, " ")
if "" == splitCommand[len(splitCommand)-2] {
t.Fatalf("Command %q contains an extra-space which may cause arg parsing issues", comm.StartCmd.Command)
}
}

View File

@ -1,13 +0,0 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var PuppetMasterlessPluginVersion *version.PluginVersion
func init() {
PuppetMasterlessPluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}

View File

@ -1,390 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
// Package puppetserver implements a provisioner for Packer that executes
// Puppet on the remote machine connecting to a Puppet master.
package puppetserver
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/guestexec"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
ctx interpolate.Context
// If true, staging directory is removed after executing puppet.
CleanStagingDir bool `mapstructure:"clean_staging_directory"`
// A path to the client certificate
ClientCertPath string `mapstructure:"client_cert_path"`
// A path to a directory containing the client private keys
ClientPrivateKeyPath string `mapstructure:"client_private_key_path"`
// The command used to execute Puppet.
ExecuteCommand string `mapstructure:"execute_command"`
// Additional argument to pass when executing Puppet.
ExtraArguments []string `mapstructure:"extra_arguments"`
// Additional facts to set when executing Puppet
Facter map[string]string
// The Guest OS Type (unix or windows)
GuestOSType string `mapstructure:"guest_os_type"`
// If true, packer will ignore all exit-codes from a puppet run
IgnoreExitCodes bool `mapstructure:"ignore_exit_codes"`
// If true, `sudo` will NOT be used to execute Puppet.
PreventSudo bool `mapstructure:"prevent_sudo"`
// The directory that contains the puppet binary.
// E.g. if it can't be found on the standard path.
PuppetBinDir string `mapstructure:"puppet_bin_dir"`
// The hostname of the Puppet node.
PuppetNode string `mapstructure:"puppet_node"`
// The hostname of the Puppet server.
PuppetServer string `mapstructure:"puppet_server"`
// The directory where files will be uploaded. Packer requires write
// permissions in this directory.
StagingDir string `mapstructure:"staging_dir"`
// The directory from which the command will be executed.
// Packer requires the directory to exist when running puppet.
WorkingDir string `mapstructure:"working_directory"`
// Instructs the communicator to run the remote script as a Windows
// scheduled task, effectively elevating the remote user by impersonating
// a logged-in user
ElevatedUser string `mapstructure:"elevated_user"`
ElevatedPassword string `mapstructure:"elevated_password"`
}
type guestOSTypeConfig struct {
executeCommand string
facterVarsFmt string
facterVarsJoiner string
stagingDir string
tempDir string
}
// FIXME assumes both Packer host and target are same OS
var guestOSTypeConfigs = map[string]guestOSTypeConfig{
guestexec.UnixOSType: {
tempDir: "/tmp",
stagingDir: "/tmp/packer-puppet-server",
executeCommand: "cd {{.WorkingDir}} && " +
`{{if ne .FacterVars ""}}{{.FacterVars}} {{end}}` +
"{{if .Sudo}}sudo -E {{end}}" +
`{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}` +
"puppet agent --onetime --no-daemonize --detailed-exitcodes " +
"{{if .Debug}}--debug {{end}}" +
`{{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}}` +
`{{if ne .PuppetNode ""}}--certname='{{.PuppetNode}}' {{end}}` +
`{{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}}` +
`{{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}` +
`{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}`,
facterVarsFmt: "FACTER_%s='%s'",
facterVarsJoiner: " ",
},
guestexec.WindowsOSType: {
tempDir: filepath.ToSlash(os.Getenv("TEMP")),
stagingDir: filepath.ToSlash(os.Getenv("SYSTEMROOT")) + "/Temp/packer-puppet-server",
executeCommand: "cd {{.WorkingDir}} && " +
`{{if ne .FacterVars ""}}{{.FacterVars}} && {{end}}` +
`{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}` +
"puppet agent --onetime --no-daemonize --detailed-exitcodes " +
"{{if .Debug}}--debug {{end}}" +
`{{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}}` +
`{{if ne .PuppetNode ""}}--certname='{{.PuppetNode}}' {{end}}` +
`{{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}}` +
`{{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}` +
`{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}`,
facterVarsFmt: `SET "FACTER_%s=%s"`,
facterVarsJoiner: " & ",
},
}
type Provisioner struct {
config Config
communicator packersdk.Communicator
guestOSTypeConfig guestOSTypeConfig
guestCommands *guestexec.GuestCommands
generatedData map[string]interface{}
}
type ExecuteTemplate struct {
ClientCertPath string
ClientPrivateKeyPath string
Debug bool
ExtraArguments string
FacterVars string
PuppetNode string
PuppetServer string
PuppetBinDir string
Sudo bool
WorkingDir string
}
type EnvVarsTemplate struct {
WinRMPassword string
}
func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
func (p *Provisioner) Prepare(raws ...interface{}) error {
err := config.Decode(&p.config, &config.DecodeOpts{
PluginType: "puppet-server",
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"execute_command",
"extra_arguments",
},
},
}, raws...)
if err != nil {
return err
}
if p.config.GuestOSType == "" {
p.config.GuestOSType = guestexec.DefaultOSType
}
p.config.GuestOSType = strings.ToLower(p.config.GuestOSType)
var ok bool
p.guestOSTypeConfig, ok = guestOSTypeConfigs[p.config.GuestOSType]
if !ok {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
p.guestCommands, err = guestexec.NewGuestCommands(p.config.GuestOSType, !p.config.PreventSudo)
if err != nil {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
}
if p.config.StagingDir == "" {
p.config.StagingDir = p.guestOSTypeConfig.stagingDir
}
if p.config.WorkingDir == "" {
p.config.WorkingDir = p.config.StagingDir
}
if p.config.Facter == nil {
p.config.Facter = make(map[string]string)
}
p.config.Facter["packer_build_name"] = p.config.PackerBuildName
p.config.Facter["packer_builder_type"] = p.config.PackerBuilderType
var errs *packersdk.MultiError
if p.config.ClientCertPath != "" {
info, err := os.Stat(p.config.ClientCertPath)
if err != nil {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("client_cert_dir is invalid: %s", err))
} else if !info.IsDir() {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("client_cert_dir must point to a directory"))
}
}
if p.config.ClientPrivateKeyPath != "" {
info, err := os.Stat(p.config.ClientPrivateKeyPath)
if err != nil {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("client_private_key_dir is invalid: %s", err))
} else if !info.IsDir() {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("client_private_key_dir must point to a directory"))
}
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, generatedData map[string]interface{}) error {
ui.Say("Provisioning with Puppet...")
p.communicator = comm
p.generatedData = generatedData
ui.Message("Creating Puppet staging directory...")
if err := p.createDir(ui, comm, p.config.StagingDir); err != nil {
return fmt.Errorf("Error creating staging directory: %s", err)
}
// Upload client cert dir if set
remoteClientCertPath := ""
if p.config.ClientCertPath != "" {
ui.Message(fmt.Sprintf(
"Uploading client cert from: %s", p.config.ClientCertPath))
remoteClientCertPath = fmt.Sprintf("%s/certs", p.config.StagingDir)
err := p.uploadDirectory(ui, comm, remoteClientCertPath, p.config.ClientCertPath)
if err != nil {
return fmt.Errorf("Error uploading client cert: %s", err)
}
}
// Upload client cert dir if set
remoteClientPrivateKeyPath := ""
if p.config.ClientPrivateKeyPath != "" {
ui.Message(fmt.Sprintf(
"Uploading client private keys from: %s", p.config.ClientPrivateKeyPath))
remoteClientPrivateKeyPath = fmt.Sprintf("%s/private_keys", p.config.StagingDir)
err := p.uploadDirectory(ui, comm, remoteClientPrivateKeyPath, p.config.ClientPrivateKeyPath)
if err != nil {
return fmt.Errorf("Error uploading client private keys: %s", err)
}
}
// Compile the facter variables
facterVars := make([]string, 0, len(p.config.Facter))
for k, v := range p.config.Facter {
facterVars = append(facterVars, fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, k, v))
}
data := ExecuteTemplate{
ClientCertPath: remoteClientCertPath,
ClientPrivateKeyPath: remoteClientPrivateKeyPath,
ExtraArguments: "",
FacterVars: strings.Join(facterVars, p.guestOSTypeConfig.facterVarsJoiner),
PuppetNode: p.config.PuppetNode,
PuppetServer: p.config.PuppetServer,
PuppetBinDir: p.config.PuppetBinDir,
Sudo: !p.config.PreventSudo,
WorkingDir: p.config.WorkingDir,
}
p.config.ctx.Data = &data
_ExtraArguments, err := interpolate.Render(strings.Join(p.config.ExtraArguments, " "), &p.config.ctx)
if err != nil {
return err
}
data.ExtraArguments = _ExtraArguments
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
return err
}
if p.config.ElevatedUser != "" {
command, err = guestexec.GenerateElevatedRunner(command, p)
if err != nil {
return err
}
}
cmd := &packersdk.RemoteCmd{
Command: command,
}
ui.Message(fmt.Sprintf("Running Puppet: %s", command))
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 && cmd.ExitStatus() != 2 && !p.config.IgnoreExitCodes {
return fmt.Errorf("Puppet exited with a non-zero exit status: %d", cmd.ExitStatus())
}
if p.config.CleanStagingDir {
if err := p.removeDir(ui, comm, p.config.StagingDir); err != nil {
return fmt.Errorf("Error removing staging directory: %s", err)
}
}
return nil
}
func (p *Provisioner) createDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ui.Message(fmt.Sprintf("Creating directory: %s", dir))
ctx := context.TODO()
cmd := &packersdk.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.")
}
// Chmod the directory to 0777 just so that we can access it as our user
cmd = &packersdk.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.")
}
return nil
}
func (p *Provisioner) removeDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ctx := context.TODO()
cmd := &packersdk.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.")
}
return nil
}
func (p *Provisioner) uploadDirectory(ui packersdk.Ui, comm packersdk.Communicator, dst string, src string) error {
if err := p.createDir(ui, comm, dst); err != nil {
return err
}
// Make sure there is a trailing "/" so that the directory isn't
// created on the other side.
if src[len(src)-1] != '/' {
src = src + "/"
}
return comm.UploadDir(dst, src, nil)
}
func (p *Provisioner) Communicator() packersdk.Communicator {
return p.communicator
}
func (p *Provisioner) ElevatedUser() string {
return p.config.ElevatedUser
}
func (p *Provisioner) ElevatedPassword() string {
// Replace ElevatedPassword for winrm users who used this feature
p.config.ctx.Data = p.generatedData
elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx)
return elevatedPassword
}

View File

@ -1,77 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package puppetserver
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
CleanStagingDir *bool `mapstructure:"clean_staging_directory" cty:"clean_staging_directory" hcl:"clean_staging_directory"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientPrivateKeyPath *string `mapstructure:"client_private_key_path" cty:"client_private_key_path" hcl:"client_private_key_path"`
ExecuteCommand *string `mapstructure:"execute_command" cty:"execute_command" hcl:"execute_command"`
ExtraArguments []string `mapstructure:"extra_arguments" cty:"extra_arguments" hcl:"extra_arguments"`
Facter map[string]string `cty:"facter" hcl:"facter"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type" hcl:"guest_os_type"`
IgnoreExitCodes *bool `mapstructure:"ignore_exit_codes" cty:"ignore_exit_codes" hcl:"ignore_exit_codes"`
PreventSudo *bool `mapstructure:"prevent_sudo" cty:"prevent_sudo" hcl:"prevent_sudo"`
PuppetBinDir *string `mapstructure:"puppet_bin_dir" cty:"puppet_bin_dir" hcl:"puppet_bin_dir"`
PuppetNode *string `mapstructure:"puppet_node" cty:"puppet_node" hcl:"puppet_node"`
PuppetServer *string `mapstructure:"puppet_server" cty:"puppet_server" hcl:"puppet_server"`
StagingDir *string `mapstructure:"staging_dir" cty:"staging_dir" hcl:"staging_dir"`
WorkingDir *string `mapstructure:"working_directory" cty:"working_directory" hcl:"working_directory"`
ElevatedUser *string `mapstructure:"elevated_user" cty:"elevated_user" hcl:"elevated_user"`
ElevatedPassword *string `mapstructure:"elevated_password" cty:"elevated_password" hcl:"elevated_password"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"clean_staging_directory": &hcldec.AttrSpec{Name: "clean_staging_directory", Type: cty.Bool, Required: false},
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
"client_private_key_path": &hcldec.AttrSpec{Name: "client_private_key_path", Type: cty.String, Required: false},
"execute_command": &hcldec.AttrSpec{Name: "execute_command", Type: cty.String, Required: false},
"extra_arguments": &hcldec.AttrSpec{Name: "extra_arguments", Type: cty.List(cty.String), Required: false},
"facter": &hcldec.AttrSpec{Name: "facter", Type: cty.Map(cty.String), Required: false},
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
"ignore_exit_codes": &hcldec.AttrSpec{Name: "ignore_exit_codes", Type: cty.Bool, Required: false},
"prevent_sudo": &hcldec.AttrSpec{Name: "prevent_sudo", Type: cty.Bool, Required: false},
"puppet_bin_dir": &hcldec.AttrSpec{Name: "puppet_bin_dir", Type: cty.String, Required: false},
"puppet_node": &hcldec.AttrSpec{Name: "puppet_node", Type: cty.String, Required: false},
"puppet_server": &hcldec.AttrSpec{Name: "puppet_server", Type: cty.String, Required: false},
"staging_dir": &hcldec.AttrSpec{Name: "staging_dir", Type: cty.String, Required: false},
"working_directory": &hcldec.AttrSpec{Name: "working_directory", Type: cty.String, Required: false},
"elevated_user": &hcldec.AttrSpec{Name: "elevated_user", Type: cty.String, Required: false},
"elevated_password": &hcldec.AttrSpec{Name: "elevated_password", Type: cty.String, Required: false},
}
return s
}

View File

@ -1,227 +0,0 @@
package puppetserver
import (
"io/ioutil"
"os"
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func testConfig() (config map[string]interface{}, tf *os.File) {
tf, err := ioutil.TempFile("", "packer")
if err != nil {
panic(err)
}
config = map[string]interface{}{
"puppet_server": tf.Name(),
}
return config, tf
}
func TestProvisioner_Impl(t *testing.T) {
var raw interface{}
raw = &Provisioner{}
if _, ok := raw.(packersdk.Provisioner); !ok {
t.Fatalf("must be a Provisioner")
}
}
func TestProvisionerPrepare_puppetBinDir(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "puppet_bin_dir")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a good one
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("error tempfile: %s", err)
}
defer os.Remove(tf.Name())
config["puppet_bin_dir"] = tf.Name()
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_clientPrivateKeyPath(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "client_private_key_path")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with bad paths
config["client_private_key_path"] = "i-should-not-exist"
p = new(Provisioner)
err = p.Prepare(config)
if err == nil {
t.Fatal("should be an error")
}
// Test with a good one
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("error: %s", err)
}
defer os.RemoveAll(td)
config["client_private_key_path"] = td
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_clientCertPath(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "client_cert_path")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with bad paths
config["client_cert_path"] = "i-should-not-exist"
p = new(Provisioner)
err = p.Prepare(config)
if err == nil {
t.Fatal("should be an error")
}
// Test with a good one
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("error: %s", err)
}
defer os.RemoveAll(td)
config["client_cert_path"] = td
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_executeCommand(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "execute_command")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_facterFacts(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "facter")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with malformed fact
config["facter"] = "fact=stringified"
p = new(Provisioner)
err = p.Prepare(config)
if err == nil {
t.Fatal("should be an error")
}
// Test with a good one
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("error: %s", err)
}
defer os.RemoveAll(td)
facts := make(map[string]string)
facts["fact_name"] = "fact_value"
config["facter"] = facts
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Make sure the default facts are present
delete(config, "facter")
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.Facter == nil {
t.Fatalf("err: Default facts are not set in the Puppet provisioner!")
}
if _, ok := p.config.Facter["packer_build_name"]; !ok {
t.Fatalf("err: packer_build_name fact not set in the Puppet provisioner!")
}
if _, ok := p.config.Facter["packer_builder_type"]; !ok {
t.Fatalf("err: packer_builder_type fact not set in the Puppet provisioner!")
}
}
func TestProvisionerPrepare_stagingDir(t *testing.T) {
config, tempfile := testConfig()
defer os.Remove(tempfile.Name())
defer tempfile.Close()
delete(config, "staging_dir")
p := new(Provisioner)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Make sure the default staging directory is correct
if p.config.StagingDir != "/tmp/packer-puppet-server" {
t.Fatalf("err: Default staging_dir is not set in the Puppet provisioner!")
}
// Make sure default staging directory can be overridden
config["staging_dir"] = "/tmp/override"
p = new(Provisioner)
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.StagingDir != "/tmp/override" {
t.Fatalf("err: Overridden staging_dir is not set correctly in the Puppet provisioner!")
}
}

View File

@ -1,13 +0,0 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var PuppetServerPluginVersion *version.PluginVersion
func init() {
PuppetServerPluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}

View File

@ -1,198 +0,0 @@
---
description: >
The masterless Puppet Packer provisioner configures Puppet to run on the
machines by Packer from local modules and manifest files. Modules and
manifests
can be uploaded from your local machine to the remote machine or can simply
use
remote paths. Puppet is run in masterless mode, meaning it never communicates
to a Puppet master.
page_title: Puppet Masterless - Provisioners
---
# Puppet (Masterless) Provisioner
@include 'provisioners/unmaintained-plugin.mdx'
Type: `puppet-masterless`
The masterless Puppet Packer provisioner configures Puppet to run on the
machines by Packer from local modules and manifest files. Modules and manifests
can be uploaded from your local machine to the remote machine or can simply use
remote paths (perhaps obtained using something like the shell provisioner).
Puppet is run in masterless mode, meaning it never communicates to a Puppet
master.
-> **Note:** Puppet will _not_ be installed automatically by this
provisioner. This provisioner expects that Puppet is already installed on the
machine. It is common practice to use the [shell
provisioner](/docs/provisioners/shell) before the Puppet provisioner to do
this.
## Basic Example
The example below is fully functional and expects the configured manifest file
to exist relative to your working directory.
```json
{
"type": "puppet-masterless",
"manifest_file": "site.pp"
}
```
## Configuration Reference
The reference of available configuration options is listed below.
Required parameters:
- `manifest_file` (string) - This is either a path to a puppet manifest
(`.pp` file) _or_ a directory containing multiple manifests that puppet
will apply (the ["main
manifest"](https://docs.puppetlabs.com/puppet/latest/reference/dirs_manifest)).
These file(s) must exist on your local system and will be uploaded to the
remote machine.
Optional parameters:
- `execute_command` (string) - The command-line to execute Puppet. This is a
[template engine](/docs/templates/legacy_json_templates/engine). Therefore, you
may use user variables and template functions in this field. In addition,
you may use the following extra variables:
- FacterVars: Additional facts to set when executing Puppet, joined for
use in a command. This is internal and not settable by the user.
- HieraConfigPath: Path to a hiera configuration file to upload and use;
set in the template option hiera_config_path
- ManifestDir: The directory of the manifest file on the remote machine.
- ManifestFile: The manifest file on the remote machine.
- ModulePath: The module_paths, combined into one path variable.
- ModulePathJoiner: A colon `:` on posix systems and a semicolon `;` on
Windows.
- PuppetBinDir: The directory that the Puppet binary is in.
- Sudo: Returns false when PreventSudo is set, and true when it is not.
- WorkingDir: The value provided in the `working_directory` option.
See below for `execute_command`'s default value.
* `extra_arguments` (array of strings) - Additional options to pass to the
Puppet command. This allows for customization of
`execute_command` without having to completely replace or subsume its
contents, making forward-compatible customizations much easier to maintain.
This string is lazy-evaluated so one can incorporate logic driven by
template variables as well as private elements of ExecuteTemplate (see
source: provisioner/puppet-masterless/provisioner.go).
```text
[
{{if ne "{{user environment}}" ""}}--environment={{user environment}}{{end}},
{{if ne ".ModulePath" ""}}--modulepath="{{.ModulePath}}{{.ModulePathJoiner}}$(puppet config print {{if ne "{{user `environment`}}" ""}}--environment={{user `environment`}}{{end}} modulepath)"{{end}}
]
```
* `facter` (object of key:value strings) - Additional
[facts](https://docs.puppet.com/facter/) to make available to the Puppet
run.
* `guest_os_type` (string) - The remote host's OS type ('windows' or 'unix')
to tailor command-line and path separators. (default: unix).
* `hiera_config_path` (string) - Local path to self-contained Hiera data to
be uploaded. NOTE: If you need data directories they must be previously
transferred with a File provisioner.
* `ignore_exit_codes` (boolean) - If true, Packer will ignore failures.
* `manifest_dir` (string) - Local directory with manifests to be uploaded.
This is useful if your main manifest uses imports, but the directory might
not contain the `manifest_file` itself.
~> `manifest_dir` is passed to Puppet as `--manifestdir` option. This
option was deprecated in puppet 3.6, and removed in puppet 4.0. If you have
multiple manifests you should use `manifest_file` instead.
- `module_paths` (array of strings) - Array of local module directories to be
uploaded.
- `prevent_sudo` (boolean) - On Unix platforms Puppet is typically invoked
with `sudo`. If true, it will be omitted. (default: false)
- `puppet_bin_dir` (string) - Path to the Puppet binary. Ideally the program
should be on the system (unix: `$PATH`, windows: `%PATH%`), but some
builders (eg. Docker) do not run profile-setup scripts and therefore PATH
might be empty or minimal. On Windows, spaces should be `^`-escaped, i.e.
`c:/program^ files/puppet^ labs/puppet/bin`.
- `staging_directory` (string) - Directory to where uploaded files will be
placed (unix: "/tmp/packer-puppet-masterless", windows:
"%SYSTEMROOT%/Temp/packer-puppet-masterless"). It doesn't need to
pre-exist, but the parent must have permissions sufficient for the account
Packer connects as to create directories and write files. Use a Shell
provisioner to prepare the way if needed.
- `working_directory` (string) - Directory from which `execute_command` will
be run. If using Hiera files with relative paths, this option can be
helpful. (default: `staging_directory`)
- `elevated_user` and `elevated_password` (string) - If specified, Puppet
will be run with elevated privileges using the given Windows user. See the
[powershell](/docs/provisioners/powershell) provisioner for the full
details. This is a [template engine](/docs/templates/legacy_json_templates/engine).
Therefore, you may use user variables and template functions in this
field, including `` {{ build `Password`}} `` to use the password being used
by the communicator to connect to your instance.
@include 'provisioners/common-config.mdx'
## Execute Command
By default, Packer uses the following command (broken across multiple lines for
readability) to execute Puppet:
```shell
cd {{.WorkingDir}} &&
{{if ne .FacterVars ""}}{{.FacterVars}} {{end}}
{{if .Sudo}}sudo -E {{end}}
{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}
puppet apply --detailed-exitcodes
{{if .Debug}}--debug {{end}}
{{if ne .ModulePath ""}}--modulepath='{{.ModulePath}}' {{end}}
{{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}}
{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}}
{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}
{{.ManifestFile}}
```
The following command is used if guest OS type is windows:
```shell
cd {{.WorkingDir}} &&
{{if ne .FacterVars ""}}{{.FacterVars}} && {{end}}
{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}
puppet apply --detailed-exitcodes
{{if .Debug}}--debug {{end}}
{{if ne .ModulePath ""}}--modulepath='{{.ModulePath}}' {{end}}
{{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}}
{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}}
{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}
{{.ManifestFile}}
```
## Default Facts
In addition to being able to specify custom Facter facts using the `facter`
configuration, the provisioner automatically defines certain commonly useful
facts:
- `packer_build_name` is set to the name of the build that Packer is running.
This is most useful when Packer is making multiple builds and you want to
distinguish them in your Hiera hierarchy.
- `packer_builder_type` is the type of the builder that was used to create
the machine that Puppet is running on. This is useful if you want to run
only certain parts of your Puppet code on systems built with certain
builders.

View File

@ -1,176 +0,0 @@
---
description: |
The puppet-server Packer provisioner provisions Packer machines with Puppet by
connecting to a Puppet master.
page_title: Puppet Server - Provisioners
---
# Puppet Server Provisioner
@include 'provisioners/unmaintained-plugin.mdx'
Type: `puppet-server`
The `puppet-server` Packer provisioner provisions Packer machines with Puppet
by connecting to a Puppet master.
-> **Note:** Puppet will _not_ be installed automatically by this
provisioner. This provisioner expects that Puppet is already installed on the
machine. It is common practice to use the [shell
provisioner](/docs/provisioners/shell) before the Puppet provisioner to do
this.
## Basic Example
The example below is fully functional and expects a Puppet server to be
accessible from your network.
```json
{
"type": "puppet-server",
"extra_arguments": "--test --pluginsync",
"facter": {
"server_role": "webserver"
}
}
```
## Configuration Reference
The reference of available configuration options is listed below.
The provisioner takes various options. None are strictly required. They are
listed below:
- `client_cert_path` (string) - Path to the directory on your disk that
contains the client certificate for the node. This defaults to nothing, in
which case a client cert won't be uploaded.
- `client_private_key_path` (string) - Path to the directory on your disk
that contains the client private key for the node. This defaults to
nothing, in which case a client private key won't be uploaded.
- `execute_command` (string) - The command-line to execute Puppet. This is a
[template engine](/docs/templates/legacy_json_templates/engine). Therefore, you
may use user variables and template functions in this field. In addition,
you may use the following extra variables:
- ClientCertPath: The value set in the template option
`client_cert_path`.
- ClientPrivateKeyPath: The value set in the template option
`client_private_key_path`.
- FacterVars: Additional facts to set when executing Puppet, joined for
use in a command. This is internal and not settable by the user.
- PuppetNode: The hostname of the Puppet node, set in `puppet_node`.
- PuppetServer: The hostname of the Puppet server, set in `puppet_server`
- PuppetBinDir: The directory that the Puppet binary is in.
- Sudo: Returns false when PreventSudo is set, and true when it is not.
- WorkingDir: The value provided in the `working_directory` option.
See below for `execute_command`'s default value.
* `extra_arguments` (array of strings) - Additional options to pass to the
Puppet command. This allows for customization of `execute_command` without
having to completely replace or subsume its contents, making
forward-compatible customizations much easier to maintain.
This string is lazy-evaluated so one can incorporate logic driven by
template variables as well as private elements of ExecuteTemplate (see
source: provisioner/puppet-server/provisioner.go).
```text
[
{{if ne "{{user environment}}" ""}}--environment={{user environment}}{{end}}
]
```
* `facter` (object of key/value strings) - Additional
[facts](https://puppet.com/docs/puppet/latest/facter.html) to make available to the Puppet run.
* `guest_os_type` (string) - The remote host's OS type ('windows' or 'unix')
to tailor command-line and path separators. (default: unix).
* `ignore_exit_codes` (boolean) - If true, Packer will ignore failures.
* `prevent_sudo` (boolean) - On Unix platforms Puppet is typically invoked
with `sudo`. If true, it will be omitted. (default: false)
* `puppet_bin_dir` (string) - Path to the Puppet binary. Ideally the program
should be on the system (unix: `$PATH`, windows: `%PATH%`), but some
builders (eg. Docker) do not run profile-setup scripts and therefore PATH
might be empty or minimal. On Windows, spaces should be `^`-escaped, i.e.
`c:/program^ files/puppet^ labs/puppet/bin`.
* `puppet_node` (string) - The name of the node. If this isn't set, the fully
qualified domain name will be used.
* `puppet_server` (string) - Hostname of the Puppet server. By default
"puppet" will be used.
* `staging_dir` (string) - Directory to where uploaded files will be placed
(unix: "/tmp/packer-puppet-masterless", windows:
"%SYSTEMROOT%/Temp/packer-puppet-masterless"). It doesn't need to
pre-exist, but the parent must have permissions sufficient for the account
Packer connects as to create directories and write files. Use a Shell
provisioner to prepare the way if needed.
* `working_directory` (string) - Directory from which `execute_command` will
be run. If using Hiera files with relative paths, this option can be
helpful. (default: `staging_directory`)
* `elevated_user` and `elevated_password` (string) - If specified, Puppet
will be run with elevated privileges using the given Windows user. See the
[powershell](/docs/provisioners/powershell) provisioner for the full
details. This is a [template engine](/docs/templates/legacy_json_templates/engine).
Therefore, you may use user variables and template functions in this
field, including `` {{ build `Password`}} `` to use the password being used
by the communicator to connect to your instance.
@include 'provisioners/common-config.mdx'
## Execute Command
By default, Packer uses the following command (broken across multiple lines for
readability) to execute Puppet:
```shell
cd {{.WorkingDir}} &&
{{if ne .FacterVars ""}}{{.FacterVars}} {{end}}
{{if .Sudo}}sudo -E {{end}}
{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}
puppet agent --onetime --no-daemonize --detailed-exitcodes
{{if .Debug}}--debug {{end}}
{{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}}
{{if ne .PuppetNode ""}}--certname='{{.PuppetNode}}' {{end}}
{{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}}
{{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}
{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}
```
The following command is used if guest OS type is windows:
```shell
cd {{.WorkingDir}} &&
{{if ne .FacterVars ""}}{{.FacterVars}} && {{end}}
{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}
puppet agent --onetime --no-daemonize --detailed-exitcodes
{{if .Debug}}--debug {{end}}
{{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}}
{{if ne .PuppetNode ""}}--certname='{{.PuppetNode}}' {{end}}
{{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}}
{{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}
{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}
```
## Default Facts
In addition to being able to specify custom Facter facts using the `facter`
configuration, the provisioner automatically defines certain commonly useful
facts:
- `packer_build_name` is set to the name of the build that Packer is running.
This is most useful when Packer is making multiple builds and you want to
distinguish them in your Hiera hierarchy.
- `packer_builder_type` is the type of the builder that was used to create
the machine that Puppet is running on. This is useful if you want to run
only certain parts of your Puppet code on systems built with certain
builders.

View File

@ -844,14 +844,6 @@
"title": "PowerShell",
"path": "provisioners/powershell"
},
{
"title": "Puppet Masterless",
"path": "provisioners/puppet-masterless"
},
{
"title": "Puppet Server",
"path": "provisioners/puppet-server"
},
{
"title": "Salt Masterless",
"path": "provisioners/salt-masterless"