Force SCP mode for Ansible file transfers with bastion.
Add ANSIBLE_SCP_IF_SSH=True environment variable when
local_build=true to force Ansible to use SCP instead of SFTP
for file transfers.
Root cause: Ansible defaults to SFTP for file transfers, which causes
failures when connecting through bastion/jump hosts that don't have
sftp-server installed. The existing --scp-extra-args '-O' flag was
being ignored because Ansible was using SFTP, not SCP.
Error without this fix:
failed to transfer file to /home/ubuntu/.ansible/tmp/...
bash: line 1: /usr/lib/sftp-server: No such file or directory
scp: Connection closed
Solution: Set ANSIBLE_SCP_IF_SSH=True to force Ansible to use SCP mode
when local_build=true (builds through bastion). Once SCP is being used,
the --scp-extra-args '-O' flag applies, forcing legacy SCP protocol
which works correctly through the bastion.
Changes:
- Added local.ansible_env_vars conditional in all templates
- When local_build=true: includes ANSIBLE_SCP_IF_SSH=True
- When local_build=false: standard Ansible environment (Jenkins builds)
- Updated provisioner blocks to use local.ansible_env_vars
Templates updated:
- builder.pkr.hcl
- docker.pkr.hcl
- devstack.pkr.hcl
- devstack-pre-pip-yoga.pkr.hcl
- windows-builder.pkr.hcl
Backward compatibility: Existing Jenkins builds continue to work
unchanged since local_build defaults to false. Only affects builds
with local_build=true (packer-build-action through bastion).
Change-Id: Iae0d6f2284fb2cdefa827c951bd11b51a5a56e19
Signed-off-by: Anil Belur <abelur@linuxfoundation.org>
--- /dev/null
+---
+fixes:
+ - |
+ Fix Ansible file transfer failures when building through bastion/jump hosts.
+
+ **Problem**: Packer builds with ``local_build=true`` (bastion/jump host mode)
+ were failing during Ansible provisioning with SFTP errors:
+
+ .. code-block:: text
+
+ failed to transfer file to /home/ubuntu/.ansible/tmp/...
+ bash: line 1: /usr/lib/sftp-server: No such file or directory
+ scp: Connection closed
+
+ **Root Cause**: Ansible defaults to SFTP for file transfers, not SCP.
+ The existing ``--scp-extra-args '-O'`` flag was being ignored because
+ Ansible was using SFTP, not SCP. Many cloud instances and bastion hosts
+ don't have ``sftp-server`` installed, causing transfer failures.
+
+ **Resolution**: Added ``ANSIBLE_SCP_IF_SSH=True`` environment variable
+ when ``local_build=true`` to force Ansible to use SCP mode instead of
+ SFTP. This makes the ``--scp-extra-args '-O'`` flag take effect, which
+ forces the legacy SCP protocol that works through bastion hosts.
+
+ **Implementation**: Created conditional ``ansible_env_vars`` in the
+ locals block of all templates:
+
+ - When ``local_build=true``: Sets ``ANSIBLE_SCP_IF_SSH=True``
+ - When ``local_build=false``: Standard Ansible environment (no change)
+
+ **Backward Compatibility**: This change only affects builds with
+ ``local_build=true`` (packer-build-action through bastion). Existing
+ Jenkins builds continue to work unchanged since ``local_build``
+ defaults to false.
+
+ Templates updated:
+
+ - templates/builder.pkr.hcl
+ - templates/docker.pkr.hcl
+ - templates/devstack.pkr.hcl
+ - templates/devstack-pre-pip-yoga.pkr.hcl
+ - templates/windows-builder.pkr.hcl
+
+ **Usage**: When building through a bastion/jump host, set:
+
+ .. code-block:: bash
+
+ packer build -var local_build=true ...
+
+ Reference: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ssh_connection.html
}
locals {
- # SSH arguments for local builds only
+ # SSH arguments - SCP compatibility for local builds
ssh_extra_args = var.local_build ? [
"--scp-extra-args", "'-O'",
"--ssh-extra-args",
] : [
"--ssh-extra-args", "-o IdentitiesOnly=yes -o HostKeyAlgorithms=+ssh-rsa"
]
+
+ # Ansible environment variables - force SCP for local builds to work with bastion
+ ansible_env_vars = var.local_build ? [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_SCP_IF_SSH=True",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ] : [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ]
}
source "docker" "builder" {
}
provisioner "ansible" {
- ansible_env_vars = [
- "ANSIBLE_NOCOWS=1",
- "ANSIBLE_PIPELINING=False",
- "ANSIBLE_HOST_KEY_CHECKING=False",
- "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
- "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
- "ANSIBLE_STDOUT_CALLBACK=debug"
- ]
+ ansible_env_vars = local.ansible_env_vars
command = "./common-packer/ansible-playbook.sh"
extra_arguments = local.ssh_extra_args
playbook_file = "provision/local-builder.yaml"
}
locals {
- # SSH arguments for local builds only
+ # SSH arguments - SCP compatibility for local builds
ssh_extra_args = var.local_build ? [
"--extra-vars", "os_branch=stable/yoga rdo_branch=yoga",
"--scp-extra-args", "'-O'",
"--extra-vars", "os_branch=stable/yoga rdo_branch=yoga",
"--ssh-extra-args", "-o IdentitiesOnly=yes -o HostKeyAlgorithms=+ssh-rsa"
]
+
+ # Ansible environment variables - force SCP for local builds to work with bastion
+ ansible_env_vars = var.local_build ? [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_SCP_IF_SSH=True",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ] : [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ]
}
source "docker" "devstack-pre-pip-yoga" {
}
provisioner "ansible" {
- ansible_env_vars = [
- "ANSIBLE_NOCOWS=1",
- "ANSIBLE_PIPELINING=False",
- "ANSIBLE_HOST_KEY_CHECKING=False",
- "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
- "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
- "ANSIBLE_STDOUT_CALLBACK=debug"
- ]
+ ansible_env_vars = local.ansible_env_vars
command = "./common-packer/ansible-playbook.sh"
extra_arguments = local.ssh_extra_args
playbook_file = "provision/devstack-pre-pip-centos.yaml"
}
locals {
- # SSH arguments for local builds only
+ # SSH arguments - SCP compatibility for local builds
ssh_extra_args = var.local_build ? [
"--extra-vars", "os_branch=stable/yoga rdo_branch=yoga",
"--scp-extra-args", "'-O'",
"--extra-vars", "os_branch=stable/yoga rdo_branch=yoga",
"--ssh-extra-args", "-o IdentitiesOnly=yes -o HostKeyAlgorithms=+ssh-rsa"
]
+
+ # Ansible environment variables - force SCP for local builds to work with bastion
+ ansible_env_vars = var.local_build ? [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_SCP_IF_SSH=True",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ] : [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ]
}
source "docker" "devstack" {
}
provisioner "ansible" {
- ansible_env_vars = [
- "ANSIBLE_NOCOWS=1",
- "ANSIBLE_PIPELINING=False",
- "ANSIBLE_HOST_KEY_CHECKING=False",
- "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
- "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
- "ANSIBLE_STDOUT_CALLBACK=debug"
- ]
+ ansible_env_vars = local.ansible_env_vars
command = "./common-packer/ansible-playbook.sh"
extra_arguments = local.ssh_extra_args
playbook_file = "provision/devstack-centos.yaml"
}
locals {
- # SSH arguments for local builds only
+ # SSH arguments - SCP compatibility for local builds
ssh_extra_args = var.local_build ? [
"--scp-extra-args", "'-O'",
"--ssh-extra-args",
] : [
"--ssh-extra-args", "-o IdentitiesOnly=yes -o HostKeyAlgorithms=+ssh-rsa"
]
+
+ # Ansible environment variables - force SCP for local builds to work with bastion
+ ansible_env_vars = var.local_build ? [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_SCP_IF_SSH=True",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ] : [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ]
}
source "docker" "docker" {
}
provisioner "ansible" {
- ansible_env_vars = [
- "ANSIBLE_NOCOWS=1",
- "ANSIBLE_PIPELINING=False",
- "ANSIBLE_HOST_KEY_CHECKING=False",
- "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
- "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
- "ANSIBLE_STDOUT_CALLBACK=debug"
- ]
+ ansible_env_vars = local.ansible_env_vars
command = "./common-packer/ansible-playbook.sh"
extra_arguments = local.ssh_extra_args
playbook_file = "provision/local-docker.yaml"
}
locals {
- # SSH arguments for local builds only
+ # SSH arguments - SCP compatibility for local builds
ssh_extra_args = var.local_build ? [
"--extra-vars", "ansible_shell_type=powershell",
"--extra-vars", "ansible_shell_executable=None",
"--extra-vars", "ansible_shell_executable=None",
"--ssh-extra-args", "-o IdentitiesOnly=yes -o HostKeyAlgorithms=+ssh-rsa"
]
+
+ # Ansible environment variables - force SCP for local builds to work with bastion
+ ansible_env_vars = var.local_build ? [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_SCP_IF_SSH=True",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ] : [
+ "ANSIBLE_NOCOWS=1",
+ "ANSIBLE_PIPELINING=False",
+ "ANSIBLE_HOST_KEY_CHECKING=False",
+ "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
+ "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
+ "ANSIBLE_STDOUT_CALLBACK=debug"
+ ]
}
source "openstack" "windows-builder" {
}
provisioner "ansible" {
- ansible_env_vars = [
- "ANSIBLE_NOCOWS=1",
- "ANSIBLE_PIPELINING=False",
- "ANSIBLE_HOST_KEY_CHECKING=False",
- "ANSIBLE_ROLES_PATH=${var.ansible_roles_path}",
- "ANSIBLE_CALLBACK_WHITELIST=profile_tasks",
- "ANSIBLE_STDOUT_CALLBACK=debug"
- ]
+ ansible_env_vars = local.ansible_env_vars
command = "./common-packer/ansible-playbook.sh"
extra_arguments = local.ssh_extra_args
playbook_file = "provision/local-windows-builder.yaml"