1 minute read

We have a complicated deployment on my current project which includes running several commands as a different user from the main deployment user. Normally, this wouldn’t be a problem since the sudo method provides an option called ‘as’:

task :run_command_as_another_user do
  sudo "whoami", :as => "another_user"
end

Unfortunately, we do not have sudo access to our servers. Instead of using sudo, we wrote a new method called with_user which will execute a block as a different user:

task :try_another_user do
  with_user("another_user", "another_password") do
    run "whoami"
  end
end

The with_user method will set the user and password back to the original values once the block is complete. For example:

task :whoami do
  set :user, 'original'
  set :password, 'original'

  run 'whoami'
  with_user("someone_else", 'password') do
    run "whoami"
  end
  run 'whoami'
end

The output of the previous task (stripped down) is:

 ** [out :: 127.0.0.1] original
 ** [out :: 127.0.0.1] someone_else
 ** [out :: 127.0.0.1] original

The implementation of with_user is:

def with_user(new_user, new_pass, &block)
  old_user, old_pass = user, password
  set :user, new_user
  set :password, new_pass
  close_sessions
  yield
  set :user, old_user
  set :password, old_pass
  close_sessions
end

def close_sessions
  sessions.values.each { |session| session.close }
  sessions.clear
end

It saves the old user and password and then sets the new values. Then, it disconnects from all of the servers, which forces a reconnect on the next command with the new user and password. When the block is complete, it resets the user and password and disconnects again.

The repeated reconnecting is not the most efficient solution, but it is simple and works for us.

Updated: