Is there an idiomatic way in Ubuntu to run a script only the first time a machine is booted? (EC2).
Combining the first two answers
Assuming you name your script
/usr/local/bin/firstboot.sh put it at the end of
/etc/rc.local (this scripts runs on every boot) the scripts goes like this
#!/bin/bash FLAG="/var/log/firstboot.log" if [ ! -f $FLAG ]; then #Put here your initialization sentences echo "This is the first boot" #the next line creates an empty file so it won't run the next boot touch $FLAG else echo "Do nothing" fi
I'm surprised at the results I'm seeing for searching for a well-defined and supported Ubuntu "first boot" hook. Seems like the Red Hat / Fedora / CentOS crowd has had this nailed for over a decade. The closest Ubuntu equivalent seems to be oem-config-firstboot.
The idea of simply performing an
rm $0 will work. But, technically there are some interesting semantics involved. Unlike most other script interpreters under Unix a shell script is read and processed one line/statement at a time. If you unlink (
rm) the file out from under it then that the instance of the shell that's processing that script is now working with an anonymous file (any file that's open but unlinked).
Consider a file like this:
#!/bin/bash rm $0 echo "I've removed myself: $0" ls -l $0 cat <<COMMENTARY This is a test. I'm still here, because the "here" doc is being fed to 'cat' via the anonymous file through the open file descriptor. But I cannot be re-exec'd COMMENTARY exec $0
If you save that to something like
rmself.sh and (hard) link that to something like
tst then running
./tst should show something like this as output:
$ ./tst I've removed myself: ./tst ls: ./tst: No such file or directory This is a test. I'm still here, because the "here" doc is being fed to 'cat' via the anonymous file through the open file descriptor. But I cannot be re-exec'd ./tst: line 11: /home/jimd/bin/tst: No such file or directory ./tst: line 11: exec: /home/jimd/bin/tst: cannot execute: No such file or directory
Now there are some odd possible corner cases with regards to symlinks and cases where the script was invoked as a bare name (forcing the shell to search the
$PATH for the script.
But it seems that
bash (at least in version 3.2) prepends
$0 with the path if it searched the path and otherwise leaves $0 set to whatever relative or absolute path was used to invoke the script. It doesn't seem to do any normalization or resolution relative paths nor symlinks.
Probably the cleanest "firstboot" for Ubuntu would be to create a small package (.deb) containing a script to be placed in
/etc/init.d/firstboot and a post-install script that uses
update-rc.d to link that into runlevel 1 (
/etc/rc1.d) (using a command like:
update-rc.d firstboot defaults) ... and then have the last line perform a deactivation or delete using something like:
update-rc.d firstboot disable
Here's a link to the Debian update-rc.d HOWTO
The question was about running a script at first boot of EC2.
You can use
cloud-init for this purpose.
When launching a new EC2 instance you have an option to define
User data under
Advanced datails. If you place
cloud-init script there, it will be executed at first boot only.
For example you can place the following in
#cloud-config runcmd: - /usr/bin/command1.sh - /usr/bin/command2.sh
The output will be written to
Cloud-init can do much more than this. It is designed especially to perform early initialization of cloud instances. See the docs here: http://cloudinit.readthedocs.io/en/latest/index.html
In my case, it was building a custom system and I had to refuse to use cloud-init and placing script under
/etc/init.d/script didn't work so I used systemd.
[Unit] Description=One time boot script [Service] Type=simple ExecStart=/firstboot.sh [Install] WantedBy=multi-user.target
#!/bin/bash SOME COMMANDS YOU WANT TO EXECUTE systemctl disable firstbot.service rm -rf /etc/systemd/system/firstboot.service rm -f /firstboot.sh
(sudo) chmod +x /firstboot.sh (sudo) systemctl enable firstboot.service
This one works for me.