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
.
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.
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
.
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!
Hey Alex,
Do you have PHP installed on your EC2? It sounds like you don't have "php" executable command. Could you manually ssh-ed to the EC2 and run "php --version". If you don't have a PHP yet - you need to install it first, otherwise "install_composer.sh" won't work I think.
Cheers!
Ups, that was! my fault! I was executing the command with -t deploy... so provisioning tasks were called anymore...Thanks!
Hey,
Once i run the playbook i get the following error
`fatal: [192.168.33.10]: FAILED! => {"changed": true, "cmd": ["mv", "composer.phar", "/user/local/bin/composer"], "delta": "0:00:00.005364", "end": "2018-12-27 08:50:50.294032", "msg": "non-zero return code", "rc": 1, "start": "2018-12-27 08:50:50.288668", "stderr": "mv: cannot move 'composer.phar' to '/user/local/bin/composer': No such file or directory", "stderr_lines": ["mv: cannot move 'composer.phar' to '/user/local/bin/composer': No such file or directory"], "stdout": "", "stdout_lines": []}
to retry, use: --limit @/Users/emin/Ansible/ansible/playbook.retry
`
I did it like you guys told me.
Cheers!
Hey Emin,
Looks like you don't have "composer.phar" file. Could you make sure you download the "composer.phar" to successfully. And make sure you name the downloaded file as "composer.phar".
Cheers!
UPDATE: i made a new task to create a directory so it has one but once i did that
i get the following error:
`
fatal: [192.168.33.10]: FAILED! => {"changed": false, "msg": "file (/usr/local/bin/composer) is absent, cannot continue", "path": "/usr/local/bin/composer", "state": "absent"}
to retry, use: --limit @/Users/emin/Ansible/ansible/playbook.retry`
i think it does this because the folder already exist.
Hey Emin,
Wait, what folder exactly did you create? I suppose the problem was that you didn't have composer.phar file, so the problem was not in folder.
Cheers!
Het victor,
When i checked my vagrant box i saw that i did not have the directory "/usr/local/bin/composer" and i did have the composer.phar installed but i created a task just to make a directory so it could find the directory.
Cheers!
Hey Emin,
But that's incorrect! The "/usr/local/bin/composer" is not a directory but file. Basically, with that command we rename "composer.phar" file to "composer" (i.e. without ".phar" extension) and move it to the "/usr/local/bin/" directory that I bet you have. So, the problem is that command was not able to find composer.phar file to do this operation. Now you need to remove that "/usr/local/bin/composer" directory you created and make sure that "composer.phar" file exists. The only folder you need to have is "/usr/local/bin/" only.
Cheers!
Hey Victor,
Yes it works now but to run composer in need to type /usr/local/bin/composer to use it the composer and in the video it's only composer and it works so that's where i get confused a bit.
Cheers!
Hey Emin,
Hm, it depends on your OS probably. The "/usr/local/bin/" folder should be in the PATH env variable, so you can it globally. In some systems it might be "/usr/bin/". You can try to move the file there, i.e. the path should be "/usr/bin/composer". Or you need to add "/usr/local/bin/" to the PATH env var. Actually, just run "echo $PATH" and you'll see what global executable paths you have. Btw, you said that executing "/usr/local/bin/composer" works for you, but what error do you see when try to execute just "composer"?
Cheers!
Hey Victor,
No command 'composer' found, did you mean:
Command 'compose' from package 'mime-support' (main)
composer: command not found
but in the direcotry "/user/local/bin/composer" i see the composer and composer.phar both in there. so i'm getting a bit confused now with this.
Cheers!
Hey Emin,
Wait wait, what path exactly? :) you wrote "/user/local/bin/composer" but it's incorrect, it should be "/usr/local/bin/composer" - notice that usr! And what about "echo $PATH" output??
Cheers!
Hey Victor,
OMG really it works now i hate it when i miss type something and don't know where the error is comming from
:(. but yeah thank you man i works perfectly now. I need a break now from coding for an hour.
Cheers man !
Hey Emin,
Lol, I mentioned that *exact* path a few times! :) No problem! I'm glad we found the problem and it works now ;)
Cheers!
Hi guys:
Is there way we can roll back the task. Say I don't need composer, and can I roll back to previous state. I tried to delete the task and run again, composer still there/:
Hey jian su!
There's no native rollback in Ansible. As you know, the purpose of the tasks/playbook is to put your server in a specific "state". If you don't have any tasks related to Composer suddenly, then your playbook basically doesn't have any opinion about the "state" of Composer. It does nothing :). If you did really care about the state or something, then you should actually add a task to make sure it's not installed. I'm not suggesting you do this - because you probably don't care enough - but that's the official answer :). For example, the apt
module allows you to set the state
to absent
... which you would use if you actually wanted to make sure that something was not installed.
And of course, you can always start from a fresh server. But any specific rollbacks are up to you (and kind of need to be - Ansible wouldn't natively know how to rollback the Composer installation anyways).
Cheers!
I'm hitting a weird snag with the script module. Note, I am on windows linux subsystem. When I run the Install Composer step outlined above, I'm getting this error:
TASK [Download Composer] ********************************************************fatal: [192.168.33.20]: FAILED! => {"changed": true, "msg": "non-zero return code", "rc": 127, "stderr": "Shared connection to 192.168.33.20 closed.\r\n", "stdout": "/bin/sh: 1: /home/vagrant/.ansible/tmp/ansible-tmp-1521308840.85-113836180865947/install_composer.sh: not found\r\n", "stdout_lines": ["/bin/sh: 1: /home/vagrant/.ansible/tmp/ansible-tmp-1521308840.85-113836180865947/install_composer.sh: not found"]}
to retry, use: --limit @/mnt/c/Users/Gary/Development/ansible/playbook.retry
Been searching everywhere, but I can't seem to get it to run. Note that .ansible/tmp directory IS being written to. Not sure why it would not include the sh file
Ansible 2.4.3, Vagrant 2.0.3.
UPDATE: Windows strikes again. I attempted to run the script manually in vagrant command line and got the error:
/bin/bash^M: bad interpreter. Apparently this can happen when you write shell files on Windows (new one to me)
I recreated the install_composer file in vim instead of the ide and worked just fine.
credit to this guy:
https://stackoverflow.com/q...
Hey gstanto ,
Glad you got it working! And thanks for sharing this info for our Windows friends ;) Btw, the answer seems pretty popular on GitHub, so I bet many users get this error.
Cheers!
Ran into the same problem.
I'd add that after converting the file to LF from CRLF (there's a button for that at the bottom right in phpStorm) you can create a .gitattributes
file to tell git to always store files in ansible/scripts/
using LF, otherwise you'll have this problem every time you check out your repository. See https://stackoverflow.com/a/63736688/1419007
Hi,
How do we add an user to an ubuntu server with Ansible, other than root user, which can run composer without issues? Composer warns about running "composer install" command as root.
My server is an ubuntu server with root as default and no other users created. The login is made through SSH as root.
L.E.: I fixed it by creating a new user on ubuntu server manually then modified the Ansible playbook to use that specific user.
Hey Liviu,
Good workaround, well done! In case you still want to automate this, let's see what options you have.
Remember, if you don't know a specific Ansible command that you can use for doing things you want - you can always use "command" or "shell" to execute *whatever* console commands you want manually. So, just google how to create users in Ubuntu servers with a CLI command and use it :)
Though, if you're interested in creating users with Ansible - Ansible really has a specific command for this called "user" :) See the docs about it: https://docs.ansible.com/an... - you can do a lot of things with it, look at examples to start.
I hope this helps!
Cheers!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.12
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.0
"snc/redis-bundle": "^2.0", // 2.0.0
"predis/predis": "^1.1", // v1.1.1
"composer/package-versions-deprecated": "^1.11" // 1.11.99
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.8
"symfony/phpunit-bridge": "^3.0", // v3.1.4
"doctrine/data-fixtures": "^1.1", // 1.3.3
"hautelook/alice-bundle": "^1.3" // v1.4.1
}
}
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!