Use Chef to Set Environment Variables for different Windows Users - ruby

How can I use Use Chef to set Environment Variables for different Windows Users?
I've tried using the Execute block like so:
execute 'setx DUMMYENV' do
command 'setx DUMMYENV "THISISDUMMY"'
user "UserA"
end
However this requires a password and I don't want the password in plaintext in any of the recipes so this solution doesn't seem viable.
I've also thought about doing something with registry_key like so:
registry_key 'HKEY_USERS\S-1-5-21-0123456789-012345678-...\Environment' do
values [{name: 'DUMMYENV', type: :string, data: '1'},
{name: 'ENVTEST', type: :string, data: 'TESTING'}
]
action :create
end
The only problem I have with this is I'm not sure if the SID for each of the User accounts I'm trying to modify will change between machines. I can probably find a solution to loop through the available HKEY_Users and parse out the ones I'm looking for however this seems tedious.
Another option that I've started to look into is windows_env however I haven't gotten to test it much and I don't see a robust option to specify the user.
End goal is to set different environment variables for different users with Chef running as admin. Is there any easy way to do this with Chef?
Thanks in advance!

You can indeed do this with windows_env, though it isn't reflected in the documentation yet: https://github.com/chef/chef/blob/master/lib/chef/resource/windows_env.rb#L39
windows_env 'DUMMYENV' do
value 'THISISDUMMY'
user 'UserA'
end
Remember that if you're talking about scheduled tasks using env vars, you have to reboot before it will be visible.

Related

Send variable from one Chef recipe to another in order to trigger a resource block

In one recipe I have a rubyblock that ultimately obtains the port of a service that I'd like to restart.
Individually the recipes work fine and I am now trying to tie the two together.
I cannot seem to pass the variable to the other recipe having tried to follow a custom resource example.
my default recipe that obtains the port is:
Chef::Log.info("Port: #{port}")
Chef::Resource::Notification.new("stop-solr_#{port}", :run, self)
I'm trying to trigger the resource block of the name 'stop-solr_'portNumber'' using the notification sender.
My other recipe looks like the following and has a start/stop service purpose
solrCore = "solr_#{port}"
#define the service - does nothing
service solrCore do
action :nothing
end
#do something that triggers
execute "start-solr_#{port}" do
Chef::Log.info('triggers start')
action :nothing
notifies :start, run_context.resource_collection.find(:service => "#{solrCore}")
end
execute "stop-solr_#{port}" do
# some stuff
# on success...
Chef::Log.info('triggers restart')
notifies :stop, run_context.resource_collection.find(:service => "#{solrCore}"), :immediately
notifies :run, "execute[start-solr_#{port}]"
end
My main problem (I think) is that the variable solrCore uses 'port' which I cannot seem to obtain.
Is anyone able to help with what I need ot do in order to get this working?
Thanks in advance.
Variables in Ruby are local by default. You would have to use a global variable to share state between files, either a real Ruby global variable ($foo) or using the global node.run_state hash we expose to all recipes.
That said: there is a reason that mutable global variables have been a CS cliché for decades. Code like this is very fragile and difficult to debug. I would consider turning both of those recipes into custom resources and calling them from the same recipe with the same input port.
In the first recipe, you can save information in the node.
Sample:
node.normal['A']['B']['C']="completed"
node.save
and In second Recipe you can retrieve information from the node.
status=node.normal['A']['B']['C']
and based on the retrieved value you can take action

How can I conditionally run a block of resources or a recipe in chef?

I've the following recipe used to create some users, add them to a group and set the password to expire at the first login.
search(:users, '*:*').each do |user|
userPassword = "$1$scmdevop$ZDTyqia9RXSrpHGK75FjN/"
user user['id'] do
comment user['comment']
home user['home']
shell user['shell']
manage_home true
password "#{userPassword}"
end
if user['sudo'] then
group "#{node.default["sudogroup"]}" do
action :modify
members user['id']
append true
end
end
if (user['resetPassword'] == nil) || (user['resetPassword']) then
bash 'setExporation' do
code 'chage -d 0 ' + user['id']
user 'root'
end
end
end
The problem is that in this way it will continue to reset the password and set the espiration at every run so I was trying to find how to make it conditionally. I would like to use the following command to check if the user exist
grep -qs #{user["id"]} /etc/passwd
The problem is that I can use the not_if clause only in the first resource because after that the user has been clearly created. Is there a way to get the entire block of three resources being conditional to a shell exit code?
Thanks,
Michele.
What you probably want is a notification from the user resource, but this might be a little hard because that would trigger on any change, not just creation. The underlying problem here is that the desired behavior you stated is expressed in procedural terms, not in terms of convergent state. Best approach is probably to build a custom resource to hide some of this logic, but at heart what you want is an if statement like you already have.

