406 lines
12 KiB
Plaintext
406 lines
12 KiB
Plaintext
---
|
|
description: |
|
|
The shell Packer provisioner provisions machines built by Packer using shell
|
|
scripts. Shell provisioning is the easiest way to get software installed and
|
|
configured on a machine.
|
|
page_title: Shell - Provisioners
|
|
sidebar_title: Shell
|
|
---
|
|
|
|
# Shell Provisioner
|
|
|
|
Type: `shell`
|
|
|
|
The shell Packer provisioner provisions machines built by Packer using shell
|
|
scripts. Shell provisioning is the easiest way to get software installed and
|
|
configured on a machine.
|
|
|
|
-> **Building Windows images?** You probably want to use the
|
|
[PowerShell](/docs/provisioners/powershell) or [Windows
|
|
Shell](/docs/provisioners/windows-shell) provisioners.
|
|
|
|
## Basic Example
|
|
|
|
The example below is fully functional.
|
|
|
|
<Tabs>
|
|
<Tab heading="JSON">
|
|
|
|
```json
|
|
{
|
|
"type": "shell",
|
|
"inline": ["echo foo"]
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
<Tab heading="HCL2">
|
|
|
|
```hcl
|
|
provisioner "shell" {
|
|
inline = ["echo foo"]
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
## Configuration Reference
|
|
|
|
@include 'provisioners/shell-config.mdx'
|
|
|
|
- `environment_vars` (array of strings) - An array of key/value pairs to
|
|
inject prior to the execute_command. The format should be `key=value`.
|
|
Packer injects some environmental variables by default into the
|
|
environment, as well, which are covered in the section below.
|
|
|
|
- `env_var_format` (string) - When we parse the environment_vars that you
|
|
provide, this gives us a string template to use in order to make sure that
|
|
we are setting the environment vars correctly. By default it is `"%s='%s' "`.
|
|
When used in conjunction with `use_env_var_file` the default is `"export %s='%s'\n"`
|
|
|
|
- `use_env_var_file` (boolean) - If true, Packer will write your environment
|
|
variables to a tempfile and source them from that file, rather than
|
|
declaring them inline in our execute_command. The default
|
|
`execute_command` will be
|
|
`chmod +x {{.Path}}; . {{.EnvVarFile}} && {{.Path}}`. This option is
|
|
unnecessary for most cases, but if you have extra quoting in your custom
|
|
`execute_command`, then this may be required for proper script
|
|
execution. Default: false.
|
|
|
|
- `execute_command` (string) - The command to use to execute the script. By
|
|
default this is `chmod +x {{ .Path }}; {{ .Vars }} {{ .Path }}`, unless the
|
|
user has set `"use_env_var_file": true` -- in that case, the default
|
|
`execute_command` is `chmod +x {{.Path}}; . {{.EnvVarFile}} && {{.Path}}`.
|
|
This is a [template engine](/docs/templates/legacy_json_templates/engine). Therefore, you may
|
|
use user variables and template functions in this field. In addition, there
|
|
are three available extra variables:
|
|
|
|
- `Path` is the path to the script to run
|
|
- `Vars` is the list of `environment_vars`, if configured.
|
|
- `EnvVarFile` is the path to the file containing env vars, if
|
|
`use_env_var_file` is true.
|
|
|
|
- `expect_disconnect` (boolean) - Defaults to `false`. When `true`, allow the
|
|
server to disconnect from Packer without throwing an error. A disconnect
|
|
might happen if you restart the ssh server or reboot the host.
|
|
|
|
- `inline_shebang` (string) - The
|
|
[shebang](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) value to use
|
|
when running commands specified by `inline`. By default, this is
|
|
`/bin/sh -e`. If you're not using `inline`, then this configuration has no
|
|
effect. **Important:** If you customize this, be sure to include something
|
|
like the `-e` flag, otherwise individual steps failing won't fail the
|
|
provisioner.
|
|
|
|
- `remote_folder` (string) - The folder where the uploaded script will reside
|
|
on the machine. This defaults to '/tmp'.
|
|
|
|
- `remote_file` (string) - The filename the uploaded script will have on the
|
|
machine. This defaults to 'script_nnn.sh'.
|
|
|
|
- `remote_path` (string) - The full path to the uploaded script will have on
|
|
the machine. By default this is remote_folder/remote_file, if set this
|
|
option will override both remote_folder and remote_file.
|
|
|
|
- `skip_clean` (boolean) - If true, specifies that the helper scripts
|
|
uploaded to the system will not be removed by Packer. This defaults to
|
|
false (clean scripts from the system).
|
|
|
|
- `start_retry_timeout` (string) - The amount of time to attempt to _start_
|
|
the remote process. By default this is `5m` or 5 minutes. This setting
|
|
exists in order to deal with times when SSH may restart, such as a system
|
|
reboot. Set this to a higher value if reboots take a longer amount of time.
|
|
|
|
- `pause_after` (string) - Wait the amount of time after provisioning a shell
|
|
script, this pause be taken if all previous steps were successful.
|
|
|
|
@include 'provisioners/common-config.mdx'
|
|
|
|
## Execute Command Example
|
|
|
|
To many new users, the `execute_command` is puzzling. However, it provides an
|
|
important function: customization of how the command is executed. The most
|
|
common use case for this is dealing with **sudo password prompts**. You may
|
|
also need to customize this if you use a non-POSIX shell, such as `tcsh` on
|
|
FreeBSD.
|
|
|
|
### Sudo Example
|
|
|
|
Some operating systems default to a non-root user. For example if you login as
|
|
`ubuntu` and can sudo using the password `packer`, then you'll want to change
|
|
`execute_command` to be:
|
|
|
|
```text
|
|
"echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'"
|
|
```
|
|
|
|
The `-S` flag tells `sudo` to read the password from stdin, which in this case
|
|
is being piped in with the value of `packer`.
|
|
|
|
The above example won't work if your environment vars contain spaces or single
|
|
quotes; in these cases try removing the single quotes:
|
|
|
|
```text
|
|
"echo 'packer' | sudo -S env {{ .Vars }} {{ .Path }}"
|
|
```
|
|
|
|
By setting the `execute_command` to this, your script(s) can run with root
|
|
privileges without worrying about password prompts.
|
|
|
|
### FreeBSD Example
|
|
|
|
FreeBSD's default shell is `tcsh`, which deviates from POSIX semantics. In
|
|
order for packer to pass environment variables you will need to change the
|
|
`execute_command` to:
|
|
|
|
```text
|
|
chmod +x {{ .Path }}; env {{ .Vars }} {{ .Path }}
|
|
```
|
|
|
|
Note the addition of `env` before `{{ .Vars }}`.
|
|
|
|
## Default Environmental Variables
|
|
|
|
In addition to being able to specify custom environmental variables using the
|
|
`environment_vars` configuration, the provisioner automatically defines certain
|
|
commonly useful environmental variables:
|
|
|
|
- `PACKER_BUILD_NAME` is set to the [name of the
|
|
build](/docs/templates/legacy_json_templates/builders#named-builds) that Packer is running.
|
|
This is most useful when Packer is making multiple builds and you want to
|
|
distinguish them slightly from a common provisioning script.
|
|
|
|
- `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 script 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.
|
|
|
|
## Handling Reboots
|
|
|
|
Provisioning sometimes involves restarts, usually when updating the operating
|
|
system. Packer is able to tolerate restarts via the shell provisioner.
|
|
|
|
Packer handles this by retrying to start scripts for a period of time before
|
|
failing. This allows time for the machine to start up and be ready to run
|
|
scripts. The amount of time the provisioner will wait is configured using
|
|
`start_retry_timeout`, which defaults to a few minutes.
|
|
|
|
Sometimes, when executing a command like `reboot`, the shell script will return
|
|
and Packer will start executing the next one before SSH actually quits and the
|
|
machine restarts. For this, put use "pause_before" to make Packer wait before
|
|
executing the next script:
|
|
|
|
<Tabs>
|
|
<Tab heading="JSON">
|
|
|
|
```json
|
|
{
|
|
"type": "shell",
|
|
"script": "script.sh",
|
|
"pause_before": "10s",
|
|
"timeout": "10s"
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
<Tab heading="HCL2">
|
|
|
|
```hcl
|
|
provisioner "shell" {
|
|
script = "script.sh"
|
|
pause_before = "10s"
|
|
timeout = "10s"
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
Some OS configurations don't properly kill all network connections on reboot,
|
|
causing the provisioner to hang despite a reboot occurring. In this case, make
|
|
sure you shut down the network interfaces on reboot or in your shell script.
|
|
For example, on Gentoo:
|
|
|
|
```text
|
|
/etc/init.d/net.eth0 stop
|
|
```
|
|
|
|
## SSH Agent Forwarding
|
|
|
|
Some provisioning requires connecting to remote SSH servers from within the
|
|
packer instance. The below example is for pulling code from a private git
|
|
repository utilizing openssh on the client. Make sure you are running
|
|
`ssh-agent` and add your git repo ssh keys into it using
|
|
`ssh-add /path/to/key`. When the packer instance needs access to the ssh keys
|
|
the agent will forward the request back to your `ssh-agent`.
|
|
|
|
Note: when provisioning via git you should add the git server keys into the
|
|
`~/.ssh/known_hosts` file otherwise the git command could hang awaiting input.
|
|
This can be done by copying the file in via the [file
|
|
provisioner](/docs/provisioners/file) (more secure) or using `ssh-keyscan`
|
|
to populate the file (less secure). An example of the latter accessing github
|
|
would be:
|
|
|
|
<Tabs>
|
|
<Tab heading="JSON">
|
|
|
|
```json
|
|
{
|
|
"type": "shell",
|
|
"inline": [
|
|
"sudo apt-get install -y git",
|
|
"ssh-keyscan github.com >> ~/.ssh/known_hosts",
|
|
"git clone git@github.com:exampleorg/myprivaterepo.git"
|
|
]
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
<Tab heading="HCL2">
|
|
|
|
```hcl
|
|
provisioner "shell" {
|
|
inline = [
|
|
"sudo apt-get install -y git",
|
|
"ssh-keyscan github.com >> ~/.ssh/known_hosts",
|
|
"git clone git@github.com:exampleorg/myprivaterepo.git"
|
|
]
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
## Troubleshooting
|
|
|
|
_My shell script doesn't work correctly on Ubuntu_
|
|
|
|
- On Ubuntu, the `/bin/sh` shell is
|
|
[dash](https://en.wikipedia.org/wiki/Debian_Almquist_shell). If your script
|
|
has [bash](<https://en.wikipedia.org/wiki/Bash_(Unix_shell)>)-specific
|
|
commands in it, then put `#!/bin/bash -e` at the top of your script.
|
|
Differences between dash and bash can be found on the
|
|
[DashAsBinSh](https://wiki.ubuntu.com/DashAsBinSh) Ubuntu wiki page.
|
|
|
|
_My shell works when I login but fails with the shell provisioner_
|
|
|
|
- See the above tip. More than likely, your login shell is using `/bin/bash`
|
|
while the provisioner is using `/bin/sh`.
|
|
|
|
_My installs hang when using `apt-get` or `yum`_
|
|
|
|
- Make sure you add a `-y` to the command to prevent it from requiring user
|
|
input before proceeding.
|
|
|
|
_How do I tell what my shell script is doing?_
|
|
|
|
- Adding a `-x` flag to the shebang at the top of the script (`#!/bin/sh -x`)
|
|
will echo the script statements as it is executing.
|
|
|
|
_My builds don't always work the same_
|
|
|
|
- Some distributions start the SSH daemon before other core services which
|
|
can create race conditions. Your first provisioner can tell the machine to
|
|
wait until it completely boots.
|
|
|
|
<Tabs>
|
|
<Tab heading="JSON">
|
|
|
|
```json
|
|
{
|
|
"type": "shell",
|
|
"inline": ["sleep 10"]
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
<Tab heading="HCL2">
|
|
|
|
```hcl
|
|
provisioner "shell" {
|
|
inline = ["sleep 10"]
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
## Quoting Environment Variables
|
|
|
|
Packer manages quoting for you, so you should't have to worry about it. Below
|
|
is an example of packer template inputs and what you should expect to get out:
|
|
|
|
<Tabs>
|
|
<Tab heading="JSON">
|
|
|
|
```json
|
|
"provisioners": [
|
|
{
|
|
"type": "shell",
|
|
"environment_vars": ["FOO=foo",
|
|
"BAR=bar's",
|
|
"BAZ=baz=baz",
|
|
"QUX==qux",
|
|
"FOOBAR=foo bar",
|
|
"FOOBARBAZ='foo bar baz'",
|
|
"QUX2=\"qux\""],
|
|
"inline": ["echo \"FOO is $FOO\"",
|
|
"echo \"BAR is $BAR\"",
|
|
"echo \"BAZ is $BAZ\"",
|
|
"echo \"QUX is $QUX\"",
|
|
"echo \"FOOBAR is $FOOBAR\"",
|
|
"echo \"FOOBARBAZ is $FOOBARBAZ\"",
|
|
"echo \"QUX2 is $QUX2\""]
|
|
}
|
|
]
|
|
```
|
|
|
|
</Tab>
|
|
<Tab heading="HCL2">
|
|
|
|
```hcl
|
|
provisioner "shell" {
|
|
environment_vars = [
|
|
"FOO=foo",
|
|
"BAR=bar's",
|
|
"BAZ=baz=baz",
|
|
"QUX==qux",
|
|
"FOOBAR=foo bar",
|
|
"FOOBARBAZ='foo bar baz'",
|
|
"QUX2=\"qux\""
|
|
]
|
|
inline = [
|
|
"echo \"FOO is $FOO\"",
|
|
"echo \"BAR is $BAR\"",
|
|
"echo \"BAZ is $BAZ\"",
|
|
"echo \"QUX is $QUX\"",
|
|
"echo \"FOOBAR is $FOOBAR\"",
|
|
"echo \"FOOBARBAZ is $FOOBARBAZ\"",
|
|
"echo \"QUX2 is $QUX2\""
|
|
]
|
|
}
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
Output:
|
|
|
|
```text
|
|
docker: FOO is foo
|
|
docker: BAR is bar's
|
|
docker: BAZ is baz=baz
|
|
docker: QUX is =qux
|
|
docker: FOOBAR is foo bar
|
|
docker: FOOBARBAZ is 'foo bar baz'
|
|
docker: QUX2 is "qux"
|
|
```
|