How can you use a Chef recipe to set an environment variable?
I need to set an environment variable using a Chef recipe. Can you provide an example of how to accomplish this?
If you need an env var set strictly within the Chef process, you can use ENV['foo'] = 'bar' since it's a ruby process.
If you need to set one for an execute provider, Chef exposes an environment hash:
execute 'Bootstrap the database' do
cwd "#{app_dir}/current"
command "#{env_cmd} rake db:drop db:create db:schema:load RAILS_ENV=#{rails_env}"
environment 'HOME' => "/home/#{app_user}"
user app_user
action :run
not_if %[psql -U postgres -c "\\l" | grep #{db_name}]
end
If you're looking to set a persistent environment variable then you may want to have Chef edit /etc/profile.d/chef.sh, /etc/environment, a users' profile, etc.
If you want to set it on the system with Chef, checkout the magic_shell cookbook.
magic_shell_environment 'RAILS_ENV' do
value 'production'
end
If you want to set it at the system level in /etc/environment, you can do so directly per the following example without adding an additional recipe (this adds two env variables for Java):
sys_env_file = Chef::Util::FileEdit.new('/etc/environment')
{
'JAVA_HOME' => '/usr/lib/jvm/java-1.7.0-openjdk-amd64',
'LD_LIBRARY_PATH' => '/usr/lib/jvm/java-1.7.0-openjdk-amd64/lib'
}.each do |name, val|
sys_env_file.insert_line_if_no_match /^#{name}\=/, "#{name}=\"#{val}\""
sys_env_file.write_file
end
The way to do this is different between Windows and Linux. The easiest way would be:
Windows
Use the windows_env resource to create a System environment variable:
windows_env 'CHEF_LICENSE' do
value 'accept'
end
Linux
If you only need it for the cookbook run and its children, then use the Ruby ENV resource. This will NOT be permanent:
ENV['CHEF_LICENSE'] = 'accept'
If you need it to be permanent (and use bash):
Create a script in /etc/profile.d:
Create a template script (such as chef.sh.erb)
Fill out the template script:
#!/bin/bash
export CHEF_LICENSE='accept' # Needed for Chef Infra Client 15
Put the template resource in your recipe (You may want to set attributes and owner/group settings, I wanted to keep this example simple)
template '/etc/profile.d/chef.sh' do
source 'chef.sh.erb'
end
Here are some additional resources to read up on the different resources referenced here:
template resource
windows_env resource
For Linux, the best way to it is by using
ENV['Var'] = 'Value'
When you use this command, every sub process spun by the recipe will be using this ENV value. You can verify it using a bash resource and echoing the value of Var.
Related
In the Rails 4 In Action Book, it states that after doing some other setup: the final setup to boot up your rails app in production mode (with web brick) is to enter this command in terminal:
SECRET_KEY_BASE='rake secret' rails s -e production
I am trying to see where the environment variable of SECRET_KEY_BASE is stored.
Within /app/config/secrets.yml it says that the secret_key_base variable is an environment variable:
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
But I looked within .bashrc and .bash_profile and the variable SECRET_KEY_BASE is not there.
Ultimately I want to know: where is environment variable and its value? Is it stored somewhere in my rails app? I hope not for security purposes if I push this app to github. I assume it is not stored in the app but in my computer system somewhere. Within a different terminal window I do echo $SECRET_KEY_BASE but nothing gets returned.
Thanks in advance for helping me understand the missing pieces.
As a side note: I am aware of this question, but the question is not as detailed and there is no provided answer.
When you run this:
SECRET_KEY_BASE='rake secret' rails s -e production
you are not actually 'saving' the secret key for future you. You're defining it on a one-time basis. Whenever you run a Ruby command you can set temporary environment variables:
# from shell
KEY="VAL" OTHER_KEY=OTHER_VAL ruby my_command.rb
# from the ruby script
puts ENV["KEY"] # => "VAL"
puts ENV["OTHER_KEY"] # => "OTHER_VAL"
To persist the environment variables you have a couple options. You could hard code them in your source code, but this is probably not a good idea because if you push your code to Github, anyone will be able to see it. That's kind of the point of environment variables, anyway, that you can keep them system specific.
Option A
You can set them in .bashrc or .bash_profile
First get the result of rake secret (will be a random string) and set a shell variable:
KEY=`rake secret` # uses backticks to get command result
Then add a line in bashrc to export it:
echo -e "export SECRET_KEY_BASE=$KEY" >> ~/.bashrc
Option B
This is the one I'd recommend, you can use dotenv or figaro to manage your environment variables in an app-specific way, i.e. without cluttering up your bashrc.
For example with dotenv you'd create a .env file which contains:
# change this to the result of rake secret
SECRET_KEY_BASE=j3e2dd293d
This would be excluded from source control by adding it to gitignore.
Then in your ruby app you call
require 'dotenv'
Dotenv.load
and your ENV["SECRET_KEY_BASE"] will be set.
If you want you can make a .env.example file (included in source control) which shows which environment variables need to be defined. Then when the app is cloned you can run mv .env.example .env and customize .env.
I am running Ubuntu 14.04 LTS 64 bit Rails Application and I am unable to access my App environment variables.
In OpsWorks App panel, I set my environment variables, say:
MYKEY: 1234
Then I save and deploy my app again to make these visible.
In my Rails app, or the rails console I get nil:
$ bundle exec rails c production
>ENV["MYKEY"]
=> nil
I have tried restarting the server. I'm not sure what I am missing, I have been using environment variables in other services.
How can I trace where these should be set?
OpsWorks stores environmental variables in different places depending on what kind of app you're deploying. On Rails / Passenger they should be saved in the Apache config file #{your_app_name}.conf. (Source)
This means they aren't available in your normal shell environment.
I know the Node.js recipes stored everything in an /srv/www/#{app_name}/shared/app.env file... which is then sourced to pull in the environment to run the Node server. This implementation detail also meant you could write shell scripts that sourced that app.env file, then called some Node script or whatever.
Of course, Rails isn't Node. I have no idea if the environmental variables are also stored somewhere else or not: a quick look at the Rails recipes in the OpsWorks cookbooks didn't find anything obvious, but maybe I missed something.
Depending on the amount of modifications you have going on in your OpsWorks cookbook, you could create a deploy recipe that does something like this:
application_environment_file do
user deploy[:user]
group deploy[:group]
path ::File.join(deploy[:deploy_to], "shared")
environment_variables deploy[:environment_variables]
end
(maybe adjusting the path)
Then to run your console, when you're SSHed into the server, do something like
sudo source /srv/www/my_app_name/shared/app.env; bundle exec rails console -e production or whatever.
AWS OpsWorks console lets you declare environment variables but to let them be available for our Rails app we need to use a Chef cookbook recipe plus some precautions.
In a nutshell we use the config/secrets.yml file combined with config/application.yml file, Figaro gem and a Chef cookbook recipe.
The chef cookbook recipe read the variables defined in OpsWorks console and let them available to Rails app writing the config/application.yml file.
I have published a detailed guide to explain how exactly do it. Link here.
These are the core points that I covered:
Use config/secrets.yml file (added from Rails 4.1)
Use Figaro gem to load variables in the environment
Declare environment variables inside AWS OpsWorks Console
Use a custom Chef recipe to create a config/application.yml file that Figaro will use to let variables available
I (with some help from Bruno at the AWS PopUp Loft in NYC) added some custom Chef code inside the after_restart.rb deploy hook, simply add the folder "deploy" to your apps root directory and inside add "after_restart.eb." In it ....
Chef::Log.info("Running deploy/after_restart.rb")
contents = []
node[:deploy].each do |application, deploy|
deploy[:environment_variables].each do |key, value|
contents << "export #{key}=\"'#{value}'\""
end
end
Chef::Log.info("Adding the environment variables to /etc/profile.d/startup_env_config.sh")
bash "create_startup_env_config.sh" do
user "root"
cwd "/etc/profile.d"
code <<-EOH
echo \''#{contents.join(" ")}\'' > startup_env_config.sh
source startup_env_config.sh
cd #{release_path}
EOH
end
And that's it. If you update the environment variables inside the OpsWorks panel remember to restart your instances.
I'm using AWS Opsworks to host my Rails App (Ruby 2.0/Rails 3.2).
For assets compilation process, I am using AssetSync to upload the compiled assets automatically on S3. I used to store the credentials as environment variables.
Do you have any idea how can I do this with Chef/Opsworks?
Thanks.
I know this is an older post, but I'm posting this in case this helps someone else.
I found the easiest way actually was to use one of Chef's deploy hooks (http://docs.opscode.com/resource_deploy.html#deploy-phases).
Add a directory called 'deploy' at the Rails project root.
In it add a file called before_restart.rb, with the code:
Chef::Log.info("Running deploy/before_restart.rb")
# map the environment_variables node to ENV
node[:deploy].each do |application, deploy|
deploy[:environment_variables].each do |key, value|
Chef::Log.info("Setting ENV[#{key}] to #{value}")
ENV[key] = value
end
end
When you trigger the OpsWorks deploy, you should be able to see the ENV vars being set in the Rails App Server instance log.
I ended up using https://github.com/joeyAghion/opsworks_custom_env.
It works pretty well.
I used a slightly different approach, using OpsWorks hook to copy JSON to application.yml. you can read more about it here: http://zaman.io/how-to-import-aws-opsworks-json-into-rails-app/
Another option outside of environment variables is you can generate a file with the variables in it at deploy time.
For example, for a Rails app, the config/secrets.yml is a reasonable place to put these. I created a deploy/before_restart.rb deploy hook with the following content:
def create_secrets(secrets, release_path)
Chef::Log.info("Creating secrets")
file_path = ::File.join(release_path, 'config/secrets.yml')
::File.open(file_path, 'w') do |f|
f.write("production:\n")
secrets.each do |k,v|
f.write(" #{k}: #{v}\n")
end
end
end
node[:deploy].each do |application, deploy|
create_secrets(deploy[:secrets], release_path)
end
And then in your OpsWorks stack Custom JSON you can add your secrets:
"deploy": {
"super_cool_app": {
"secrets": {
"some_service_id": "foo",
"some_password": "bar"
}
}
You can create a deploy folder in the root of your application, create a file before_restart.rb inside it, then in your file run the precompile task
run "cd /srv/www/myapp/current && /usr/local/bin/bundle exec rake assets:precompile"
This file will run on every deployment
Source:
https://www.youtube.com/watch?v=nHu8fCp9GR4&list=WL&index=7
This can now be done directly from the AWS Console, on the application configuration, as per documentation : http://docs.aws.amazon.com/opsworks/latest/userguide/workingapps-creating.html#workingapps-creating-environment
I already answered here: AWS OpsWorks Environment variables not working
Is important to understand that from OpsWorks dashboard we can pass all declared environment variables to Chef, then we need to handle these variables with a Chef recipe to let them available to Rails environment.
Here you can find what you are looking for: https://medium.com/#diego_durante/opsworks-rails-and-environment-variables-30c6a143253c#.696grsgg9
I'm working on a project with multiple developers and currently we have to change the variables in our deploy.rb script depending on who's computer we're deploying from.
How can we set the username and path/to/project depending on which computer we're on?
We're all running on osx.
Example
if osx logged in user == "jeff"
set :user, "jeff's username'
else if ...
set :user, 'blah'
end
How can i do this in rails/capistrano/osx?
The os/x shell sets an env variable $USER that has the logged in user's login name. You can reference that as ENV['USER'] in your cap files
This is where environment variables come in handy.
set :user, ENV['USERNAME']
There are many ways to set this. You can do it directly in shell with
export USERNAME=blah
But for a cleaner, per-project solution I like the dotenv gem. With this enabled in :development group of your Gemfile, when Rails loads, it will export each line of a special .env file in the root of your application as environment variables.
USERNAME=blah
The whoami command should be available on most Unix systems, so the following should also work on Linux, etc.:
if `whoami` == "jeff"
set :user, "jeff's username'
elsif ...
set :user, 'blah'
end
I've got a staging server with both standard Ruby and Ruby Enterprise installed. As standard Ruby refuses to install a critical gem, I need to set $PATH so that ruby/gem/rake/etc. always refer to the REE versions. And since I use Capistrano to deploy to our machines, I need to do it in Capistrano.
How can I set an environment variable once, and have it persist throughout the Capistrano session?
1) It's easy to do in bashrc files, but Capistrano doesn't read bashrc files.
2) I'd use Capistrano's
default_environment['PATH'] = 'Whatever'
but Capistrano uses these environment variables like
env PATH=Whatever command arg ...
and they're lost whenever another shell is spun up within the executable passed to env. Like when you use sudo. Which is kinda important:
[holt#Michaela trunk]$ env VAR=hello ruby -e "puts ENV['VAR']"
hello
[holt#Michaela trunk]$ env VAR=hello sudo ruby -e "puts ENV['VAR']"
nil
3) And I can't use the bash export command, as these are lost too - Capistrano seems to start up a new shell for each command (or something like that), and that's lost, too:
cap> export MYVAR=12
[establishing connection(s) to xxx.xxx.xxx.xxx]
cap> echo $MYVAR
** [out :: xxx.xxx.xxx.xxx]
cap>
4) I've tried messing with Capistrano's :shell and :pty options as well (and in combination with the other approaches), but no luck there, either.
So - what's the right way to do this? This seems like such a basic task that there should be a really simple way to accomplish it, but I'm out of ideas. Anyone?
Thanks in advance!
I have the exactly same problem, but I think this solution is better:
set :default_environment, {
'env_var1' => 'value1',
'env_var2' => 'value2'
}
This works for me like a charm.
If you need to set a variable on the remote host other than PATH, you should know that sshd only allows certain /etc/profile or ~/.bashrc environment variables by default, for security reasons. As Lou said, you can either do cap shell and use the cap> printenv command, or you can do cap COMMAND=printenv invoke in one command.
If you see the variable when you ssh into the remote shell normally, but you don't see it in the cap printenv command, here's one solution:
Set PermitUserEnvironment yes in your remote server's /etc/ssh/sshd_config file, and restart sshd
Edit the ~/.ssh/environment file for the remote user you are ssh'ing in as, and put your variable(s) there as VARIABLE=value
Now those should show up when you do cap COMMAND=printenv invoke
I think you have in fact 2 problems:
1) You want to change the PATH on your remote host(s).
Alter/set the path in your .bashrc on your remote host(s) and run cap> printenv, if your path is right, goto #2, else try to add export BASH_ENV=~/.bashrc to your /etc/profile (be careful, ~/.bashrc will then be run for all non-interactive shell for all users)
2) You want sudo to keep your PATH
Run visudo on your remote host(s) and add:
Defaults exempt_group = "<your_user>"
I needed to set an environment variable for a specific task to work. The "run" command allows you to pass options which include :env:
run "cmd", :env => { 'name' => 'value' }
In my case, I wanted to add the environment variable to a task that I didn't write, so I used default_run_options which is used by all invocations of run. I added this to the top of my Capfile:
default_run_options[:env] = { 'name' => 'value' }
I tried unsuccessfully to use #brian-deterling's technique, which is pretty commonly used by others who have discussed this... Maybe I'm doing something wrong, but meanwhile I found the dotenv-rails gem, and it worked very nicely for loading up values out of a .env file in my project root.
The instructions on their Github repo are pretty straight-forward. I added the Dotenv.load to my config/application.rb