Installing Composer & the script Module
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With our project cloned, the next step is obvious: use Composer to install our dependencies!
Actually, we used the Composer module earlier from the command line. Google again
for "Ansible Modules" and find the "All Modules" page. I'll find the composer
module and open that in a new tab.
This module is really easy... except for one problem. Under Requirements, it says
that Composer already needs to be installed. We have not done that yet... and,
unfortunately, it can't be installed with apt-get.
Installing Composer Programmatically?
So how do you install it? Check out https://getcomposer.org and click "Download".
Normally, we just paste these lines into our terminal and celebrate! But... there's this problematic fine print at the bottom:
Do not redistribute the install code. It will change for every version of the install.
Huh. Composer includes a bit of built-in security: a sha hash to make sure that the installer hasn't been tampered with. If we tried to use these 4 commands in Ansible, it would work... for awhile. But next time the installer is updated, and that sha changed... it would stop working.
What to do? Check out that how to install Composer programmatically
link. Eureka: a shell script that will safely download the latest version of Composer.
The end result is a composer.phar file wherever we run this script from.
Installing Composer with script
Our mission is clear: somehow, execute this shell script via Ansible. But before we do that, near the top, add one new task: Install low-level utilities:
| - hosts: vb | |
| // ... lines 3 - 6 | |
| tasks: | |
| // ... lines 8 - 24 | |
| - name: Install low-level utilities | |
| // ... lines 26 - 109 |
Here, use the apt module and the with_items syntax to install zip and unzip:
| - hosts: vb | |
| // ... lines 3 - 6 | |
| tasks: | |
| // ... lines 8 - 24 | |
| - name: Install low-level utilities | |
| become: true | |
| apt: | |
| name: "{{ item }}" | |
| with_items: | |
| - zip | |
| - unzip | |
| // ... lines 32 - 109 |
Without these, Composer will run really slowly and you'll blame Jordi when you should be thanking him.
Now, back to our main job: how can we execute a script on a host? Why, with... the
script module of course!
Runs a local script on a remote node after transferring it
Neato! We just point it at a local script, and it takes care of the rest. Go copy
the script and, in our ansible directory, create a new scripts directory and
a new file called install_composer.sh. Paste the code there:
| EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig) | |
| php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" | |
| ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');") | |
| if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ] | |
| then | |
| >&2 echo 'ERROR: Invalid installer signature' | |
| rm composer-setup.php | |
| exit 1 | |
| fi | |
| php composer-setup.php --quiet | |
| RESULT=$? | |
| rm composer-setup.php | |
| exit $RESULT |
Back in the playbook, at the bottom, create a new task: Download Composer:
| - hosts: vb | |
| // ... lines 3 - 6 | |
| tasks: | |
| // ... lines 8 - 96 | |
| - name: Download Composer | |
| // ... lines 98 - 109 |
Use the script module. Then, the easiest way to use this is to literally put the script
filename on the same line as the module name: script: scripts/install_composer.sh:
| - hosts: vb | |
| // ... lines 3 - 6 | |
| tasks: | |
| // ... lines 8 - 96 | |
| - name: Download Composer | |
| script: scripts/install_composer.sh | |
| // ... lines 99 - 109 |
Actually, every module can be used with a one-line syntax like this... but since line breaks are pretty cheap these days, I usually organize things a bit more.
Thanks to this task, we'll have a new composer.phar file in our home directory,
which is where this task - well, all tasks - are running. But that's not enough:
we need to move this to /usr/local/bin/composer.
Moving Composer Globally
Create another task: Move Composer globally. This time, use become: true and use
the command module:
| - hosts: vb | |
| // ... lines 3 - 6 | |
| tasks: | |
| // ... lines 8 - 99 | |
| - name: Move Composer globally | |
| become: true | |
| // ... lines 102 - 109 |
In your browser, go find the command module. Like with script, command has
a short syntax. We'll say: command: mv composer.phar to /usr/local/bin/composer:
| - hosts: vb | |
| // ... lines 3 - 6 | |
| tasks: | |
| // ... lines 8 - 99 | |
| - name: Move Composer globally | |
| become: true | |
| command: mv composer.phar /usr/local/bin/composer | |
| // ... lines 103 - 109 |
If you're a little surprised that I'm using the command module instead of some
built-in file or move module... me too! In general, you should always look for
a built-in module first: they're always more powerful than using command. But sometimes,
like with moving files, command is the right tool for the job.
Add one more task to make sure the file is executable: "Set Permissions on Composer"
with become: true:
| - hosts: vb | |
| // ... lines 3 - 6 | |
| tasks: | |
| // ... lines 8 - 103 | |
| - name: Set permissions on Composer | |
| become: true | |
| // ... lines 106 - 109 |
Remember, Ansible is all about state. The job of the file module isn't really
to create files or symlinks. Instead, it's to make sure that they exist and
have the right permissions. In this case, we're going to take advantage of the
mode option to guarantee that the file is executable.
In the playbook, use the file module, set path to /usr/local/bin/composer
and mode to "a+x" to guarantee that all users have executable permission:
| - hosts: vb | |
| // ... lines 3 - 6 | |
| tasks: | |
| // ... lines 8 - 103 | |
| - name: Set permissions on Composer | |
| become: true | |
| file: | |
| path: /usr/local/bin/composer | |
| mode: "a+x" |
Oh, and make sure the file you created is install_composer.sh.
Time to give this a try. Find your main machine's terminal and run the playbook!
ansible-playbook ansible/playbook.yml -i ansible/hosts.ini
Back on the VM, I'm in my home directory. And right now, it's empty. But if you're
really fast, you can see the installation script doing its work. There it is:
composer-setup.phar, composer-temp.phar, composer.phar and then it's gone
once our task moves it. Yes!
And finally, we can type composer. Let's install some dependencies already!
Hi Guys, I'm on a trouble with installing composer on EC2. I'm writting here because it's error related on previous comments:
skipping: [192.168.33.10]<br />fatal: [18.203.185.87]: FAILED! => {"changed": true, "failed": true, "rc": 1, "stderr": "Shared connection to 18.203.185.87 closed.\r\n", "stdout": "/home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: 4: /home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: php: not found\r\n/home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: 5: /home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: php: not found\r\nERROR: Invalid installer signature\r\nrm: cannot remove 'composer-setup.php': No such file or directory\r\n", "stdout_lines": ["/home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: 4: /home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: php: not found", "/home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: 5: /home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: php: not found", "ERROR: Invalid installer signature", "rm: cannot remove 'composer-setup.php': No such file or directory"]}As you can see, on vagrant machine it is working fine, but on EC2 it is not correct signature... how I can fix that?
It is a 16.04 ubuntu ec2 instance.
Thanks!