Extract ansible plugins (#10912)

* Remove ansible components and docs

* Vendored packer-plugin-ansible

* Add remote ansible docs
This commit is contained in:
Wilken Rivera 2021-04-16 10:31:09 -04:00 committed by GitHub
parent bb1a025f60
commit ceb96d061a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 482 additions and 3593 deletions

View File

@ -71,8 +71,6 @@ import (
vagrantcloudpostprocessor "github.com/hashicorp/packer/post-processor/vagrant-cloud"
yandexexportpostprocessor "github.com/hashicorp/packer/post-processor/yandex-export"
yandeximportpostprocessor "github.com/hashicorp/packer/post-processor/yandex-import"
ansibleprovisioner "github.com/hashicorp/packer/provisioner/ansible"
ansiblelocalprovisioner "github.com/hashicorp/packer/provisioner/ansible-local"
azuredtlartifactprovisioner "github.com/hashicorp/packer/provisioner/azure-dtlartifact"
breakpointprovisioner "github.com/hashicorp/packer/provisioner/breakpoint"
chefclientprovisioner "github.com/hashicorp/packer/provisioner/chef-client"
@ -143,8 +141,6 @@ var Builders = map[string]packersdk.Builder{
}
var Provisioners = map[string]packersdk.Provisioner{
"ansible": new(ansibleprovisioner.Provisioner),
"ansible-local": new(ansiblelocalprovisioner.Provisioner),
"azure-dtlartifact": new(azuredtlartifactprovisioner.Provisioner),
"breakpoint": new(breakpointprovisioner.Provisioner),
"chef-client": new(chefclientprovisioner.Provisioner),

View File

@ -15,6 +15,8 @@ import (
amazonamidatasource "github.com/hashicorp/packer-plugin-amazon/datasource/ami"
amazonsecretsmanagerdatasource "github.com/hashicorp/packer-plugin-amazon/datasource/secretsmanager"
anazibimportpostprocessor "github.com/hashicorp/packer-plugin-amazon/post-processor/import"
ansibleprovisioner "github.com/hashicorp/packer-plugin-ansible/provisioner/ansible"
ansiblelocalprovisioner "github.com/hashicorp/packer-plugin-ansible/provisioner/ansible-local"
dockerbuilder "github.com/hashicorp/packer-plugin-docker/builder/docker"
dockerimportpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-import"
dockerpushpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-push"
@ -48,7 +50,10 @@ var VendoredBuilders = map[string]packersdk.Builder{
// VendoredProvisioners are provisioner components that were once bundled with the
// Packer core, but are now being imported from their counterpart plugin repos
var VendoredProvisioners = map[string]packersdk.Provisioner{}
var VendoredProvisioners = map[string]packersdk.Provisioner{
"ansible": new(ansibleprovisioner.Provisioner),
"ansible-local": new(ansiblelocalprovisioner.Provisioner),
}
// VendoredPostProcessors are post-processor components that were once bundled with the
// Packer core, but are now being imported from their counterpart plugin repos

1
go.mod
View File

@ -49,6 +49,7 @@ require (
github.com/hashicorp/go-version v1.2.0
github.com/hashicorp/hcl/v2 v2.9.1
github.com/hashicorp/packer-plugin-amazon v0.0.1
github.com/hashicorp/packer-plugin-ansible v0.0.2
github.com/hashicorp/packer-plugin-docker v0.0.7
github.com/hashicorp/packer-plugin-sdk v0.1.3
github.com/hashicorp/packer-plugin-vsphere v0.0.0-20210415100050-d0269b5646e6

3
go.sum
View File

@ -433,6 +433,8 @@ github.com/hashicorp/packer v1.7.0/go.mod h1:3KRJcwOctl2JaAGpQMI1bWQRArfWNWqcYjO
github.com/hashicorp/packer v1.7.1/go.mod h1:ApnmMINvuhhnfPyTVqZu6jznDWPVYDJUw7e188DFCmo=
github.com/hashicorp/packer-plugin-amazon v0.0.1 h1:EuyjNK9bL7WhQeIJzhBJxOx8nyc61ai5UbOsb1PIVwI=
github.com/hashicorp/packer-plugin-amazon v0.0.1/go.mod h1:12c9msibyHdId+Mk/pCbdRb1KaLIhaNyxeJ6n8bZt30=
github.com/hashicorp/packer-plugin-ansible v0.0.2 h1:nvBtCedXhUI5T6Up5+bmhlY7rmk8FjWuFv9A2joK7TU=
github.com/hashicorp/packer-plugin-ansible v0.0.2/go.mod h1:ocXB4KTU+I+DBRGfMP4XE7dPlURaUnb7NJvyddZ6bh0=
github.com/hashicorp/packer-plugin-docker v0.0.7 h1:hMTrH7vrkFIjphtbbtpuzffTzSjMNgxayo2DPLz9y+c=
github.com/hashicorp/packer-plugin-docker v0.0.7/go.mod h1:IpeKlwOSy2kdgQcysqd3gCsoqjME9jtmpFoKxn7RRNI=
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
@ -446,6 +448,7 @@ github.com/hashicorp/packer-plugin-sdk v0.0.14/go.mod h1:tNb3XzJPnjMl3QuUdKmF47B
github.com/hashicorp/packer-plugin-sdk v0.1.0/go.mod h1:CFsC20uZjtER/EnTn/CSMKD0kEdkqOVev8mtOmfnZiI=
github.com/hashicorp/packer-plugin-sdk v0.1.1/go.mod h1:1d3nqB9LUsXMQaNUiL67Q+WYEtjsVcLNTX8ikVlpBrc=
github.com/hashicorp/packer-plugin-sdk v0.1.2/go.mod h1:KRjczE1/c9NV5Re+PXt3myJsVTI/FxEHpZjRjOH0Fug=
github.com/hashicorp/packer-plugin-sdk v0.1.3-0.20210407232143-c217d82aefb6/go.mod h1:xePpgQgQYv/bamiypx3hH9ukidxDdcN8q0R0wLi8IEQ=
github.com/hashicorp/packer-plugin-sdk v0.1.3 h1:oHTVlgoX2piUzL54+LBo9uIMfW+L/kY7or83dDStdIY=
github.com/hashicorp/packer-plugin-sdk v0.1.3/go.mod h1:xePpgQgQYv/bamiypx3hH9ukidxDdcN8q0R0wLi8IEQ=
github.com/hashicorp/packer-plugin-vsphere v0.0.0-20210415100050-d0269b5646e6 h1:pOv7Apd4P3KEpNBHLV4E7tKlwHoInCU/bnPVadGSDxY=

View File

@ -1,497 +0,0 @@
package ansiblelocal
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"fmt"
"os/exec"
"github.com/hashicorp/packer-plugin-docker/builder/docker"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
builderT "github.com/hashicorp/packer/acctest"
"github.com/hashicorp/packer/provisioner/file"
)
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_Defaults(t *testing.T) {
var p Provisioner
config := testConfig()
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["playbook_file"] = playbook_file.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.HasPrefix(filepath.ToSlash(p.config.StagingDir), DefaultStagingDir) {
t.Fatalf("unexpected staging dir %s, expected %s",
p.config.StagingDir, DefaultStagingDir)
}
}
func TestProvisionerPrepare_PlaybookFile(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["playbook_file"] = ""
err = p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["playbook_file"] = playbook_file.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_PlaybookFiles(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["playbook_file"] = ""
config["playbook_files"] = []string{}
err = p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["playbook_file"] = playbook_file.Name()
config["playbook_files"] = []string{"some_other_file"}
err = p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
p = Provisioner{}
config["playbook_file"] = playbook_file.Name()
config["playbook_files"] = []string{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
config["playbook_file"] = ""
config["playbook_files"] = []string{playbook_file.Name()}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerProvision_PlaybookFiles(t *testing.T) {
var p Provisioner
config := testConfig()
playbooks := createTempFiles("", 3)
defer removeFiles(playbooks...)
config["playbook_files"] = playbooks
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
comm := &communicatorMock{}
if err := p.Provision(context.Background(), packersdk.TestUi(t), comm, make(map[string]interface{})); err != nil {
t.Fatalf("err: %s", err)
}
assertPlaybooksUploaded(comm, playbooks)
assertPlaybooksExecuted(comm, playbooks)
}
func TestProvisionerProvision_PlaybookFilesWithPlaybookDir(t *testing.T) {
var p Provisioner
config := testConfig()
playbook_dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Failed to create playbook_dir: %s", err)
}
defer os.RemoveAll(playbook_dir)
playbooks := createTempFiles(playbook_dir, 3)
playbookNames := make([]string, 0, len(playbooks))
playbooksInPlaybookDir := make([]string, 0, len(playbooks))
for _, playbook := range playbooks {
playbooksInPlaybookDir = append(playbooksInPlaybookDir, strings.TrimPrefix(playbook, playbook_dir))
playbookNames = append(playbookNames, filepath.Base(playbook))
}
config["playbook_files"] = playbooks
config["playbook_dir"] = playbook_dir
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
comm := &communicatorMock{}
if err := p.Provision(context.Background(), packersdk.TestUi(t), comm, make(map[string]interface{})); err != nil {
t.Fatalf("err: %s", err)
}
assertPlaybooksNotUploaded(comm, playbookNames)
assertPlaybooksExecuted(comm, playbooksInPlaybookDir)
}
func TestProvisionerPrepare_InventoryFile(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["playbook_file"] = ""
err = p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["playbook_file"] = playbook_file.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
inventory_file, err := ioutil.TempFile("", "inventory")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(inventory_file.Name())
config["inventory_file"] = inventory_file.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_Dirs(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["playbook_file"] = ""
err = p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["playbook_file"] = playbook_file.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
config["playbook_paths"] = []string{playbook_file.Name()}
err = p.Prepare(config)
if err == nil {
t.Fatal("should error if playbook paths is not a dir")
}
config["playbook_paths"] = []string{os.TempDir()}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
config["role_paths"] = []string{playbook_file.Name()}
err = p.Prepare(config)
if err == nil {
t.Fatal("should error if role paths is not a dir")
}
config["role_paths"] = []string{os.TempDir()}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
config["group_vars"] = playbook_file.Name()
err = p.Prepare(config)
if err == nil {
t.Fatalf("should error if group_vars path is not a dir")
}
config["group_vars"] = os.TempDir()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
config["host_vars"] = playbook_file.Name()
err = p.Prepare(config)
if err == nil {
t.Fatalf("should error if host_vars path is not a dir")
}
config["host_vars"] = os.TempDir()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_CleanStagingDir(t *testing.T) {
var p Provisioner
config := testConfig()
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["playbook_file"] = playbook_file.Name()
config["clean_staging_directory"] = true
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !p.config.CleanStagingDir {
t.Fatalf("expected clean_staging_directory to be set")
}
}
func TestProvisionerProvisionDocker_PlaybookFiles(t *testing.T) {
testProvisionerProvisionDockerWithPlaybookFiles(t, playbookFilesDockerTemplate)
}
func TestProvisionerProvisionDocker_PlaybookFilesWithPlaybookDir(t *testing.T) {
testProvisionerProvisionDockerWithPlaybookFiles(t, playbookFilesWithPlaybookDirDockerTemplate)
}
func testProvisionerProvisionDockerWithPlaybookFiles(t *testing.T, templateString string) {
if os.Getenv("PACKER_ACC") == "" {
t.Skip("This test is only run with PACKER_ACC=1")
}
// this should be a precheck
cmd := exec.Command("docker", "-v")
err := cmd.Run()
if err != nil {
t.Error("docker command not found; please make sure docker is installed")
}
builderT.Test(t, builderT.TestCase{
Builder: &docker.Builder{},
Template: templateString,
Check: func(a []packersdk.Artifact) error {
actualContent, err := ioutil.ReadFile("hello_world")
if err != nil {
return fmt.Errorf("Expected file not found: %s", err)
}
expectedContent := "Hello world!"
if string(actualContent) != expectedContent {
return fmt.Errorf(`Unexpected file content: expected="%s", actual="%s"`, expectedContent, actualContent)
}
return nil
},
Teardown: func() error {
os.Remove("hello_world")
return nil
},
ProvisionerStore: packersdk.MapOfProvisioner{
"ansible-local": func() (packersdk.Provisioner, error) { return &Provisioner{}, nil },
"file": func() (packersdk.Provisioner, error) { return &file.Provisioner{}, nil },
},
})
}
func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) {
cmdIndex := 0
for _, playbook := range playbooks {
playbook = filepath.ToSlash(playbook)
for ; cmdIndex < len(comm.startCommand); cmdIndex++ {
cmd := comm.startCommand[cmdIndex]
if strings.Contains(cmd, "ansible-playbook") && strings.Contains(cmd, playbook) {
break
}
}
if cmdIndex == len(comm.startCommand) {
panic(fmt.Sprintf("Playbook %s was not executed", playbook))
}
}
}
func assertPlaybooksUploaded(comm *communicatorMock, playbooks []string) {
uploadIndex := 0
for _, playbook := range playbooks {
playbook = filepath.ToSlash(playbook)
for ; uploadIndex < len(comm.uploadDestination); uploadIndex++ {
dest := comm.uploadDestination[uploadIndex]
if strings.HasSuffix(dest, playbook) {
break
}
}
if uploadIndex == len(comm.uploadDestination) {
panic(fmt.Sprintf("Playbook %s was not uploaded", playbook))
}
}
}
func assertPlaybooksNotUploaded(comm *communicatorMock, playbooks []string) {
for _, playbook := range playbooks {
playbook = filepath.ToSlash(playbook)
for _, destination := range comm.uploadDestination {
if strings.HasSuffix(destination, playbook) {
panic(fmt.Sprintf("Playbook %s was uploaded", playbook))
}
}
}
}
func testConfig() map[string]interface{} {
m := make(map[string]interface{})
return m
}
func createTempFile(dir string) string {
file, err := ioutil.TempFile(dir, "")
if err != nil {
panic(fmt.Sprintf("err: %s", err))
}
return file.Name()
}
func createTempFiles(dir string, numFiles int) []string {
files := make([]string, 0, numFiles)
defer func() {
// Cleanup the files if not all were created.
if len(files) < numFiles {
for _, file := range files {
os.Remove(file)
}
}
}()
for i := 0; i < numFiles; i++ {
files = append(files, createTempFile(dir))
}
return files
}
func removeFiles(files ...string) {
for _, file := range files {
os.Remove(file)
}
}
const playbookFilesDockerTemplate = `
{
"builders": [
{
"type": "test",
"image": "williamyeh/ansible:centos7",
"discard": true
}
],
"provisioners": [
{
"type": "ansible-local",
"playbook_files": [
"test-fixtures/hello.yml",
"test-fixtures/world.yml"
]
},
{
"type": "file",
"source": "/tmp/hello_world",
"destination": "hello_world",
"direction": "download"
}
]
}
`
const playbookFilesWithPlaybookDirDockerTemplate = `
{
"builders": [
{
"type": "test",
"image": "williamyeh/ansible:centos7",
"discard": true
}
],
"provisioners": [
{
"type": "ansible-local",
"playbook_files": [
"test-fixtures/hello.yml",
"test-fixtures/world.yml"
],
"playbook_dir": "test-fixtures"
},
{
"type": "file",
"source": "/tmp/hello_world",
"destination": "hello_world",
"direction": "download"
}
]
}
`

View File

@ -1,5 +0,0 @@
---
- hosts: all
tasks:
- name: write Hello
shell: echo -n "Hello" >> /tmp/hello_world

View File

@ -1,5 +0,0 @@
---
- hosts: all
tasks:
- name: write world!
shell: echo -n " world!" >> /tmp/hello_world

View File

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

View File

@ -1,186 +0,0 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.connection.ssh import Connection as SSHConnection
DOCUMENTATION = '''
connection: packer
short_description: ssh based connections for powershell via packer
description:
- This connection plugin allows ansible to communicate to the target packer machines via ssh based connections for powershell.
author: Packer Community
version_added: na
options:
host:
description: Hostname/ip to connect to.
default: inventory_hostname
vars:
- name: ansible_host
- name: ansible_ssh_host
host_key_checking:
#constant: HOST_KEY_CHECKING
description: Determines if ssh should check host keys
type: boolean
ini:
- section: defaults
key: 'host_key_checking'
env:
- name: ANSIBLE_HOST_KEY_CHECKING
password:
description: Authentication password for the C(remote_user). Can be supplied as CLI option.
vars:
- name: ansible_password
- name: ansible_ssh_pass
ssh_args:
description: Arguments to pass to all ssh cli tools
default: '-C -o ControlMaster=auto -o ControlPersist=60s'
ini:
- section: 'ssh_connection'
key: 'ssh_args'
env:
- name: ANSIBLE_SSH_ARGS
ssh_common_args:
description: Common extra args for all ssh CLI tools
vars:
- name: ansible_ssh_common_args
ssh_executable:
default: ssh
description:
- This defines the location of the ssh binary. It defaults to `ssh` which will use the first ssh binary available in $PATH.
- This option is usually not required, it might be useful when access to system ssh is restricted,
or when using ssh wrappers to connect to remote hosts.
env: [{name: ANSIBLE_SSH_EXECUTABLE}]
ini:
- {key: ssh_executable, section: ssh_connection}
yaml: {key: ssh_connection.ssh_executable}
#const: ANSIBLE_SSH_EXECUTABLE
version_added: "2.2"
scp_extra_args:
description: Extra exclusive to the 'scp' CLI
vars:
- name: ansible_scp_extra_args
sftp_extra_args:
description: Extra exclusive to the 'sftp' CLI
vars:
- name: ansible_sftp_extra_args
ssh_extra_args:
description: Extra exclusive to the 'ssh' CLI
vars:
- name: ansible_ssh_extra_args
retries:
# constant: ANSIBLE_SSH_RETRIES
description: Number of attempts to connect.
default: 3
type: integer
env:
- name: ANSIBLE_SSH_RETRIES
ini:
- section: connection
key: retries
- section: ssh_connection
key: retries
port:
description: Remote port to connect to.
type: int
default: 22
ini:
- section: defaults
key: remote_port
env:
- name: ANSIBLE_REMOTE_PORT
vars:
- name: ansible_port
- name: ansible_ssh_port
remote_user:
description:
- User name with which to login to the remote server, normally set by the remote_user keyword.
- If no user is supplied, Ansible will let the ssh client binary choose the user as it normally
ini:
- section: defaults
key: remote_user
env:
- name: ANSIBLE_REMOTE_USER
vars:
- name: ansible_user
- name: ansible_ssh_user
pipelining:
default: ANSIBLE_PIPELINING
description:
- Pipelining reduces the number of SSH operations required to execute a module on the remote server,
by executing many Ansible modules without actual file transfer.
- This can result in a very significant performance improvement when enabled.
- However this conflicts with privilege escalation (become).
For example, when using sudo operations you must first disable 'requiretty' in the sudoers file for the target hosts,
which is why this feature is disabled by default.
env:
- name: ANSIBLE_PIPELINING
#- name: ANSIBLE_SSH_PIPELINING
ini:
- section: defaults
key: pipelining
#- section: ssh_connection
# key: pipelining
type: boolean
vars:
- name: ansible_pipelining
- name: ansible_ssh_pipelining
private_key_file:
description:
- Path to private key file to use for authentication
ini:
- section: defaults
key: private_key_file
env:
- name: ANSIBLE_PRIVATE_KEY_FILE
vars:
- name: ansible_private_key_file
- name: ansible_ssh_private_key_file
control_path:
default: null
description:
- This is the location to save ssh's ControlPath sockets, it uses ssh's variable substitution.
- Since 2.3, if null, ansible will generate a unique hash. Use `%(directory)s` to indicate where to use the control dir path setting.
env:
- name: ANSIBLE_SSH_CONTROL_PATH
ini:
- key: control_path
section: ssh_connection
control_path_dir:
default: ~/.ansible/cp
description:
- This sets the directory to use for ssh control path if the control path setting is null.
- Also, provides the `%(directory)s` variable for the control path setting.
env:
- name: ANSIBLE_SSH_CONTROL_PATH_DIR
ini:
- section: ssh_connection
key: control_path_dir
sftp_batch_mode:
default: True
description: 'TODO: write it'
env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
ini:
- {key: sftp_batch_mode, section: ssh_connection}
type: boolean
scp_if_ssh:
default: smart
description:
- "Prefered method to use when transfering files over ssh"
- When set to smart, Ansible will try them until one succeeds or they all fail
- If set to True, it will force 'scp', if False it will use 'sftp'
env: [{name: ANSIBLE_SCP_IF_SSH}]
ini:
- {key: scp_if_ssh, section: ssh_connection}
'''
class Connection(SSHConnection):
''' ssh based connections for powershell via packer'''
transport = 'packer'
has_pipelining = True
become_methods = []
allow_executable = False
module_implementation_preferences = ('.ps1', '')
def __init__(self, *args, **kwargs):
super(Connection, self).__init__(*args, **kwargs)

View File

@ -1,204 +0,0 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.connection.ssh import Connection as SSHConnection
DOCUMENTATION = '''
connection: packer
short_description: ssh based connections for powershell via packer
description:
- This connection plugin allows ansible to communicate to the target packer machines via ssh based connections for powershell.
author: Packer Community
version_added: na
options:
host:
description: Hostname/ip to connect to.
default: inventory_hostname
vars:
- name: ansible_host
- name: ansible_ssh_host
host_key_checking:
description: Determines if ssh should check host keys
type: boolean
ini:
- section: defaults
key: 'host_key_checking'
- section: ssh_connection
key: 'host_key_checking'
version_added: '2.5'
env:
- name: ANSIBLE_HOST_KEY_CHECKING
- name: ANSIBLE_SSH_HOST_KEY_CHECKING
version_added: '2.5'
vars:
- name: ansible_host_key_checking
version_added: '2.5'
- name: ansible_ssh_host_key_checking
version_added: '2.5'
password:
description: Authentication password for the C(remote_user). Can be supplied as CLI option.
vars:
- name: ansible_password
- name: ansible_ssh_pass
ssh_args:
description: Arguments to pass to all ssh cli tools
default: '-C -o ControlMaster=auto -o ControlPersist=60s'
ini:
- section: 'ssh_connection'
key: 'ssh_args'
env:
- name: ANSIBLE_SSH_ARGS
ssh_common_args:
description: Common extra args for all ssh CLI tools
vars:
- name: ansible_ssh_common_args
ssh_executable:
default: ssh
description:
- This defines the location of the ssh binary. It defaults to `ssh` which will use the first ssh binary available in $PATH.
- This option is usually not required, it might be useful when access to system ssh is restricted,
or when using ssh wrappers to connect to remote hosts.
env: [{name: ANSIBLE_SSH_EXECUTABLE}]
ini:
- {key: ssh_executable, section: ssh_connection}
yaml: {key: ssh_connection.ssh_executable}
#const: ANSIBLE_SSH_EXECUTABLE
version_added: "2.2"
scp_extra_args:
description: Extra exclusive to the 'scp' CLI
vars:
- name: ansible_scp_extra_args
sftp_extra_args:
description: Extra exclusive to the 'sftp' CLI
vars:
- name: ansible_sftp_extra_args
ssh_extra_args:
description: Extra exclusive to the 'ssh' CLI
vars:
- name: ansible_ssh_extra_args
retries:
# constant: ANSIBLE_SSH_RETRIES
description: Number of attempts to connect.
default: 3
type: integer
env:
- name: ANSIBLE_SSH_RETRIES
ini:
- section: connection
key: retries
- section: ssh_connection
key: retries
port:
description: Remote port to connect to.
type: int
default: 22
ini:
- section: defaults
key: remote_port
env:
- name: ANSIBLE_REMOTE_PORT
vars:
- name: ansible_port
- name: ansible_ssh_port
remote_user:
description:
- User name with which to login to the remote server, normally set by the remote_user keyword.
- If no user is supplied, Ansible will let the ssh client binary choose the user as it normally
ini:
- section: defaults
key: remote_user
env:
- name: ANSIBLE_REMOTE_USER
vars:
- name: ansible_user
- name: ansible_ssh_user
pipelining:
default: ANSIBLE_PIPELINING
description:
- Pipelining reduces the number of SSH operations required to execute a module on the remote server,
by executing many Ansible modules without actual file transfer.
- This can result in a very significant performance improvement when enabled.
- However this conflicts with privilege escalation (become).
For example, when using sudo operations you must first disable 'requiretty' in the sudoers file for the target hosts,
which is why this feature is disabled by default.
env:
- name: ANSIBLE_PIPELINING
#- name: ANSIBLE_SSH_PIPELINING
ini:
- section: defaults
key: pipelining
#- section: ssh_connection
# key: pipelining
type: boolean
vars:
- name: ansible_pipelining
- name: ansible_ssh_pipelining
private_key_file:
description:
- Path to private key file to use for authentication
ini:
- section: defaults
key: private_key_file
env:
- name: ANSIBLE_PRIVATE_KEY_FILE
vars:
- name: ansible_private_key_file
- name: ansible_ssh_private_key_file
control_path:
default: null
description:
- This is the location to save ssh's ControlPath sockets, it uses ssh's variable substitution.
- Since 2.3, if null, ansible will generate a unique hash. Use `%(directory)s` to indicate where to use the control dir path setting.
env:
- name: ANSIBLE_SSH_CONTROL_PATH
ini:
- key: control_path
section: ssh_connection
control_path_dir:
default: ~/.ansible/cp
description:
- This sets the directory to use for ssh control path if the control path setting is null.
- Also, provides the `%(directory)s` variable for the control path setting.
env:
- name: ANSIBLE_SSH_CONTROL_PATH_DIR
ini:
- section: ssh_connection
key: control_path_dir
sftp_batch_mode:
default: True
description: 'TODO: write it'
env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
ini:
- {key: sftp_batch_mode, section: ssh_connection}
type: boolean
scp_if_ssh:
default: smart
description:
- "Prefered method to use when transfering files over ssh"
- When set to smart, Ansible will try them until one succeeds or they all fail
- If set to True, it will force 'scp', if False it will use 'sftp'
env: [{name: ANSIBLE_SCP_IF_SSH}]
ini:
- {key: scp_if_ssh, section: ssh_connection}
use_tty:
version_added: '2.5'
default: True
description: add -tt to ssh commands to force tty allocation
env: [{name: ANSIBLE_SSH_USETTY}]
ini:
- {key: usetty, section: ssh_connection}
type: boolean
yaml: {key: connection.usetty}
'''
class Connection(SSHConnection):
''' ssh based connections for powershell via packer'''
transport = 'packer'
has_pipelining = True
become_methods = []
allow_executable = False
module_implementation_preferences = ('.ps1', '')
def __init__(self, *args, **kwargs):
super(Connection, self).__init__(*args, **kwargs)

View File

@ -1,218 +0,0 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.connection.ssh import Connection as SSHConnection
DOCUMENTATION = '''
connection: packer
short_description: ssh based connections for powershell via packer
description:
- This connection plugin allows ansible to communicate to the target packer machines via ssh based connections for powershell.
author: Packer Community
version_added: na
options:
host:
description: Hostname/ip to connect to.
default: inventory_hostname
vars:
- name: ansible_host
- name: ansible_ssh_host
host_key_checking:
description: Determines if ssh should check host keys
type: boolean
ini:
- section: defaults
key: 'host_key_checking'
- section: ssh_connection
key: 'host_key_checking'
version_added: '2.5'
env:
- name: ANSIBLE_HOST_KEY_CHECKING
- name: ANSIBLE_SSH_HOST_KEY_CHECKING
version_added: '2.5'
vars:
- name: ansible_host_key_checking
version_added: '2.5'
- name: ansible_ssh_host_key_checking
version_added: '2.5'
password:
description: Authentication password for the C(remote_user). Can be supplied as CLI option.
vars:
- name: ansible_password
- name: ansible_ssh_pass
ssh_args:
description: Arguments to pass to all ssh cli tools
default: '-C -o ControlMaster=auto -o ControlPersist=60s'
ini:
- section: 'ssh_connection'
key: 'ssh_args'
env:
- name: ANSIBLE_SSH_ARGS
ssh_common_args:
description: Common extra args for all ssh CLI tools
vars:
- name: ansible_ssh_common_args
ssh_executable:
default: ssh
description:
- This defines the location of the ssh binary. It defaults to ``ssh`` which will use the first ssh binary available in $PATH.
- This option is usually not required, it might be useful when access to system ssh is restricted,
or when using ssh wrappers to connect to remote hosts.
env: [{name: ANSIBLE_SSH_EXECUTABLE}]
ini:
- {key: ssh_executable, section: ssh_connection}
#const: ANSIBLE_SSH_EXECUTABLE
version_added: "2.2"
sftp_executable:
default: sftp
description:
- This defines the location of the sftp binary. It defaults to ``sftp`` which will use the first binary available in $PATH.
env: [{name: ANSIBLE_SFTP_EXECUTABLE}]
ini:
- {key: sftp_executable, section: ssh_connection}
version_added: "2.6"
scp_executable:
default: scp
description:
- This defines the location of the scp binary. It defaults to `scp` which will use the first binary available in $PATH.
env: [{name: ANSIBLE_SCP_EXECUTABLE}]
ini:
- {key: scp_executable, section: ssh_connection}
version_added: "2.6"
scp_extra_args:
description: Extra exclusive to the ``scp`` CLI
vars:
- name: ansible_scp_extra_args
sftp_extra_args:
description: Extra exclusive to the ``sftp`` CLI
vars:
- name: ansible_sftp_extra_args
ssh_extra_args:
description: Extra exclusive to the 'ssh' CLI
vars:
- name: ansible_ssh_extra_args
retries:
# constant: ANSIBLE_SSH_RETRIES
description: Number of attempts to connect.
default: 3
type: integer
env:
- name: ANSIBLE_SSH_RETRIES
ini:
- section: connection
key: retries
- section: ssh_connection
key: retries
port:
description: Remote port to connect to.
type: int
default: 22
ini:
- section: defaults
key: remote_port
env:
- name: ANSIBLE_REMOTE_PORT
vars:
- name: ansible_port
- name: ansible_ssh_port
remote_user:
description:
- User name with which to login to the remote server, normally set by the remote_user keyword.
- If no user is supplied, Ansible will let the ssh client binary choose the user as it normally
ini:
- section: defaults
key: remote_user
env:
- name: ANSIBLE_REMOTE_USER
vars:
- name: ansible_user
- name: ansible_ssh_user
pipelining:
default: ANSIBLE_PIPELINING
description:
- Pipelining reduces the number of SSH operations required to execute a module on the remote server,
by executing many Ansible modules without actual file transfer.
- This can result in a very significant performance improvement when enabled.
- However this conflicts with privilege escalation (become).
For example, when using sudo operations you must first disable 'requiretty' in the sudoers file for the target hosts,
which is why this feature is disabled by default.
env:
- name: ANSIBLE_PIPELINING
#- name: ANSIBLE_SSH_PIPELINING
ini:
- section: defaults
key: pipelining
#- section: ssh_connection
# key: pipelining
type: boolean
vars:
- name: ansible_pipelining
- name: ansible_ssh_pipelining
private_key_file:
description:
- Path to private key file to use for authentication
ini:
- section: defaults
key: private_key_file
env:
- name: ANSIBLE_PRIVATE_KEY_FILE
vars:
- name: ansible_private_key_file
- name: ansible_ssh_private_key_file
control_path:
description:
- This is the location to save ssh's ControlPath sockets, it uses ssh's variable substitution.
- Since 2.3, if null, ansible will generate a unique hash. Use `%(directory)s` to indicate where to use the control dir path setting.
env:
- name: ANSIBLE_SSH_CONTROL_PATH
ini:
- key: control_path
section: ssh_connection
control_path_dir:
default: ~/.ansible/cp
description:
- This sets the directory to use for ssh control path if the control path setting is null.
- Also, provides the `%(directory)s` variable for the control path setting.
env:
- name: ANSIBLE_SSH_CONTROL_PATH_DIR
ini:
- section: ssh_connection
key: control_path_dir
sftp_batch_mode:
default: 'yes'
description: 'TODO: write it'
env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
ini:
- {key: sftp_batch_mode, section: ssh_connection}
type: bool
scp_if_ssh:
default: smart
description:
- "Prefered method to use when transfering files over ssh"
- When set to smart, Ansible will try them until one succeeds or they all fail
- If set to True, it will force 'scp', if False it will use 'sftp'
env: [{name: ANSIBLE_SCP_IF_SSH}]
ini:
- {key: scp_if_ssh, section: ssh_connection}
use_tty:
version_added: '2.5'
default: 'yes'
description: add -tt to ssh commands to force tty allocation
env: [{name: ANSIBLE_SSH_USETTY}]
ini:
- {key: usetty, section: ssh_connection}
type: bool
yaml: {key: connection.usetty}
'''
class Connection(SSHConnection):
''' ssh based connections for powershell via packer'''
transport = 'packer'
has_pipelining = True
become_methods = []
allow_executable = False
module_implementation_preferences = ('.ps1', '')
def __init__(self, *args, **kwargs):
super(Connection, self).__init__(*args, **kwargs)

View File

@ -1,781 +0,0 @@
// +build !windows
package ansible
import (
"bytes"
"context"
"crypto/rand"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
confighelper "github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/stretchr/testify/assert"
)
// Be sure to remove the Ansible stub file in each test with:
// defer os.Remove(config["command"].(string))
func testConfig(t *testing.T) map[string]interface{} {
m := make(map[string]interface{})
wd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
ansible_stub := path.Join(wd, "packer-ansible-stub.sh")
err = ioutil.WriteFile(ansible_stub, []byte("#!/usr/bin/env bash\necho ansible 1.6.0"), 0777)
if err != nil {
t.Fatalf("err: %s", err)
}
m["command"] = ansible_stub
return m
}
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_Defaults(t *testing.T) {
var p Provisioner
config := testConfig(t)
defer os.Remove(config["command"].(string))
err := p.Prepare(config)
if err == nil {
t.Fatalf("should have error")
}
hostkey_file, err := ioutil.TempFile("", "hostkey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(hostkey_file.Name())
publickey_file, err := ioutil.TempFile("", "publickey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(publickey_file.Name())
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["ssh_host_key_file"] = hostkey_file.Name()
config["ssh_authorized_key_file"] = publickey_file.Name()
config["playbook_file"] = playbook_file.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
err = os.Unsetenv("USER")
if err != nil {
t.Fatalf("err: %s", err)
}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_PlaybookFile(t *testing.T) {
var p Provisioner
config := testConfig(t)
defer os.Remove(config["command"].(string))
hostkey_file, err := ioutil.TempFile("", "hostkey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(hostkey_file.Name())
publickey_file, err := ioutil.TempFile("", "publickey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(publickey_file.Name())
config["ssh_host_key_file"] = hostkey_file.Name()
config["ssh_authorized_key_file"] = publickey_file.Name()
err = p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["playbook_file"] = playbook_file.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_HostKeyFile(t *testing.T) {
var p Provisioner
config := testConfig(t)
defer os.Remove(config["command"].(string))
publickey_file, err := ioutil.TempFile("", "publickey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(publickey_file.Name())
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
filename := make([]byte, 10)
n, err := io.ReadFull(rand.Reader, filename)
if n != len(filename) || err != nil {
t.Fatal("could not create random file name")
}
config["ssh_host_key_file"] = fmt.Sprintf("%x", filename)
config["ssh_authorized_key_file"] = publickey_file.Name()
config["playbook_file"] = playbook_file.Name()
err = p.Prepare(config)
if err == nil {
t.Fatal("should error if ssh_host_key_file does not exist")
}
hostkey_file, err := ioutil.TempFile("", "hostkey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(hostkey_file.Name())
config["ssh_host_key_file"] = hostkey_file.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_AuthorizedKeyFile(t *testing.T) {
var p Provisioner
config := testConfig(t)
defer os.Remove(config["command"].(string))
hostkey_file, err := ioutil.TempFile("", "hostkey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(hostkey_file.Name())
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
filename := make([]byte, 10)
n, err := io.ReadFull(rand.Reader, filename)
if n != len(filename) || err != nil {
t.Fatal("could not create random file name")
}
config["ssh_host_key_file"] = hostkey_file.Name()
config["playbook_file"] = playbook_file.Name()
config["ssh_authorized_key_file"] = fmt.Sprintf("%x", filename)
err = p.Prepare(config)
if err == nil {
t.Errorf("should error if ssh_authorized_key_file does not exist")
}
publickey_file, err := ioutil.TempFile("", "publickey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(publickey_file.Name())
config["ssh_authorized_key_file"] = publickey_file.Name()
err = p.Prepare(config)
if err != nil {
t.Errorf("err: %s", err)
}
}
func TestProvisionerPrepare_LocalPort(t *testing.T) {
var p Provisioner
config := testConfig(t)
defer os.Remove(config["command"].(string))
hostkey_file, err := ioutil.TempFile("", "hostkey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(hostkey_file.Name())
publickey_file, err := ioutil.TempFile("", "publickey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(publickey_file.Name())
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["ssh_host_key_file"] = hostkey_file.Name()
config["ssh_authorized_key_file"] = publickey_file.Name()
config["playbook_file"] = playbook_file.Name()
config["local_port"] = 65537
err = p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["local_port"] = 22222
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_InventoryDirectory(t *testing.T) {
var p Provisioner
config := testConfig(t)
defer os.Remove(config["command"].(string))
hostkey_file, err := ioutil.TempFile("", "hostkey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(hostkey_file.Name())
publickey_file, err := ioutil.TempFile("", "publickey")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(publickey_file.Name())
playbook_file, err := ioutil.TempFile("", "playbook")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(playbook_file.Name())
config["ssh_host_key_file"] = hostkey_file.Name()
config["ssh_authorized_key_file"] = publickey_file.Name()
config["playbook_file"] = playbook_file.Name()
config["inventory_directory"] = "doesnotexist"
err = p.Prepare(config)
if err == nil {
t.Errorf("should error if inventory_directory does not exist")
}
inventoryDirectory, err := ioutil.TempDir("", "some_inventory_dir")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(inventoryDirectory)
config["inventory_directory"] = inventoryDirectory
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestAnsibleGetVersion(t *testing.T) {
if os.Getenv("PACKER_ACC") == "" {
t.Skip("This test is only run with PACKER_ACC=1 and it requires Ansible to be installed")
}
var p Provisioner
p.config.Command = "ansible-playbook"
err := p.getVersion()
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestAnsibleGetVersionError(t *testing.T) {
var p Provisioner
p.config.Command = "./test-fixtures/exit1"
err := p.getVersion()
if err == nil {
t.Fatal("Should return error")
}
if !strings.Contains(err.Error(), "./test-fixtures/exit1 --version") {
t.Fatal("Error message should include command name")
}
}
func TestAnsibleLongMessages(t *testing.T) {
if os.Getenv("PACKER_ACC") == "" {
t.Skip("This test is only run with PACKER_ACC=1 and it requires Ansible to be installed")
}
var p Provisioner
p.config.Command = "ansible-playbook"
p.config.PlaybookFile = "./test-fixtures/long-debug-message.yml"
err := p.Prepare()
if err != nil {
t.Fatalf("err: %s", err)
}
comm := &packersdk.MockCommunicator{}
ui := &packersdk.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
}
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestCreateInventoryFile(t *testing.T) {
type inventoryFileTestCases struct {
AnsibleVersion uint
User string
Groups []string
EmptyGroups []string
UseProxy confighelper.Trilean
GeneratedData map[string]interface{}
Expected string
}
TestCases := []inventoryFileTestCases{
{
AnsibleVersion: 1,
User: "testuser",
UseProxy: confighelper.TriFalse,
GeneratedData: basicGenData(nil),
Expected: "default ansible_ssh_host=123.45.67.89 ansible_ssh_user=testuser ansible_ssh_port=1234\n",
},
{
AnsibleVersion: 2,
User: "testuser",
UseProxy: confighelper.TriFalse,
GeneratedData: basicGenData(nil),
Expected: "default ansible_host=123.45.67.89 ansible_user=testuser ansible_port=1234\n",
},
{
AnsibleVersion: 1,
User: "testuser",
Groups: []string{"Group1", "Group2"},
UseProxy: confighelper.TriFalse,
GeneratedData: basicGenData(nil),
Expected: `default ansible_ssh_host=123.45.67.89 ansible_ssh_user=testuser ansible_ssh_port=1234
[Group1]
default ansible_ssh_host=123.45.67.89 ansible_ssh_user=testuser ansible_ssh_port=1234
[Group2]
default ansible_ssh_host=123.45.67.89 ansible_ssh_user=testuser ansible_ssh_port=1234
`,
},
{
AnsibleVersion: 1,
User: "testuser",
EmptyGroups: []string{"Group1", "Group2"},
UseProxy: confighelper.TriFalse,
GeneratedData: basicGenData(nil),
Expected: `default ansible_ssh_host=123.45.67.89 ansible_ssh_user=testuser ansible_ssh_port=1234
[Group1]
[Group2]
`,
},
{
AnsibleVersion: 1,
User: "testuser",
Groups: []string{"Group1", "Group2"},
EmptyGroups: []string{"Group3"},
UseProxy: confighelper.TriFalse,
GeneratedData: basicGenData(nil),
Expected: `default ansible_ssh_host=123.45.67.89 ansible_ssh_user=testuser ansible_ssh_port=1234
[Group1]
default ansible_ssh_host=123.45.67.89 ansible_ssh_user=testuser ansible_ssh_port=1234
[Group2]
default ansible_ssh_host=123.45.67.89 ansible_ssh_user=testuser ansible_ssh_port=1234
[Group3]
`,
},
{
AnsibleVersion: 2,
User: "testuser",
UseProxy: confighelper.TriFalse,
GeneratedData: basicGenData(map[string]interface{}{
"ConnType": "winrm",
"Password": "12345",
}),
Expected: "default ansible_host=123.45.67.89 ansible_connection=winrm ansible_winrm_transport=basic ansible_shell_type=powershell ansible_user=testuser ansible_port=1234\n",
},
}
for _, tc := range TestCases {
var p Provisioner
p.Prepare(testConfig(t))
defer os.Remove(p.config.Command)
p.ansibleMajVersion = tc.AnsibleVersion
p.config.User = tc.User
p.config.Groups = tc.Groups
p.config.EmptyGroups = tc.EmptyGroups
p.config.UseProxy = tc.UseProxy
p.generatedData = tc.GeneratedData
err := p.createInventoryFile()
if err != nil {
t.Fatalf("error creating config using localhost and local port proxy")
}
if p.config.InventoryFile == "" {
t.Fatalf("No inventory file was created")
}
defer os.Remove(p.config.InventoryFile)
f, err := ioutil.ReadFile(p.config.InventoryFile)
if err != nil {
t.Fatalf("couldn't read created inventoryfile: %s", err)
}
expected := tc.Expected
if fmt.Sprintf("%s", f) != expected {
t.Fatalf("File didn't match expected:\n\n expected: \n%s\n; recieved: \n%s\n", expected, f)
}
}
}
func basicGenData(input map[string]interface{}) map[string]interface{} {
gd := map[string]interface{}{
"Host": "123.45.67.89",
"Port": int64(1234),
"ConnType": "ssh",
"SSHPrivateKeyFile": "",
"SSHPrivateKey": "asdf",
"SSHAgentAuth": false,
"User": "PartyPacker",
"PackerHTTPAddr": commonsteps.HttpAddrNotImplemented,
"PackerHTTPIP": commonsteps.HttpIPNotImplemented,
"PackerHTTPPort": commonsteps.HttpPortNotImplemented,
}
if input == nil {
return gd
}
for k, v := range input {
gd[k] = v
}
return gd
}
func TestCreateCmdArgs(t *testing.T) {
type testcase struct {
TestName string
PackerBuildName string
PackerBuilderType string
UseProxy confighelper.Trilean
generatedData map[string]interface{}
AnsibleSSHExtraArgs []string
ExtraArguments []string
AnsibleEnvVars []string
callArgs []string // httpAddr inventory playbook privKeyFile
ExpectedArgs []string
ExpectedEnvVars []string
}
TestCases := []testcase{
{
// SSH with private key and an extra argument.
TestName: "SSH with private key and an extra argument",
PackerBuildName: "packerparty",
generatedData: basicGenData(nil),
ExtraArguments: []string{"-e", "hello-world"},
AnsibleEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", "/path/to/privkey.pem"},
ExpectedArgs: []string{"-e", "packer_build_name=\"packerparty\"", "-e", "packer_builder_type=fakebuilder", "-e", "ansible_ssh_private_key_file=/path/to/privkey.pem", "--ssh-extra-args", "'-o IdentitiesOnly=yes'", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
},
{
// SSH with private key and an extra argument.
TestName: "SSH with private key and an extra argument and a ssh extra argument",
PackerBuildName: "packerparty",
generatedData: basicGenData(nil),
ExtraArguments: []string{"-e", "hello-world"},
AnsibleSSHExtraArgs: []string{"-o IdentitiesOnly=no"},
AnsibleEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", "/path/to/privkey.pem"},
ExpectedArgs: []string{"-e", "packer_build_name=\"packerparty\"", "-e", "packer_builder_type=fakebuilder", "--ssh-extra-args", "'-o IdentitiesOnly=no'", "-e", "ansible_ssh_private_key_file=/path/to/privkey.pem", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
},
{
TestName: "SSH with private key and an extra argument and UseProxy",
PackerBuildName: "packerparty",
UseProxy: confighelper.TriTrue,
generatedData: basicGenData(nil),
ExtraArguments: []string{"-e", "hello-world"},
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", "/path/to/privkey.pem"},
ExpectedArgs: []string{"-e", "packer_build_name=\"packerparty\"", "-e", "packer_builder_type=fakebuilder", "-e", "ansible_ssh_private_key_file=/path/to/privkey.pem", "--ssh-extra-args", "'-o IdentitiesOnly=yes'", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{},
},
{
// Winrm, but no_proxy is unset so we don't do anything with ansible_password.
TestName: "Winrm, but no_proxy is unset so we don't do anything with ansible_password",
PackerBuildName: "packerparty",
generatedData: basicGenData(map[string]interface{}{
"ConnType": "winrm",
}),
ExtraArguments: []string{"-e", "hello-world"},
AnsibleEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_build_name=\"packerparty\"", "-e", "packer_builder_type=fakebuilder", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
},
{
// HTTPAddr should be set. No env vars.
TestName: "HTTPAddr should be set. No env vars",
PackerBuildName: "packerparty",
ExtraArguments: []string{"-e", "hello-world"},
generatedData: basicGenData(map[string]interface{}{
"PackerHTTPAddr": "123.45.67.89",
}),
callArgs: []string{"123.45.67.89", "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_build_name=\"packerparty\"", "-e", "packer_builder_type=fakebuilder", "-e", "packer_http_addr=123.45.67.89", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{},
},
{
// Add ansible_password for proxyless winrm connection.
TestName: "Add ansible_password for proxyless winrm connection.",
UseProxy: confighelper.TriFalse,
generatedData: basicGenData(map[string]interface{}{
"ConnType": "winrm",
"Password": "ilovebananapancakes",
"PackerHTTPAddr": "123.45.67.89",
}),
AnsibleEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
callArgs: []string{"123.45.67.89", "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_builder_type=fakebuilder", "-e", "packer_http_addr=123.45.67.89", "-e", "ansible_password=ilovebananapancakes", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
},
{
// Neither special ssh stuff, nor special windows stuff. This is docker!
TestName: "Neither special ssh stuff, nor special windows stuff. This is docker!",
PackerBuildName: "packerparty",
generatedData: basicGenData(map[string]interface{}{
"ConnType": "docker",
}),
ExtraArguments: []string{"-e", "hello-world"},
AnsibleEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_build_name=\"packerparty\"", "-e", "packer_builder_type=fakebuilder", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
},
{
// Windows, no proxy, with extra vars.
TestName: "Windows, no proxy, with extra vars.",
UseProxy: confighelper.TriFalse,
generatedData: basicGenData(map[string]interface{}{
"ConnType": "winrm",
"Password": "ilovebananapancakes",
"PackerHTTPAddr": "123.45.67.89",
}),
ExtraArguments: []string{"-e", "hello-world"},
AnsibleEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
callArgs: []string{"123.45.67.89", "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_builder_type=fakebuilder", "-e", "packer_http_addr=123.45.67.89", "-e", "ansible_password=ilovebananapancakes", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
},
{
// SSH, use Password.
TestName: "SSH, use ansible_password.",
generatedData: basicGenData(map[string]interface{}{
"ConnType": "ssh",
"Password": "ilovebananapancakes",
"PackerHTTPAddr": "123.45.67.89",
}),
ExtraArguments: []string{"-e", "hello-world", "-e", "ansible_password=ilovebananapancakes"},
AnsibleEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
callArgs: []string{"123.45.67.89", "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_builder_type=fakebuilder", "-e", "packer_http_addr=123.45.67.89", "-e", "hello-world", "-e", "ansible_password=ilovebananapancakes", "-e", "ansible_host_key_checking=False", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas"},
},
{
// SSH, use Password .
TestName: "SSH, already in ENV ansible_host_key_checking.",
generatedData: basicGenData(map[string]interface{}{
"ConnType": "ssh",
"Password": "ilovebananapancakes",
"PackerHTTPAddr": "123.45.67.89",
}),
ExtraArguments: []string{"-e", "hello-world", "-e", "ansible_password=ilovebananapancakes"},
AnsibleEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas", "ANSIBLE_HOST_KEY_CHECKING=False"},
callArgs: []string{"123.45.67.89", "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_builder_type=fakebuilder", "-e", "packer_http_addr=123.45.67.89", "-e", "hello-world", "-e", "ansible_password=ilovebananapancakes", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{"ENV_1=pancakes", "ENV_2=bananas", "ANSIBLE_HOST_KEY_CHECKING=False"},
},
{
TestName: "Use PrivateKey",
PackerBuildName: "packerparty",
UseProxy: confighelper.TriTrue,
generatedData: basicGenData(nil),
ExtraArguments: []string{"-e", "hello-world"},
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", "/path/to/privkey.pem"},
ExpectedArgs: []string{"-e", "packer_build_name=\"packerparty\"", "-e", "packer_builder_type=fakebuilder", "-e", "ansible_ssh_private_key_file=/path/to/privkey.pem", "--ssh-extra-args", "'-o IdentitiesOnly=yes'", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{},
},
{
TestName: "Use PrivateKey and SSH Extra Arg",
PackerBuildName: "packerparty",
UseProxy: confighelper.TriTrue,
generatedData: basicGenData(nil),
AnsibleSSHExtraArgs: []string{"-o IdentitiesOnly=no"},
ExtraArguments: []string{"-e", "hello-world"},
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", "/path/to/privkey.pem"},
ExpectedArgs: []string{"-e", "packer_build_name=\"packerparty\"", "-e", "packer_builder_type=fakebuilder", "-e", "ansible_ssh_private_key_file=/path/to/privkey.pem", "--ssh-extra-args", "'-o IdentitiesOnly=no'", "-e", "hello-world", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{},
},
{
TestName: "Use SSH Agent",
UseProxy: confighelper.TriTrue,
generatedData: basicGenData(nil),
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_builder_type=fakebuilder", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{},
},
{
// No builder name. This shouldn't cause an error, it just shouldn't be set. HCL, yo.
TestName: "No builder name. This shouldn't cause an error, it just shouldn't be set. HCL, yo.",
generatedData: basicGenData(nil),
callArgs: []string{commonsteps.HttpAddrNotImplemented, "/var/inventory", "test-playbook.yml", ""},
ExpectedArgs: []string{"-e", "packer_builder_type=fakebuilder", "-i", "/var/inventory", "test-playbook.yml"},
ExpectedEnvVars: []string{},
},
}
for _, tc := range TestCases {
var p Provisioner
p.Prepare(testConfig(t))
defer os.Remove(p.config.Command)
p.config.UseProxy = tc.UseProxy
p.config.PackerBuilderType = "fakebuilder"
p.config.PackerBuildName = tc.PackerBuildName
p.generatedData = tc.generatedData
p.config.AnsibleSSHExtraArgs = tc.AnsibleSSHExtraArgs
p.config.ExtraArguments = tc.ExtraArguments
p.config.AnsibleEnvVars = tc.AnsibleEnvVars
args, envVars := p.createCmdArgs(tc.callArgs[0], tc.callArgs[1], tc.callArgs[2], tc.callArgs[3])
assert.ElementsMatch(t, args, tc.ExpectedArgs,
"TestName: %s\nArgs didn't match expected:\nexpected: \n%s\n; recieved: \n%s\n", tc.TestName, tc.ExpectedArgs, args)
assert.ElementsMatch(t, envVars, tc.ExpectedEnvVars,
"TestName: %s\nArgs didn't match expected:\n\nEnvVars didn't match expected:\n\n expected: \n%s\n; recieved: \n%s\n", tc.TestName, tc.ExpectedEnvVars, envVars)
assert.EqualValues(t, tc.callArgs[2], args[len(args)-1],
"TestName: %s\nPlayBook File Not Returned as last element: \nexpected: %s\nrecieved: %s\n", tc.TestName, tc.callArgs[2], args[len(args)-1])
}
}
func TestUseProxy(t *testing.T) {
type testcase struct {
UseProxy confighelper.Trilean
generatedData map[string]interface{}
expectedSetupAdapterCalled bool
explanation string
}
tcs := []testcase{
{
explanation: "use_proxy is true; we should set up adapter",
UseProxy: confighelper.TriTrue,
generatedData: basicGenData(nil),
expectedSetupAdapterCalled: true,
},
{
explanation: "use_proxy is false but no IP addr is available; we should set up adapter anyway.",
UseProxy: confighelper.TriFalse,
generatedData: basicGenData(map[string]interface{}{
"Host": "",
"Port": nil,
}),
expectedSetupAdapterCalled: true,
},
{
explanation: "use_proxy is false; we shouldn't set up adapter.",
UseProxy: confighelper.TriFalse,
generatedData: basicGenData(nil),
expectedSetupAdapterCalled: false,
},
{
explanation: "use_proxy is false but connType isn't ssh or winrm.",
UseProxy: confighelper.TriFalse,
generatedData: basicGenData(map[string]interface{}{
"ConnType": "docker",
}),
expectedSetupAdapterCalled: true,
},
{
explanation: "use_proxy is unset; we should default to setting up the adapter (for now).",
UseProxy: confighelper.TriUnset,
generatedData: basicGenData(nil),
expectedSetupAdapterCalled: true,
},
{
explanation: "use_proxy is false and connType is winRM. we should not set up the adapter.",
UseProxy: confighelper.TriFalse,
generatedData: basicGenData(map[string]interface{}{
"ConnType": "winrm",
}),
expectedSetupAdapterCalled: false,
},
{
explanation: "use_proxy is unset and connType is winRM. we should set up the adapter.",
UseProxy: confighelper.TriUnset,
generatedData: basicGenData(map[string]interface{}{
"ConnType": "winrm",
}),
expectedSetupAdapterCalled: true,
},
}
for _, tc := range tcs {
var p Provisioner
p.Prepare(testConfig(t))
p.config.UseProxy = tc.UseProxy
defer os.Remove(p.config.Command)
p.ansibleMajVersion = 1
var l provisionLogicTracker
l.setupAdapterCalled = false
p.setupAdapterFunc = l.setupAdapter
p.executeAnsibleFunc = l.executeAnsible
ctx := context.TODO()
comm := new(packersdk.MockCommunicator)
ui := &packersdk.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
}
p.Provision(ctx, ui, comm, tc.generatedData)
if l.setupAdapterCalled != tc.expectedSetupAdapterCalled {
t.Fatalf("%s", tc.explanation)
}
os.Remove(p.config.Command)
}
}

View File

@ -1,3 +0,0 @@
#!/bin/sh
exit 1

View File

@ -1,8 +0,0 @@
- name: Stub for Packer testing long Ansible messages
hosts: localhost
connection: local
tasks:
- name: Very long Ansible output (>65535 chars) (Issue https://github.com/hashicorp/packer/issues/3268)
debug:
msg: "{{ lipsum(n=300, html=false) }}"

View File

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

View File

@ -1,34 +0,0 @@
{
"variables": {},
"provisioners": [
{
"type": "shell-local",
"command": "echo 'TODO(bhcleek): write the public key to $HOME/.ssh/known_hosts and stop using ANSIBLE_HOST_KEY_CHECKING=False'"
}, {
"type": "ansible",
"playbook_file": "./playbook.yml",
"extra_arguments": [
"--private-key", "ansible-test-id"
],
"sftp_command": "/usr/lib/sftp-server -e -l INFO",
"use_sftp": true,
"ansible_env_vars": ["PACKER_ANSIBLE_TEST=1", "ANSIBLE_HOST_KEY_CHECKING=False"],
"groups": ["PACKER_TEST"],
"empty_groups": ["PACKER_EMPTY_GROUP"],
"host_alias": "packer-test",
"user": "packer",
"local_port": 2222,
"ssh_host_key_file": "ansible-server.key",
"ssh_authorized_key_file": "ansible-test-id.pub"
}
],
"builders": [{
"type": "googlecompute",
"account_file": "{{user `account_file`}}",
"project_id": "{{user `project_id`}}",
"image_name": "packerbats-alloptions-{{timestamp}}",
"source_image": "debian-8-jessie-v20161027",
"zone": "us-central1-a",
"ssh_username": "debian"
}]
}

View File

@ -1,16 +0,0 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.connection.ssh import Connection as SSHConnection
class Connection(SSHConnection):
''' ssh based connections for powershell via packer'''
transport = 'packer'
has_pipelining = True
become_methods = []
allow_executable = False
module_implementation_preferences = ('.ps1', '')
def __init__(self, *args, **kwargs):
super(Connection, self).__init__(*args, **kwargs)

View File

@ -1 +0,0 @@
this file's parent directory should not be transferred to the node.

View File

@ -1 +0,0 @@
This is a file

View File

@ -1 +0,0 @@
This file and its parent directory should be transferred to the node.

View File

@ -1,26 +0,0 @@
{
"variables": {},
"provisioners": [
{
"type": "shell",
"inline": [
"apt-get update",
"apt-get -y install python"
]
}, {
"type": "ansible",
"playbook_file": "./playbook.yml",
"extra_arguments": [
],
"sftp_command": "/usr/bin/false",
"use_sftp": false
}
],
"builders": [
{
"type": "docker",
"image": "debian:jessie",
"discard": true
}
]
}

View File

@ -1,33 +0,0 @@
---
- hosts: default:packer-test
gather_facts: no
collections:
- artis3n.github
tasks:
- name: touch
raw: touch /tmp/ansible-raw-test
- name: raw test
raw: date
- name: command test
command: echo "the command module"
- name: prepare remote directory
command: mkdir /tmp/remote-dir
args:
creates: /tmp/remote-dir
- name: transfer file.txt
copy: src=dir/file.txt dest=/tmp/remote-dir/file.txt
- name: fetch file.text
fetch: src=/tmp/remote-dir/file.txt dest=fetched-dir validate=yes fail_on_missing=yes
- name: copy contents of directory
copy: src=dir/contents-only/ dest=/tmp/remote-dir
- name: fetch contents of directory
fetch: src=/tmp/remote-dir/file.txt dest="fetched-dir/{{ inventory_hostname }}/tmp/remote-dir/contents-only/" flat=yes validate=yes fail_on_missing=yes
- name: copy directory recursively
copy: src=dir/subdir dest=/tmp/remote-dir
- name: fetch recursively copied directory
fetch: src=/tmp/remote-dir/subdir/file.txt dest=fetched-dir validate=yes fail_on_missing=yes
- copy: src=largish-file.txt dest=/tmp/largish-file.txt
- name: test collection - fetch latest repo version
set_fact:
# Ansible will fail if collection is not installed
packer_version: "{{ lookup('artis3n.github.latest_release', 'hashicorp/packer' }}"

View File

@ -1,21 +0,0 @@
{
"variables": {},
"provisioners": [
{
"type": "ansible",
"playbook_file": "./galaxy-playbook.yml",
"galaxy_file": "./requirements.yml"
}
],
"builders": [
{
"type": "googlecompute",
"account_file": "{{user `account_file`}}",
"project_id": "{{user `project_id`}}",
"image_name": "packerbats-galaxy-{{timestamp}}",
"source_image": "debian-8-jessie-v20161027",
"zone": "us-central1-a",
"ssh_username": "debian"
}
]
}

View File

@ -1,20 +0,0 @@
{
"variables": {},
"provisioners": [
{
"type": "ansible",
"playbook_file": "./playbook.yml"
}
],
"builders": [
{
"type": "googlecompute",
"account_file": "{{user `account_file`}}",
"project_id": "{{user `project_id`}}",
"image_name": "packerbats-minimal-{{timestamp}}",
"source_image": "debian-8-jessie-v20161027",
"zone": "us-central1-a",
"ssh_username": "debian"
}
]
}

View File

@ -1,27 +0,0 @@
---
- hosts: default:packer-test
gather_facts: no
tasks:
- name: touch
raw: touch /tmp/ansible-raw-test
- name: raw test
raw: date
- name: command test
command: echo "the command module"
- name: prepare remote directory
command: mkdir /tmp/remote-dir
args:
creates: /tmp/remote-dir
- name: transfer file.txt
copy: src=dir/file.txt dest=/tmp/remote-dir/file.txt
- name: fetch file.text
fetch: src=/tmp/remote-dir/file.txt dest=fetched-dir validate=yes fail_on_missing=yes
- name: copy contents of directory
copy: src=dir/contents-only/ dest=/tmp/remote-dir
- name: fetch contents of directory
fetch: src=/tmp/remote-dir/file.txt dest="fetched-dir/{{ inventory_hostname }}/tmp/remote-dir/contents-only/" flat=yes validate=yes fail_on_missing=yes
- name: copy directory recursively
copy: src=dir/subdir dest=/tmp/remote-dir
- name: fetch recursively copied directory
fetch: src=/tmp/remote-dir/subdir/file.txt dest=fetched-dir validate=yes fail_on_missing=yes
- copy: src=largish-file.txt dest=/tmp/largish-file.txt

View File

@ -1,2 +0,0 @@
collections:
- name: artis3n.github

View File

@ -1,25 +0,0 @@
{
"variables": {},
"provisioners": [
{
"type": "ansible",
"playbook_file": "./playbook.yml",
"extra_arguments": [
],
"sftp_command": "/usr/bin/false",
"use_sftp": false
}
],
"builders": [
{
"type": "googlecompute",
"account_file": "{{user `account_file`}}",
"project_id": "{{user `project_id`}}",
"image_name": "packerbats-scp-to-sftp-{{timestamp}}",
"source_image": "debian-8-jessie-v20161027",
"zone": "us-central1-a",
"ssh_username": "debian",
"ssh_file_transfer_method": "sftp"
}
]
}

View File

@ -1,24 +0,0 @@
{
"variables": {},
"provisioners": [
{
"type": "ansible",
"playbook_file": "./playbook.yml",
"extra_arguments": [
],
"sftp_command": "/usr/bin/false",
"use_sftp": false
}
],
"builders": [
{
"type": "googlecompute",
"account_file": "{{user `account_file`}}",
"project_id": "{{user `project_id`}}",
"image_name": "packerbats-scp-{{timestamp}}",
"source_image": "debian-8-jessie-v20161027",
"zone": "us-central1-a",
"ssh_username": "debian"
}
]
}

View File

@ -1,22 +0,0 @@
{
"variables": {},
"provisioners": [
{
"type": "ansible",
"playbook_file": "./playbook.yml",
"sftp_command": "/usr/lib/sftp-server -e -l INFO",
"use_sftp": true
}
],
"builders": [
{
"type": "googlecompute",
"account_file": "{{user `account_file`}}",
"project_id": "{{user `project_id`}}",
"image_name": "packerbats-sftp-{{timestamp}}",
"source_image": "debian-8-jessie-v20161027",
"zone": "us-central1-a",
"ssh_username": "debian"
}
]
}

View File

@ -1,17 +0,0 @@
---
- hosts: default:packer-test
gather_facts: no
tasks:
#- debug: msg="testing regular modules that function with Windows: raw, fetch, slurp, setup"
- name: raw test
raw: date /t
- debug: msg="testing windows modules"
#- win_file: path=tmp/remote-dir state=directory
#- name: win_shell test
#win_shell: date /t
- name: win_copy test
win_copy: src=dir/file.txt dest=file.txt
#- win_copy: src=dir/file.txt dest=/tmp/remote-dir/file.txt
#- fetch: src=/tmp/remote-dir/file.txt dest=fetched-dir validate=yes fail_on_missing=yes
#- win_copy: src=largish-file.txt dest=/tmp/largish-file.txt
- debug: msg="packer does not support downloading from windows"

View File

@ -1,31 +0,0 @@
{
"variables": {},
"provisioners": [
{
"type": "ansible",
"playbook_file": "./win-playbook.yml",
"extra_arguments": [
"--connection", "packer",
"--extra-vars", "ansible_shell_type=powershell ansible_shell_executable=None"
]
}
],
"builders": [
{
"type": "googlecompute",
"account_file": "{{user `account_file`}}",
"project_id": "{{user `project_id`}}",
"image_name": "packerbats-winrm-{{timestamp}}",
"source_image": "windows-server-2012-r2-dc-v20160916",
"communicator": "winrm",
"zone": "us-central1-a",
"disk_size": 50,
"winrm_username": "packer",
"winrm_use_ssl": true,
"winrm_insecure": true,
"metadata": {
"sysprep-specialize-script-cmd": "winrm set winrm/config/service/auth @{Basic=\"true\"}"
}
}
]
}

View File

@ -1,107 +0,0 @@
#!/usr/bin/env bats
#
# This tests the ansible provisioner on Google Cloud Provider (i.e.
# googlecompute). The teardown function will delete any images with the text
# "packerbats" within the name.
load test_helper
fixtures provisioner-ansible
# Required parameters
: ${GC_ACCOUNT_FILE:?}
: ${GC_PROJECT_ID:?}
command -v gcloud >/dev/null 2>&1 || {
echo "'gcloud' must be installed" >&2
exit 1
}
USER_VARS="${USER_VARS} -var account_file=${GC_ACCOUNT_FILE}"
USER_VARS="${USER_VARS} -var project_id=${GC_PROJECT_ID}"
# This tests if GCE has an image that contains the given parameter.
gc_has_image() {
gcloud compute --format='table[no-heading](name)' --project=${GC_PROJECT_ID} images list \
| grep $1 | wc -l
}
setup(){
rm -f $FIXTURE_ROOT/ansible-test-id
rm -f $FIXTURE_ROOT/ansible-server.key
ssh-keygen -N "" -f $FIXTURE_ROOT/ansible-test-id
ssh-keygen -N "" -f $FIXTURE_ROOT/ansible-server.key
}
teardown() {
gcloud compute --format='table[no-heading](name)' --project=${GC_PROJECT_ID} images list \
| grep packerbats \
| xargs -n1 gcloud compute --project=${GC_PROJECT_ID} images delete
rm -f $FIXTURE_ROOT/ansible-test-id
rm -f $FIXTURE_ROOT/ansible-test-id.pub
rm -f $FIXTURE_ROOT/ansible-server.key
rm -f $FIXTURE_ROOT/ansible-server.key.pub
rm -rf $FIXTURE_ROOT/fetched-dir
}
@test "ansible provisioner: build docker.json" {
cd $FIXTURE_ROOT
run packer build ${USER_VARS} $FIXTURE_ROOT/docker.json
[ "$status" -eq 0 ]
diff -r dir fetched-dir/default/tmp/remote-dir > /dev/null
}
@test "ansible provisioner: build minimal.json" {
cd $FIXTURE_ROOT
run packer build ${USER_VARS} $FIXTURE_ROOT/minimal.json
[ "$status" -eq 0 ]
[ "$(gc_has_image "packerbats-minimal")" -eq 1 ]
diff -r dir fetched-dir/default/tmp/remote-dir > /dev/null
}
@test "ansible provisioner: build all_options.json" {
cd $FIXTURE_ROOT
run packer build ${USER_VARS} $FIXTURE_ROOT/all_options.json
[ "$status" -eq 0 ]
[ "$(gc_has_image "packerbats-alloptions")" -eq 1 ]
diff -r dir fetched-dir/packer-test/tmp/remote-dir > /dev/null
}
@test "ansible provisioner: build galaxy.json" {
cd $FIXTURE_ROOT
run packer build ${USER_VARS} $FIXTURE_ROOT/galaxy.json
[ "$status" -eq 0 ]
[ "$(gc_has_image "packerbats-galaxy")" -eq 1 ]
diff -r dir fetched-dir/default/tmp/remote-dir > /dev/null
}
@test "ansible provisioner: build scp.json" {
cd $FIXTURE_ROOT
run packer build ${USER_VARS} $FIXTURE_ROOT/scp.json
[ "$status" -eq 0 ]
[ "$(gc_has_image "packerbats-scp")" -eq 1 ]
diff -r dir fetched-dir/default/tmp/remote-dir > /dev/null
}
@test "ansible provisioner: build scp-to-sftp.json" {
cd $FIXTURE_ROOT
run packer build ${USER_VARS} $FIXTURE_ROOT/scp-to-sftp.json
[ "$status" -eq 0 ]
[ "$(gc_has_image "packerbats-scp-to-sftp")" -eq 1 ]
diff -r dir fetched-dir/default/tmp/remote-dir > /dev/null
}
@test "ansible provisioner: build sftp.json" {
cd $FIXTURE_ROOT
run packer build ${USER_VARS} $FIXTURE_ROOT/sftp.json
[ "$status" -eq 0 ]
[ "$(gc_has_image "packerbats-sftp")" -eq 1 ]
diff -r dir fetched-dir/default/tmp/remote-dir > /dev/null
}
@test "ansible provisioner: build winrm.json" {
cd $FIXTURE_ROOT
run packer build ${USER_VARS} $FIXTURE_ROOT/winrm.json
[ "$status" -eq 0 ]
[ "$(gc_has_image "packerbats-winrm")" -eq 1 ]
echo "packer does not support downloading files from download, skipping verification"
#diff -r dir fetched-dir/default/tmp/remote-dir > /dev/null
}

View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@ -36,5 +36,6 @@ func (c *communicatorMock) DownloadDir(src, dst string, exclude []string) error
return nil
}
//nolint:unused
func (c *communicatorMock) verify() {
}

View File

@ -0,0 +1,31 @@
packer {
required_version = ">1.7.0"
required_plugins {
docker = {
source = "github.com/hashicorp/docker"
version = ">=0.0.7"
}
}
}
source "docker" "autogenerated_1" {
discard = true
image = "williamyeh/ansible:centos7"
}
build {
sources = ["source.docker.autogenerated_1"]
provisioner "ansible-local" {
playbook_dir = "test-fixtures"
playbook_files = ["test-fixtures/hello.yml", "test-fixtures/world.yml"]
}
provisioner "file" {
destination = "hello_world"
direction = "download"
source = "/tmp/hello_world"
}
}

View File

@ -0,0 +1,30 @@
packer {
required_version = ">1.7.0"
required_plugins {
docker = {
source = "github.com/hashicorp/docker"
version = ">=0.0.7"
}
}
}
source "docker" "autogenerated_1" {
discard = true
image = "williamyeh/ansible:centos7"
}
build {
sources = ["source.docker.autogenerated_1"]
provisioner "ansible-local" {
playbook_files = ["test-fixtures/hello.yml", "test-fixtures/world.yml"]
}
provisioner "file" {
destination = "hello_world"
direction = "download"
source = "/tmp/hello_world"
}
}

View File

@ -485,7 +485,9 @@ func (p *Provisioner) createInventoryFile() error {
}
w := bufio.NewWriter(tf)
w.WriteString(host)
if _, err := w.WriteString(host); err != nil {
log.Printf("[TRACE] error writing the generated inventory file: %s", err)
}
for _, group := range p.config.Groups {
fmt.Fprintf(w, "[%s]\n%s", group, host)
@ -681,7 +683,7 @@ func (p *Provisioner) executeGalaxy(ui packersdk.Ui, comm packersdk.Communicator
// Intended to be invoked from p.executeGalaxy depending on the Ansible Galaxy parameters passed to Packer
func (p *Provisioner) invokeGalaxyCommand(args []string, ui packersdk.Ui, comm packersdk.Communicator) error {
ui.Message(fmt.Sprintf("Executing Ansible Galaxy"))
ui.Message("Executing Ansible Galaxy")
cmd := exec.Command(p.config.GalaxyCommand, args...)
stdout, err := cmd.StdoutPipe()

View File

@ -0,0 +1,23 @@
packer {
required_version = ">1.7.0"
required_plugins {
docker = {
source = "github.com/hashicorp/docker"
version = ">=0.0.7"
}
}
}
source "docker" "autogenerated_1" {
discard = true
image = "williamyeh/ansible:centos7"
}
build {
sources = ["source.docker.autogenerated_1"]
provisioner "ansible" {
playbook_file = "test-fixtures/long-debug-message.yml"
}
}

4
vendor/modules.txt vendored
View File

@ -509,6 +509,10 @@ github.com/hashicorp/packer-plugin-amazon/builder/instance
github.com/hashicorp/packer-plugin-amazon/datasource/ami
github.com/hashicorp/packer-plugin-amazon/datasource/secretsmanager
github.com/hashicorp/packer-plugin-amazon/post-processor/import
# github.com/hashicorp/packer-plugin-ansible v0.0.2
## explicit
github.com/hashicorp/packer-plugin-ansible/provisioner/ansible
github.com/hashicorp/packer-plugin-ansible/provisioner/ansible-local
# github.com/hashicorp/packer-plugin-docker v0.0.7
## explicit
github.com/hashicorp/packer-plugin-docker/builder/docker

View File

@ -1,136 +0,0 @@
---
description: >
The ansible-local Packer provisioner will run ansible in ansible's "local"
mode on the remote/guest VM using Playbook and Role files that exist on the
guest VM. This means Ansible must be installed on the remote/guest VM.
Playbooks and Roles can be uploaded from your build machine (the one running
Packer) to the vm.
page_title: Ansible Local - Provisioners
---
# Ansible Local Provisioner
Type: `ansible-local`
The `ansible-local` Packer provisioner will execute `ansible` in Ansible's "local"
mode on the remote/guest VM using Playbook and Role files that exist on the
guest VM. This means Ansible must be installed on the remote/guest VM.
Playbooks and Roles can be uploaded from your build machine (the one running
Packer) to the vm. Ansible is then run on the guest machine in [local
mode](https://docs.ansible.com/ansible/latest/playbooks_delegation.html#local-playbooks)
via the `ansible-playbook` command.
-> **Note:** Ansible will _not_ be installed automatically by this
provisioner. This provisioner expects that Ansible is already installed on the
guest/remote machine. It is common practice to use the [shell
provisioner](/docs/provisioners/shell) before the Ansible provisioner to
do this.
## Basic Example
The example below is fully functional.
<Tabs>
<Tab heading="JSON">
```json
{
"builders": [
{
"type": "docker",
"image": "williamyeh/ansible:ubuntu14.04",
"export_path": "packer_example",
"run_command": ["-d", "-i", "-t", "--entrypoint=/bin/bash", "{{.Image}}"]
}
],
"variables": {
"topping": "mushroom"
},
"provisioners": [
{
"type": "ansible-local",
"playbook_file": "./playbook.yml",
"extra_arguments": [
"--extra-vars",
"\"pizza_toppings={{ user `topping`}}\""
]
}
]
}
```
</Tab>
<Tab heading="HCL2">
```hcl
variable "topping" {
type = string
default = "mushroom"
}
source "docker" "example" {
image = "williamyeh/ansible:ubuntu14.04"
export_path = "packer_example"
run_command = ["-d", "-i", "-t", "--entrypoint=/bin/bash", "{{.Image}}"]
}
build {
sources = [
"source.docker.example"
]
provisioner "ansible-local" {
playbook_file = "./playbook.yml"
extra_arguments = ["--extra-vars", "\"pizza_toppings=${var.topping}\""]
}
}
```
</Tab>
</Tabs>
where ./playbook.yml contains
```
---
- name: hello world
hosts: 127.0.0.1
connection: local
tasks:
- command: echo {{ pizza_toppings }}
- debug: msg="{{ pizza_toppings }}"
```
## Configuration Reference
The reference of available configuration options is listed below.
Note that one of `playbook_file` or `playbook_files` is required.
@include '/provisioner/ansible/Config-not-required.mdx'
@include 'provisioners/common-config.mdx'
## Default Extra Variables
In addition to being able to specify extra arguments using the
`extra_arguments` configuration, the provisioner automatically defines certain
commonly useful Ansible variables:
- `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 slightly when using a common playbook.
- `packer_builder_type` is the type of the builder that was used to create
the machine that the script is running on. This is useful if you want to
run only certain parts of the playbook on systems built with certain
builders.
- `packer_http_addr` If using a builder that provides an HTTP server for file
transfer (such as `hyperv`, `parallels`, `qemu`, `virtualbox`, and `vmware`), this
will be set to the address. You can use this address in your provisioner to
download large files over HTTP. This may be useful if you're experiencing
slower speeds using the default file provisioner. A file provisioner using
the `winrm` communicator may experience these types of difficulties.

View File

@ -1,805 +0,0 @@
---
description: |
The ansible Packer provisioner allows Ansible playbooks to be run to provision
the machine.
page_title: Ansible - Provisioners
---
# Ansible Provisioner
Type: `ansible`
The `ansible` Packer provisioner runs Ansible playbooks. It dynamically creates
an Ansible inventory file configured to use SSH, runs an SSH server, executes
`ansible-playbook`, and marshals Ansible plays through the SSH server to the
machine being provisioned by Packer.
-> **Note:** Any `remote_user` defined in tasks will be ignored. Packer
will always connect with the user given in the json config for this
provisioner.
-> **Note:** Options below that use the Packer template engine won't be able to
accept jinja2 `{{ function }}` macro syntax in a way that can be preserved to
the Ansible run. If you need to set variables using Ansible macros, you need to
do so inside your playbooks or inventory files.
Please see the [Debugging](#debugging), [Limitations](#limitations), or [Troubleshooting](#troubleshooting) if you are having trouble
getting started.
## Basic Example
This is a fully functional template that will provision an image on
DigitalOcean. Replace the mock `api_token` value with your own.
Example Packer template:
<Tabs>
<Tab heading="HCL2">
```hcl
source "digitalocean" "example"{
api_token = "6a561151587389c7cf8faa2d83e94150a4202da0e2bad34dd2bf236018ffaeeb"
image = "ubuntu-20-04-x64"
region = "sfo1"
}
build {
sources = [
"source.digitalocean.example"
]
provisioner "ansible" {
playbook_file = "./playbook.yml"
}
}
```
</Tab>
<Tab heading="JSON">
```json
{
"builders": [
{
"type": "digitalocean",
"api_token": "6a561151587389c7cf8faa2d83e94150a4202da0e2bad34dd2bf236018ffaeeb",
"image": "ubuntu-20-04-x64",
"region": "sfo1"
}
],
"provisioners": [
{
"type": "ansible",
"playbook_file": "./playbook.yml"
}
]
}
```
</Tab>
</Tabs>
Example playbook:
```yaml
---
# playbook.yml
- name: 'Provision Image'
hosts: default
become: true
tasks:
- name: install Apache
package:
name: 'httpd'
state: present
```
## Configuration Reference
Required Parameters:
@include 'provisioner/ansible/Config-required.mdx'
Optional Parameters:
@include '/provisioner/ansible/Config-not-required.mdx'
@include 'provisioners/common-config.mdx'
## Default Extra Variables
In addition to being able to specify extra arguments using the
`extra_arguments` configuration, the provisioner automatically defines certain
commonly useful Ansible variables:
- `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 slightly when using a common playbook.
- `packer_builder_type` is the type of the builder that was used to create
the machine that the script is running on. This is useful if you want to
run only certain parts of the playbook on systems built with certain
builders.
- `packer_http_addr` If using a builder that provides an HTTP server for file
transfer (such as `hyperv`, `parallels`, `qemu`, `virtualbox`, and `vmware`), this
will be set to the address. You can use this address in your provisioner to
download large files over HTTP. This may be useful if you're experiencing
slower speeds using the default file provisioner. A file provisioner using
the `winrm` communicator may experience these types of difficulties.
## Debugging
To debug underlying issues with Ansible, add `"-vvvv"` to `"extra_arguments"`
to enable verbose logging.
<Tabs>
<Tab heading="HCL2">
```hcl
extra_arguments = [ "-vvvv" ]
```
</Tab>
<Tab heading="JSON">
```json
"extra_arguments": [ "-vvvv" ]
```
</Tab>
</Tabs>
## Limitations
### Redhat / CentOS
Redhat / CentOS builds have been known to fail with the following error due to
`sftp_command`, which should be set to `/usr/libexec/openssh/sftp-server -e`:
```text
==> virtualbox-ovf: starting sftp subsystem
virtualbox-ovf: fatal: [default]: UNREACHABLE! => {"changed": false, "msg": "SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh", "unreachable": true}
```
### chroot communicator
Building within a chroot (e.g. `amazon-chroot`) requires changing the Ansible
connection to chroot and running Ansible as root/sudo.
<Tabs>
<Tab heading="HCL2">
```hcl
source "amazon-chroot" "example" {
mount_path = "/mnt/packer-amazon-chroot"
region = "us-east-1"
source_ami = "ami-123456"
}
build {
sources = [
"source.amazon-chroot.example"
]
provisioner "ansible" {
extra_arguments = [
"--connection=chroot",
"--inventory-file=/mnt/packer-amazon-chroot"
]
playbook_file = "main.yml"
}
}
```
</Tab>
<Tab heading="JSON">
```json
{
"builders": [
{
"type": "amazon-chroot",
"mount_path": "/mnt/packer-amazon-chroot",
"region": "us-east-1",
"source_ami": "ami-123456"
}
],
"provisioners": [
{
"type": "ansible",
"extra_arguments": [
"--connection=chroot",
"--inventory-file=/mnt/packer-amazon-chroot"
],
"playbook_file": "main.yml"
}
]
}
```
</Tab>
</Tabs>
### WinRM Communicator
There are two possible methods for using Ansible with the WinRM communicator.
Please note that if you're having trouble getting Ansible to connect, you may
want to take a look at the script that the Ansible project provides to help
configure remoting for Ansible:
https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1
#### Method 1 (recommended)
The recommended way to use the WinRM communicator is to set `"use_proxy": false`
and let the Ansible provisioner handle the rest for you. If you
are using WinRM with HTTPS, and you are using a self-signed certificate you
will also have to set `ansible_winrm_server_cert_validation=ignore` in your
extra_arguments.
Below is a fully functioning Ansible example using WinRM:
<Tabs>
<Tab heading="HCL2">
```hcl
data "amazon-ami" "ubuntu" {
access_key = var.aws_access_key
filters = {
name = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["099720109477"]
region = var.aws_region
secret_key = var.aws_secret_key
}
source "amazon-ebs" "example" {
region = "us-east-1"
instance_type = "t2.micro"
source_ami = data.amazon-ami.ubuntu.id
ami_name = "test-ansible-packer"
user_data_file = "windows_bootstrap.txt"
communicator = "winrm"
force_deregister = true
winrm_username = "Administrator"
winrm_insecure = true
winrm_use_ssl = true
}
build {
sources = [
"source.amazon-ebs.example",
]
provisioner "ansible" {
playbook_file = "./playbooks/playbook-windows.yml"
user = "Administrator"
use_proxy = false
extra_arguments = [
"-e",
"ansible_winrm_server_cert_validation=ignore"
]
}
}
```
</Tab>
<Tab heading="JSON">
```json
{
"builders": [
{
"type": "amazon-ebs",
"region": "us-east-1",
"instance_type": "t2.micro",
"source_ami_filter": {
"filters": {
"virtualization-type": "hvm",
"name": "*Windows_Server-2012*English-64Bit-Base*",
"root-device-type": "ebs"
},
"most_recent": true,
"owners": "amazon"
},
"ami_name": "test-ansible-packer",
"user_data_file": "windows_bootstrap.txt",
"communicator": "winrm",
"force_deregister": true,
"winrm_insecure": true,
"winrm_username": "Administrator",
"winrm_use_ssl": true
}
],
"provisioners": [
{
"type": "ansible",
"playbook_file": "./playbook.yml",
"user": "Administrator",
"use_proxy": false,
"extra_arguments": ["-e", "ansible_winrm_server_cert_validation=ignore"]
}
]
}
```
</Tab>
</Tabs>
Note that you do have to set the "Administrator" user, because otherwise Ansible
will default to using the user that is calling Packer, rather than the user
configured inside of the Packer communicator. For the contents of
windows_bootstrap.txt, see the WinRM docs for the amazon-ebs communicator.
When running from OSX, you may see an error like:
```text
amazon-ebs: objc[9752]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called.
amazon-ebs: objc[9752]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
amazon-ebs: ERROR! A worker was found in a dead state
```
If you see this, you may be able to work around the issue by telling Ansible to
explicitly not use any proxying; you can do this by setting the template option
<Tabs>
<Tab heading="HCL2">
```hcl
ansible_env_vars = ["no_proxy=\"*\""]
```
</Tab>
<Tab heading="JSON">
```json
"ansible_env_vars": ["no_proxy=\"*\""],
```
</Tab>
</Tabs>
in the above Ansible template.
#### Method 2 (Not recommended)
If you want to use the Packer SSH proxy, then you need a custom Ansible
connection plugin and a particular configuration. You need a directory named
`connection_plugins` next to the playbook which contains a file named
packer.py` which implements the connection plugin. On versions of Ansible
before 2.4.x, the following works as the connection plugin:
```python
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.connection.ssh import Connection as SSHConnection
class Connection(SSHConnection):
''' ssh based connections for powershell via packer'''
transport = 'packer'
has_pipelining = True
become_methods = []
allow_executable = False
module_implementation_preferences = ('.ps1', '')
def __init__(self, *args, **kwargs):
super(Connection, self).__init__(*args, **kwargs)
```
Newer versions of Ansible require all plugins to have a documentation string.
You can see if there is a plugin available for the version of Ansible you are
using
[here](https://github.com/hashicorp/packer/tree/master/provisioner/ansible/examples/connection-plugin).
To create the plugin yourself, you will need to copy all of the `options` from
the `DOCUMENTATION` string from the [ssh.py Ansible connection
plugin](https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/connection/ssh.py)
of the Ansible version you are using and add it to a packer.py file similar to
as follows
```python
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.connection.ssh import Connection as SSHConnection
DOCUMENTATION = '''
connection: packer
short_description: ssh based connections for powershell via packer
description:
- This connection plugin allows ansible to communicate to the target packer machines via ssh based connections for powershell.
author: Packer
version_added: na
options:
**** Copy ALL the options from
https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/connection/ssh.py
for the version of Ansible you are using ****
'''
class Connection(SSHConnection):
''' ssh based connections for powershell via packer'''
transport = 'packer'
has_pipelining = True
become_methods = []
allow_executable = False
module_implementation_preferences = ('.ps1', '')
def __init__(self, *args, **kwargs):
super(Connection, self).__init__(*args, **kwargs)
```
This template should build a Windows Server 2012 image on Google Cloud
Platform:
```json
{
"variables": {},
"provisioners": [
{
"type": "ansible",
"playbook_file": "./win-playbook.yml",
"extra_arguments": [
"--connection",
"packer",
"--extra-vars",
"\"ansible_shell_type=powershell ansible_shell_executable=None\""
]
}
],
"builders": [
{
"type": "googlecompute",
"account_file": "{{ user `account_file`}}",
"project_id": "{{user `project_id`}}",
"source_image": "windows-server-2012-r2-dc-v20160916",
"communicator": "winrm",
"zone": "us-central1-a",
"disk_size": 50,
"winrm_username": "packer",
"winrm_use_ssl": true,
"winrm_insecure": true,
"metadata": {
"sysprep-specialize-script-cmd": "winrm set winrm/config/service/auth @{Basic=\"true\"}"
}
}
]
}
```
-> **Warning:** Please note that if you're setting up WinRM for provisioning, you'll probably want to turn it off or restrict its permissions as part of a shutdown script at the end of Packer's provisioning process. For more details on the why/how, check out this useful blog post and the associated code:
https://cloudywindows.io/post/winrm-for-provisioning-close-the-door-on-the-way-out-eh/
### Post i/o timeout errors
If you see
`unknown error: Post http://<ip>:<port>/wsman:dial tcp <ip>:<port>: i/o timeout`
errors while provisioning a Windows machine, try setting Ansible to copy files
over [ssh instead of
sftp](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#envvar-ANSIBLE_SCP_IF_SSH).
### Too many SSH keys
SSH servers only allow you to attempt to authenticate a certain number of
times. All of your loaded keys will be tried before the dynamically generated
key. If you have too many SSH keys loaded in your `ssh-agent`, the Ansible
provisioner may fail authentication with a message similar to this:
```text
googlecompute: fatal: [default]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '[127.0.0.1]:62684' (RSA) to the list of known hosts.\r\nReceived disconnect from 127.0.0.1 port 62684:2: too many authentication failures\r\nAuthentication failed.\r\n", "unreachable": true}
```
To unload all keys from your `ssh-agent`, run:
```shell-session
$ ssh-add -D
```
### Become: yes
We recommend against running Packer as root; if you do then you won't be able
to successfully run your Ansible playbook as root; `become: yes` will fail.
### Using a wrapping script for your Ansible call
Sometimes, you may have extra setup that needs to be called as part of your
ansible run. The easiest way to do this is by writing a small bash script and
using that bash script in your "command" in place of the default
"ansible-playbook". For example, you may need to launch a Python virtualenv
before calling Ansible. To do this, you'd want to create a bash script like
```shell
#!/bin/bash
source /tmp/venv/bin/activate && ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 /tmp/venv/bin/ansible-playbook "$@"
```
The Ansible provisioner template remains very simple. For example:
<Tabs>
<Tab heading="HCL2">
```hcl
provisioner "ansible" {
command = "/Path/To/call_ansible.sh"
playbook_file = "./playbook.yml"
}
```
</Tab>
<Tab heading="JSON">
```json
{
"type": "ansible",
"command": "/Path/To/call_ansible.sh",
"playbook_file": "./playbook.yml"
}
```
</Tab>
</Tabs>
Note that we're calling ansible-playbook at the end of this command and passing
all command line arguments through into this call; this is necessary for
making sure that --extra-vars and other important Ansible arguments get set.
Note the quoting around the bash array, too; if you don't use quotes, any
arguments with spaces will not be read properly.
### Docker
When trying to use Ansible with Docker, it should "just work" but if it doesn't
you may need to tweak a few options.
- Change the ansible_connection from "ssh" to "docker"
- Set a Docker container name via the --name option.
On a CI server you probably want to overwrite ansible_host with a random name.
Example Packer template:
<Tabs>
<Tab heading="HCL2">
```hcl
variable "ansible_host" {
default = "default"
}
variable "ansible_connection" {
default = "docker"
}
source "docker" "example" {
image = "centos:7"
commit = true
run_command = [ "-d", "-i", "-t", "--name", var.ansible_host, "{{.Image}}", "/bin/bash" ]
}
build {
sources = [
"source.docker.example"
]
provisioner "ansible" {
groups = [ "webserver" ]
playbook_file = "./webserver.yml"
extra_arguments = [
"--extra-vars",
"ansible_host=${var.ansible_host} ansible_connection=${var.ansible_connection}"
]
}
}
```
</Tab>
<Tab heading="JSON">
```json
{
"variables": {
"ansible_host": "default",
"ansible_connection": "docker"
},
"builders": [
{
"type": "docker",
"image": "centos:7",
"commit": true,
"run_command": [
"-d",
"-i",
"-t",
"--name",
"{{user `ansible_host`}}",
"{{.Image}}",
"/bin/bash"
]
}
],
"provisioners": [
{
"type": "ansible",
"groups": ["webserver"],
"playbook_file": "./webserver.yml",
"extra_arguments": [
"--extra-vars",
"ansible_host={{user `ansible_host`}} ansible_connection={{user `ansible_connection`}}"
]
}
]
}
```
</Tab>
</Tabs>
Example playbook:
```yaml
- name: configure webserver
hosts: webserver
tasks:
- name: install Apache
yum:
name: httpd
```
### Amazon Session Manager
When trying to use Ansible with Amazon's Session Manager, you may run into an error where Ansible
is unable to connect to the remote Amazon instance if the local proxy adapter for Ansible [use_proxy](#use_proxy) is false.
The error may look something like the following:
```
amazon-ebs: fatal: [default]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 127.0.0.1 port 8362: Connection timed out", "unreachable": true}
```
The error is caused by a limitation on using Amazon's SSM default Port Forwarding session which only allows for one
remote connection on the forwarded port. Since Ansible's SSH communication is not using the local proxy adapter
it will try to make a new SSH connection to the same forwarded localhost port and fail.
In order to workaround this issue Ansible can be configured via a custom inventory file to use the AWS session-manager-plugin
directly to create a new session, separate from the one created by Packer, at runtime to connect and remotely provision the instance.
-> **Warning:** Please note that the default region configured for the `aws` cli must match the build region where the instance is being
provisioned otherwise you may run into a TargetNotConnected error. Users can use `AWS_DEFAULT_REGION` to temporarily override
their configured region.
<Tabs>
<Tab heading="HCL2">
```hcl
provisioner "ansible" {
use_proxy = false
playbook_file = "./playbooks/playbook_remote.yml"
ansible_env_vars = ["PACKER_BUILD_NAME={{ build_name }}"]
inventory_file_template = "{{ .HostAlias }} ansible_host={{ .ID }} ansible_user={{ .User }} ansible_ssh_common_args='-o StrictHostKeyChecking=no -o ProxyCommand=\"sh -c \\\"aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\\\"\"'\n"
}
```
</Tab>
<Tab heading="JSON">
```json
"provisioners": [
{
"type": "ansible",
"use_proxy": false,
"ansible_env_vars": ["PACKER_BUILD_NAME={{ build_name }}"],
"playbook_file": "./playbooks/playbook_remote.yml",
"inventory_file_template": "{{ .HostAlias }} ansible_host={{ .ID }} ansible_user={{ .User }} ansible_ssh_common_args='-o StrictHostKeyChecking=no -o ProxyCommand=\"sh -c \\\"aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\\\"\"'\n"
}
]
```
</Tab>
</Tabs>
Full Packer template example:
<Tabs>
<Tab heading="HCL2">
```hcl
variables {
instance_role = "SSMInstanceProfile"
}
source "amazon-ebs" "ansible-example" {
region = "us-east-1"
ami_name = "packer-ami-ansible"
instance_type = "t2.micro"
source_ami_filter {
filters = {
name = "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*"
virtualization-type = "hvm"
root-device-type = "ebs"
}
owners = [ "099720109477" ]
most_recent = true
}
communicator = "ssh"
ssh_username = "ubuntu"
ssh_interface = "session_manager"
iam_instance_profile = var.instance_role
}
build {
sources = ["source.amazon-ebs.ansible-example"]
provisioner "ansible" {
use_proxy = false
playbook_file = "./playbooks/playbook_remote.yml"
ansible_env_vars = ["PACKER_BUILD_NAME={{ build_name }}"]
inventory_file_template = "{{ .HostAlias }} ansible_host={{ .ID }} ansible_user={{ .User }} ansible_ssh_common_args='-o StrictHostKeyChecking=no -o ProxyCommand=\"sh -c \\\"aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\\\"\"'\n"
}
}
```
</Tab>
<Tab heading="JSON">
```json
{
"variables": {
"instance_role": "SSMInstanceProfile"
},
"builders": [
{
"type": "amazon-ebs",
"region": "us-east-1",
"ami_name": "packer-ami-ansible",
"instance_type": "t2.micro",
"source_ami_filter": {
"filters": {
"virtualization-type": "hvm",
"name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
"root-device-type": "ebs"
},
"owners": ["099720109477"],
"most_recent": true
},
"communicator": "ssh",
"ssh_username": "ubuntu",
"ssh_interface": "session_manager",
"iam_instance_profile": "{{user `instance_role`}}"
}
],
"provisioners": [
{
"type": "ansible",
"use_proxy": false,
"ansible_env_vars": ["PACKER_BUILD_NAME={{ build_name }}"],
"playbook_file": "./playbooks/playbook_remote.yml",
"inventory_file_template": "{{ .HostAlias }} ansible_host={{ .ID }} ansible_user={{ .User }} ansible_ssh_common_args='-o StrictHostKeyChecking=no -o ProxyCommand=\"sh -c \\\"aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\\\"\"'\n"
}
]
}
```
</Tab>
</Tabs>
### Troubleshooting
If you are using an Ansible version >= 2.8 and Packer hangs in the
"Gathering Facts" stage, this could be the result of a pipelineing issue with
the proxy adapter that Packer uses. Setting `use_proxy: false,` in your
Packer config should resolve the issue. In the future we will default to setting
this, so you won't have to but for now it is a manual change you must make.

View File

@ -1,124 +0,0 @@
<!-- Code generated from the comments of the Config struct in provisioner/ansible-local/provisioner.go; DO NOT EDIT MANUALLY -->
- `command` (string) - The command to invoke ansible. Defaults to
`ansible-playbook`. If you would like to provide a more complex command,
for example, something that sets up a virtual environment before calling
ansible, take a look at the ansible wrapper guide below for inspiration.
Please note that Packer expects Command to be a path to an executable.
Arbitrary bash scripting will not work and needs to go inside an
executable script.
- `extra_arguments` ([]string) - Extra arguments to pass to Ansible.
These arguments _will not_ be passed through a shell and arguments should
not be quoted. Usage example:
```json
"extra_arguments": [ "--extra-vars", "Region={{user `Region`}} Stage={{user `Stage`}}" ]
```
In certain scenarios where you want to pass ansible command line arguments
that include parameter and value (for example `--vault-password-file pwfile`),
from ansible documentation this is correct format but that is NOT accepted here.
Instead you need to do it like `--vault-password-file=pwfile`.
If you are running a Windows build on AWS, Azure, Google Compute, or OpenStack
and would like to access the auto-generated password that Packer uses to
connect to a Windows instance via WinRM, you can use the template variable
`{{.WinRMPassword}}` in this option. For example:
```json
"extra_arguments": [
"--extra-vars", "winrm_password={{ .WinRMPassword }}"
]
```
- `group_vars` (string) - A path to the directory containing ansible group
variables on your local system to be copied to the remote machine. By
default, this is empty.
- `host_vars` (string) - A path to the directory containing ansible host variables on your local
system to be copied to the remote machine. By default, this is empty.
- `playbook_dir` (string) - A path to the complete ansible directory structure on your local system
to be copied to the remote machine as the `staging_directory` before all
other files and directories.
- `playbook_file` (string) - The playbook file to be executed by ansible. This file must exist on your
local system and will be uploaded to the remote machine. This option is
exclusive with `playbook_files`.
- `playbook_files` ([]string) - The playbook files to be executed by ansible. These files must exist on
your local system. If the files don't exist in the `playbook_dir` or you
don't set `playbook_dir` they will be uploaded to the remote machine. This
option is exclusive with `playbook_file`.
- `playbook_paths` ([]string) - An array of directories of playbook files on your local system. These
will be uploaded to the remote machine under `staging_directory`/playbooks.
By default, this is empty.
- `role_paths` ([]string) - An array of paths to role directories on your local system. These will be
uploaded to the remote machine under `staging_directory`/roles. By default,
this is empty.
- `staging_directory` (string) - The directory where all the configuration of Ansible by Packer will be placed.
By default this is `/tmp/packer-provisioner-ansible-local/<uuid>`, where
`<uuid>` is replaced with a unique ID so that this provisioner can be run more
than once. If you'd like to know the location of the staging directory in
advance, you should set this to a known location. This directory doesn't need
to exist but must have proper permissions so that the SSH user that Packer uses
is able to create directories and write into this folder. If the permissions
are not correct, use a shell provisioner prior to this to configure it
properly.
- `clean_staging_directory` (bool) - If set to `true`, the content of the `staging_directory` will be removed after
executing ansible. By default this is set to `false`.
- `inventory_file` (string) - The inventory file to be used by ansible. This
file must exist on your local system and will be uploaded to the remote
machine.
When using an inventory file, it's also required to `--limit` the hosts to the
specified host you're building. The `--limit` argument can be provided in the
`extra_arguments` option.
An example inventory file may look like:
```text
[chi-dbservers]
db-01 ansible_connection=local
db-02 ansible_connection=local
[chi-appservers]
app-01 ansible_connection=local
app-02 ansible_connection=local
[chi:children]
chi-dbservers
chi-appservers
[dbservers:children]
chi-dbservers
[appservers:children]
chi-appservers
```
- `inventory_groups` ([]string) - `inventory_groups` (string) - A comma-separated list of groups to which
packer will assign the host `127.0.0.1`. A value of `my_group_1,my_group_2`
will generate an Ansible inventory like:
```text
[my_group_1]
127.0.0.1
[my_group_2]
127.0.0.1
```
- `galaxy_file` (string) - A requirements file which provides a way to
install roles or collections with the [ansible-galaxy
cli](https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#the-ansible-galaxy-command-line-tool)
on the local machine before executing `ansible-playbook`. By default, this is empty.
- `galaxy_command` (string) - The command to invoke ansible-galaxy. By default, this is
`ansible-galaxy`.
<!-- End of code generated from the comments of the Config struct in provisioner/ansible-local/provisioner.go; -->

View File

@ -1,167 +0,0 @@
<!-- Code generated from the comments of the Config struct in provisioner/ansible/provisioner.go; DO NOT EDIT MANUALLY -->
- `command` (string) - The command to invoke ansible. Defaults to
`ansible-playbook`. If you would like to provide a more complex command,
for example, something that sets up a virtual environment before calling
ansible, take a look at the ansible wrapper guide below for inspiration.
Please note that Packer expects Command to be a path to an executable.
Arbitrary bash scripting will not work and needs to go inside an
executable script.
- `extra_arguments` ([]string) - Extra arguments to pass to Ansible.
These arguments _will not_ be passed through a shell and arguments should
not be quoted. Usage example:
```json
"extra_arguments": [ "--extra-vars", "Region={{user `Region`}} Stage={{user `Stage`}}" ]
```
In certain scenarios where you want to pass ansible command line arguments
that include parameter and value (for example `--vault-password-file pwfile`),
from ansible documentation this is correct format but that is NOT accepted here.
Instead you need to do it like `--vault-password-file=pwfile`.
If you are running a Windows build on AWS, Azure, Google Compute, or OpenStack
and would like to access the auto-generated password that Packer uses to
connect to a Windows instance via WinRM, you can use the template variable
`{{.WinRMPassword}}` in this option. For example:
```json
"extra_arguments": [
"--extra-vars", "winrm_password={{ .WinRMPassword }}"
]
```
- `ansible_env_vars` ([]string) - Environment variables to set before
running Ansible. Usage example:
```json
"ansible_env_vars": [ "ANSIBLE_HOST_KEY_CHECKING=False", "ANSIBLE_SSH_ARGS='-o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s'", "ANSIBLE_NOCOLOR=True" ]
```
This is a [template engine](/docs/templates/legacy_json_templates/engine). Therefore, you
may use user variables and template functions in this field.
For example, if you are running a Windows build on AWS, Azure,
Google Compute, or OpenStack and would like to access the auto-generated
password that Packer uses to connect to a Windows instance via WinRM, you
can use the template variable `{{.WinRMPassword}}` in this option. Example:
```json
"ansible_env_vars": [ "WINRM_PASSWORD={{.WinRMPassword}}" ],
```
- `ansible_ssh_extra_args` ([]string) - Specifies --ssh-extra-args on command line defaults to -o IdentitiesOnly=yes
- `groups` ([]string) - The groups into which the Ansible host should
be placed. When unspecified, the host is not associated with any groups.
- `empty_groups` ([]string) - The groups which should be present in
inventory file but remain empty.
- `host_alias` (string) - The alias by which the Ansible host should be
known. Defaults to `default`. This setting is ignored when using a custom
inventory file.
- `user` (string) - The `ansible_user` to use. Defaults to the user running
packer, NOT the user set for your communicator. If you want to use the same
user as the communicator, you will need to manually set it again in this
field.
- `local_port` (int) - The port on which to attempt to listen for SSH
connections. This value is a starting point. The provisioner will attempt
listen for SSH connections on the first available of ten ports, starting at
`local_port`. A system-chosen port is used when `local_port` is missing or
empty.
- `ssh_host_key_file` (string) - The SSH key that will be used to run the SSH
server on the host machine to forward commands to the target machine.
Ansible connects to this server and will validate the identity of the
server using the system known_hosts. The default behavior is to generate
and use a onetime key. Host key checking is disabled via the
`ANSIBLE_HOST_KEY_CHECKING` environment variable if the key is generated.
- `ssh_authorized_key_file` (string) - The SSH public key of the Ansible
`ssh_user`. The default behavior is to generate and use a onetime key. If
this key is generated, the corresponding private key is passed to
`ansible-playbook` with the `-e ansible_ssh_private_key_file` option.
- `sftp_command` (string) - The command to run on the machine being
provisioned by Packer to handle the SFTP protocol that Ansible will use to
transfer files. The command should read and write on stdin and stdout,
respectively. Defaults to `/usr/lib/sftp-server -e`.
- `skip_version_check` (bool) - Check if ansible is installed prior to
running. Set this to `true`, for example, if you're going to install
ansible during the packer run.
- `use_sftp` (bool) - Use SFTP
- `inventory_directory` (string) - The directory in which to place the
temporary generated Ansible inventory file. By default, this is the
system-specific temporary file location. The fully-qualified name of this
temporary file will be passed to the `-i` argument of the `ansible` command
when this provisioner runs ansible. Specify this if you have an existing
inventory directory with `host_vars` `group_vars` that you would like to
use in the playbook that this provisioner will run.
- `inventory_file_template` (string) - This template represents the format for the lines added to the temporary
inventory file that Packer will create to run Ansible against your image.
The default for recent versions of Ansible is:
"{{ .HostAlias }} ansible_host={{ .Host }} ansible_user={{ .User }} ansible_port={{ .Port }}\n"
Available template engines are: This option is a template engine;
variables available to you include the examples in the default (Host,
HostAlias, User, Port) as well as any variables available to you via the
"build" template engine.
- `inventory_file` (string) - The inventory file to use during provisioning.
When unspecified, Packer will create a temporary inventory file and will
use the `host_alias`.
- `keep_inventory_file` (bool) - If `true`, the Ansible provisioner will
not delete the temporary inventory file it creates in order to connect to
the instance. This is useful if you are trying to debug your ansible run
and using "--on-error=ask" in order to leave your instance running while you
test your playbook. this option is not used if you set an `inventory_file`.
- `galaxy_file` (string) - A requirements file which provides a way to
install roles or collections with the [ansible-galaxy
cli](https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#the-ansible-galaxy-command-line-tool)
on the local machine before executing `ansible-playbook`. By default, this is empty.
- `galaxy_command` (string) - The command to invoke ansible-galaxy. By default, this is
`ansible-galaxy`.
- `galaxy_force_install` (bool) - Force overwriting an existing role.
Adds `--force` option to `ansible-galaxy` command. By default, this is
`false`.
- `roles_path` (string) - The path to the directory on your local system in which to
install the roles. Adds `--roles-path /path/to/your/roles` to
`ansible-galaxy` command. By default, this is empty, and thus `--roles-path`
option is not added to the command.
- `collections_path` (string) - The path to the directory on your local system in which to
install the collections. Adds `--collections-path /path/to/your/collections` to
`ansible-galaxy` command. By default, this is empty, and thus `--collections-path`
option is not added to the command.
- `use_proxy` (boolean) - When `true`, set up a localhost proxy adapter
so that Ansible has an IP address to connect to, even if your guest does not
have an IP address. For example, the adapter is necessary for Docker builds
to use the Ansible provisioner. If you set this option to `false`, but
Packer cannot find an IP address to connect Ansible to, it will
automatically set up the adapter anyway.
In order for Ansible to connect properly even when use_proxy is false, you
need to make sure that you are either providing a valid username and ssh key
to the ansible provisioner directly, or that the username and ssh key
being used by the ssh communicator will work for your needs. If you do not
provide a user to ansible, it will use the user associated with your
builder, not the user running Packer.
use_proxy=false is currently only supported for SSH and WinRM.
Currently, this defaults to `true` for all connection types. In the future,
this option will be changed to default to `false` for SSH and WinRM
connections where the provisioner has access to a host IP.
<!-- End of code generated from the comments of the Config struct in provisioner/ansible/provisioner.go; -->

View File

@ -1,5 +0,0 @@
<!-- Code generated from the comments of the Config struct in provisioner/ansible/provisioner.go; DO NOT EDIT MANUALLY -->
- `playbook_file` (string) - The playbook to be run by Ansible.
<!-- End of code generated from the comments of the Config struct in provisioner/ansible/provisioner.go; -->

View File

@ -945,14 +945,6 @@
"title": "Overview",
"path": "provisioners"
},
{
"title": "Ansible Local",
"path": "provisioners/ansible-local"
},
{
"title": "Ansible (Remote)",
"path": "provisioners/ansible"
},
{
"title": "Breakpoint",
"path": "provisioners/breakpoint"

View File

@ -16,5 +16,11 @@
"path": "vsphere",
"repo": "hashicorp/packer-plugin-vsphere",
"version": "latest"
},
{
"title": "Ansible",
"path": "ansible",
"repo": "hashicorp/packer-plugin-ansible",
"version": "latest"
}
]