30

Edit Apparently, the problem was that I should have done
su - postgres -c "commands, commands, commands",
that is, passed any commands to su, rather than attempting to list them below su, because those commands (below su) aren't affected by su. /Edit

Edit 2: See David Braun's answer for an even better solution: https://stackoverflow.com/a/22947716/694469


Can I not switch user (su - postgres) in a Vagrant bootstrap shell provisioning script? (Why not?)

I'm writing such a script. In that script, I do:

echo '===== Creating PostgreSQL databases and users'

su - postgres

psql -c "
  create user SomeUserName password '...';
  alter user ...;
  "

Here, psql should attempt to login as user postgres. What happens, however, is that su - postgres apparently fails, and the shell attempts to login as user root. (Apparently, root is the user that runs the Vagrant bootstrap shell script.)

So this error appears and the psql commands aren't invoked:

psql: FATAL: role "root" does not exist

Replacing su - postgres with sudo su - postgres has no effect (I think the script is already run as root.)

I added id (which prints the current user ID) before and after su - postgres, and id prints uid=0(root) gid=0(root) groups=0(root) both before and after su was called. So as far as I can tell, su - postgres is kind of ignored? And a certain exit, later on when I attempt to switch back to the root user, exits the bootstrap script completely :-(

However. After doing vagrant ssh, I'm able to sudo su - postgres just fine, and then start psql. But not from within the provisioning script.

(A workaround is to specify -h 127.0.0.1 --username postgres when I call psql (instead of switching user to postgres). And also enable PostgreSQL trust based authentication for VM local connections.)

Community
  • 1
  • 1
KajMagnus
  • 11,308
  • 15
  • 79
  • 127

4 Answers4

25
echo '===== Creating PostgreSQL databases and users'

su postgres << EOF
psql -c "
  create user SomeUserName password '...';
  alter user ...;
  "
EOF
David Braun
  • 5,573
  • 3
  • 36
  • 42
14

I just found this while looking for a way to run Vagrant provider scripts as non-root, and would like to add an explanation for the observed behaviour, which is quite fundamental Unix knowledge that would be useful for readers to know.

Running a program (such as /bin/sh, sudo or su) in a shell script cannot change anything in the context (working directory, running user etc) in which it runs, only set the context for the process it creates (and only change the running uid if running as root - uid 0). This is also why cd is a builtin command in shells. su is a setuid program (note the "s" when you try the command "ls -l which su") which means that it will run as the user owning the program (root) rather than as the user running it.

LHP
  • 180
  • 1
  • 10
10

LHP is right: it is not really a vagrant issue. I needed to do that some time ago and here is how I've solved it:

#!/bin/bash

case $(id -u) in
    0) 
         echo first: running as root
         echo doing the root tasks...
         sudo -u vagrant -i $0  # script calling itself as the vagrant user
         ;;
    *) 
         echo then: running as vagrant user
         echo doing the vagrant user's tasks
         ;;
esac

Maybe this could be useful for someone, but another obvious solution will be just run a second script using sudo.

FIXED: Thank you Macattack. I wrote directly in stackoverflow and it still not tested.

EDIT: The question has been edited and now its description, and most of the answers, are focusing on "How to create a postgres database with the right user" rather than how to switch user in a vagrant script.

olivecoder
  • 2,858
  • 23
  • 22
  • You're missing the `;;` at the end of each `case`. Other than that, great solution, was just what I was looking for. – Macattack Feb 25 '14 at 23:33
  • use `"` at line `echo doing the vagrant user's tasks`! The correct syntax would be `echo "doing the vagrant user's tasks"` – Doka Dec 30 '15 at 16:40
  • @Doka: despite that being completely irrelevant I don't see any documentation supporting your claim – olivecoder Feb 14 '18 at 08:57
3

I'm not sure if you can switch users in Vagrant shell scripts as that might require user input which you cannot give it when the script is running in the provisioner... but why not just specify the user with the psql command?

psql --username=postgres -c "..."
Matt Cooper
  • 9,962
  • 2
  • 31
  • 26
  • 1
    Re: "as that might require user input" — Okay, I didn't think about that. So if I had specified `--command COMMAND` as a `su` option, I might have gotten everything to work, eventually. I suppose that explains it all, and the problem was that I'm not too familiar with Bash, rather than anything related to Vagrant. (?) – KajMagnus May 27 '13 at 13:17
  • 1
    Thanks for the `--username` suggestion. (I did mention at the end of the question that: "A workaround is to specify `-h 127.0.0.1 --username postgres`".) – KajMagnus May 27 '13 at 13:19
  • Yeah sorry, I just saw you mentioned the username part as well... jumped into the answer too quickly hehe... – Matt Cooper May 27 '13 at 13:31