diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 1c226fa12..a9bcdd344 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -89,6 +89,8 @@ type Config struct { AnsibleEnvVars []string `mapstructure:"ansible_env_vars"` // The playbook to be run by Ansible. PlaybookFile string `mapstructure:"playbook_file" required:"true"` + // Specifies --ssh-extra-args on command line defaults to -o IdentitiesOnly=yes + AnsibleSSHExtraArgs []string `mapstructure:"ansible_ssh_extra_args"` // The groups into which the Ansible host should // be placed. When unspecified, the host is not associated with any groups. Groups []string `mapstructure:"groups"` @@ -701,7 +703,15 @@ func (p *Provisioner) createCmdArgs(httpAddr, inventory, playbook, privKeyFile s if p.generatedData["ConnType"] == "ssh" && len(privKeyFile) > 0 { // Add ssh extra args to set IdentitiesOnly - args = append(args, "--ssh-extra-args", "'-o IdentitiesOnly=yes'") + if len(p.config.AnsibleSSHExtraArgs) > 0 { + var sshExtraArgs string + for _, sshExtraArg := range p.config.AnsibleSSHExtraArgs { + sshExtraArgs = sshExtraArgs + sshExtraArg + } + args = append(args, "--ssh-extra-args", fmt.Sprintf("'%s'", sshExtraArgs)) + } else { + args = append(args, "--ssh-extra-args", "'-o IdentitiesOnly=yes'") + } } args = append(args, p.config.ExtraArguments...) diff --git a/provisioner/ansible/provisioner.hcl2spec.go b/provisioner/ansible/provisioner.hcl2spec.go index 746697c7d..91b7fce71 100644 --- a/provisioner/ansible/provisioner.hcl2spec.go +++ b/provisioner/ansible/provisioner.hcl2spec.go @@ -20,6 +20,7 @@ type FlatConfig struct { ExtraArguments []string `mapstructure:"extra_arguments" cty:"extra_arguments" hcl:"extra_arguments"` AnsibleEnvVars []string `mapstructure:"ansible_env_vars" cty:"ansible_env_vars" hcl:"ansible_env_vars"` PlaybookFile *string `mapstructure:"playbook_file" required:"true" cty:"playbook_file" hcl:"playbook_file"` + AnsibleSSHExtraArgs []string `mapstructure:"ansible_ssh_extra_args" cty:"ansible_ssh_extra_args" hcl:"ansible_ssh_extra_args"` Groups []string `mapstructure:"groups" cty:"groups" hcl:"groups"` EmptyGroups []string `mapstructure:"empty_groups" cty:"empty_groups" hcl:"empty_groups"` HostAlias *string `mapstructure:"host_alias" cty:"host_alias" hcl:"host_alias"` @@ -64,6 +65,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "extra_arguments": &hcldec.AttrSpec{Name: "extra_arguments", Type: cty.List(cty.String), Required: false}, "ansible_env_vars": &hcldec.AttrSpec{Name: "ansible_env_vars", Type: cty.List(cty.String), Required: false}, "playbook_file": &hcldec.AttrSpec{Name: "playbook_file", Type: cty.String, Required: false}, + "ansible_ssh_extra_args": &hcldec.AttrSpec{Name: "ansible_ssh_extra_args", Type: cty.List(cty.String), Required: false}, "groups": &hcldec.AttrSpec{Name: "groups", Type: cty.List(cty.String), Required: false}, "empty_groups": &hcldec.AttrSpec{Name: "empty_groups", Type: cty.List(cty.String), Required: false}, "host_alias": &hcldec.AttrSpec{Name: "host_alias", Type: cty.String, Required: false}, diff --git a/provisioner/ansible/provisioner_test.go b/provisioner/ansible/provisioner_test.go index cc2198de9..a98fae8a4 100644 --- a/provisioner/ansible/provisioner_test.go +++ b/provisioner/ansible/provisioner_test.go @@ -490,16 +490,17 @@ func basicGenData(input map[string]interface{}) map[string]interface{} { func TestCreateCmdArgs(t *testing.T) { type testcase struct { - TestName string - PackerBuildName string - PackerBuilderType string - UseProxy confighelper.Trilean - generatedData map[string]interface{} - ExtraArguments []string - AnsibleEnvVars []string - callArgs []string // httpAddr inventory playbook privKeyFile - ExpectedArgs []string - ExpectedEnvVars []string + 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{ { @@ -513,6 +514,18 @@ func TestCreateCmdArgs(t *testing.T) { 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{common.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", @@ -628,6 +641,17 @@ func TestCreateCmdArgs(t *testing.T) { 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{common.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, @@ -654,6 +678,7 @@ func TestCreateCmdArgs(t *testing.T) { 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 diff --git a/website/pages/partials/provisioner/ansible/Config-not-required.mdx b/website/pages/partials/provisioner/ansible/Config-not-required.mdx index aef7ac522..751649e99 100644 --- a/website/pages/partials/provisioner/ansible/Config-not-required.mdx +++ b/website/pages/partials/provisioner/ansible/Config-not-required.mdx @@ -46,6 +46,8 @@ "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.