Initializing Mongoid without .load()

I have the following line in my Sinatra app:
Mongoid.load!('./config/database/mongoid.yml')
This is nice, but I don't want to keep my connection details in a YAML file, and add it to .gitignore. I want to keep them in ENV.
I was able to bypass this in the past by adding stuff like username: <%= ENV['MONGODB_USER'] %> to the YAML config file, then reading it as ERB, saving it and reading it again with Mongoid.load! before Heroku wiped the disk. Needless to say, that's pretty nutty.
All I could find is the definition of .load! over here and it doesn't look like there's any way around this.
Is there some hidden way to programmatically configure Mongoid connections?
Thanks in advance.
Building on mu's answer:
You can give Mongoid a hash to use for initialization like this:
Mongoid.load_configuration(clients: {
default: {
database: database,
hosts: [ host ]
}
})
Note that the hash you pass to load_configuration is not expected to start with an environment key like you would normally have in mongoid.yml.
Mongoid.load! doesn't do very much:
def load!(path, environment = nil)
settings = Environment.load_yaml(path, environment)
if settings.present?
Sessions.disconnect
Sessions.clear
load_configuration(settings)
end
settings
end
All it does is a bit of bookkeeping, loads the YAML, and hands off to load_configuration to do the heavy lifting. There's nothing stopping you from building the settings Hash by hand and calling Mongoid.load_configuration yourself.

Passing options and parameters to Test/Unit via Rake::TestTask

So I've been trying to figure this out, and the best solution I can came up with his global variables - but that seems so dirty and 1974 - Am I missing a feature of Rake/ Test::Unit?
I have a Rake file in which I'm running tests:
Rake::TestTask.new(:test) do |t|
t.test_files = FileList['test_*.rb']
end
and test_1.rb has something like this:
require "test/unit"
class TestStuff < Test::Unit::TestCase
def setup
#thingy = Thing.New(parameter1, parameter2)
end
def test_this_thing
#thing.do()
end
end
My problem is, Thing.new() requires arguments, and those arguments are specific to my environment. In reality, I'm running Selenium-WebDriver, and I want to pass in a browser type, and a url, etc... sometimes I want ff, othertimes I want chrome... sometimes this URL, sometimes that... depending on the day, etc.
The simplest thing seems to do something like:
#all that rake stuff
$parameter1 = x
$parameter2 = y
and then make Thing.new() look up my global vars:
#thingy = Thing.New($parameter1, $parameter2)
This seems sloppy.. and it just doesn't feel right to me. I'm still trying to get this 'test harness' up and running, and want to do it right the first time. That's why I chose Rake, based on a lot of other feedback.
Keep in mind, I'll probably have 100's of tests, ultimately, and they'll all need to do get this information... I thought Rake was good at making sure all of this was easy, but it doesn't seem to be.
Where did I go wrong?
I have used YAML files to store my configuration (browser config, environments including URLs, etc).
You can also use an environmental variable to define simple configurations. You can access environmental variables via ENV['foobar'] in Ruby.
So, for example, my browser call might look like this inside my setup method:
driver = Selenium::WebDriver.for (ENV['SWD_BROWSER'] || "firefox").to_sym
and in my Rake file (or in the shell console) define the environmental variable to use.

Passing values from Capistrano deploy.rb file to app

In my Capistrano's deploy.rb file, I set up different environments such as server names, ports, etc. I also require the users to send a callback to another server, also defined in the deploy.rb. How do I cleanly pass this value to my app?
Something to this effect:
config/deploy.rb:
set :callback_url, "http://somecallbackurl.com:12345/bla"
app/controllers/myapp.rb:
def get_callback_url
???
end
I'm using Sinatra.
I found a solution, and that is to use the environment variables.
Set it from deploy.rb
run "export CALLBACK_URL=#{callback_url}"
From app:
def get_callback_url
ENV['CALLBACK_URL']
end
I wouldn't say it's the cleanest solution, but it works.
I'd probably recommend using a shared YAML file to store this kind of configuration, and loading it separately. For example, have a file named something like config/settings.yml, containing something like:
:callback_url: "http://somecallbackurl.com:12345/bla"
In config/deploy.rb, you could have:
settings = YAML.load_file('config/settings.yml')
set :callback_url, settings[:callback_url]
And in config/initializers/settings.rb, you could have:
settings = YAML.load_file('config/settings.yml')
CALLBACK_URL = settings[:callback_url]
Finally, in app/controllers/myapp.rb, you would do:
def get_callback_url
CALLBACK_URL
end
Using a shared YAML file is just the first thing I thought of. Another approach would be defining some constants in a ruby file, and requiring that file both in an initializer, and in deploy.rb. The basic idea is that you don't really want your app to depend on your capistrano environment, so you should find a way to separate the shared configuration.

Resources