I needed a way to bulk add users from one group to another in Crowd this week. The problem was basically that not all members of a ‘staff’ group in Crowd were also members of a ‘jira-users’ group which was causing problems with logins in Jira.
Since Crowd’s UI doesn’t support bulk operations yet, doing it via its Remote API was the only option. I decided to implement this in Ruby for no particular reason other than that I wanted to play with another language. Ruby turned out to be surprisingly quick to learn actually.
Read on for the full implementation details…
Ruby setup
Being a complete Ruby newbie I didn’t know that Ruby (including SOAP4R the ruby soap library) comes pre-installed on OSX (Leopard). So I went ahead and installed it (again). I found out later that this is only needed on Tiger and Leopard users should use the installed version of Ruby. So here’s the setup steps for Tiger :).
I first installed MacPorts which also required Apple’s Xcode Tools to get gcc. XCode is found as an optional package on the OSX installation DVD.
Once I’d installed this, I installed Ruby following the instructions:
% port install ruby
Finally, I also needed to install SOAP4R:
% gem install soap4r --include-dependencies
Writing the script
First I generated my client scripts using wsdl2ruby:
% wsdl2ruby --wsdl http://localhost:8095/crowd/services/SecurityServer?wsdl --type client
Then it was time to write my script. There’s two ways to do this. Interactive Mode or in a script. I found that the interactive mode was incredibly useful to learn the language and try out things. You can start the interactive shell by invoking:
% irb
Once I’d figured out how to do things in the interactive shell, I ended up putting it all together into the following script:
require "defaultDriver.rb" # Set these for your crowd instance crowdBaseURL = 'http://localhost:9999/crowd' applicationName = 'demo' applicationPassword = 'demo' sourceGroup = 'staff' targetGroup = 'users' driver = SecurityServerPortType.new(crowdBaseURL + '/services/SecurityServer') authCtx = ApplicationAuthenticationContext.new(PasswordCredential.new(applicationPassword), applicationName, []) begin token = driver.authenticateApplication(:in0 => authCtx) rescue print "Error logging in: " + $! + "\n" raise end begin group = driver.findGroupByName(:in0 => token.out, :in1 => sourceGroup) print "Group '" + sourceGroup + "' has " + group.out.members.size.to_s + " members. Are you Sure you want to continue ? (y/n)\n" choice = gets if choice.chomp != 'y' print "Goodbye!" Process.exit end rescue print "Error retrieving group '" + sourceGroup + "':" + $! + "\n" raise end group.out.members.each do |member| begin driver.addPrincipalToGroup(:in0 => token.out, :in1 => member, :in2 => targetGroup) print "Added '" + member + "' to group '" + targetGroup + "'.\n" rescue print "Error adding '" + member + "' to group '" + targetGroup + "': " + $! + "\n" end end
To invoke the script simply run *ruby bulkcopyusers.rb*.
I’ve attached a zip file with all the generated scripts, if you want to use it with Crowd or just play with Ruby. Given that this is my first ever Ruby script, I’d love to hear about any improvements .
Total time taken was about 1 hour, which is probably only marginally longer than it would have taken me to write a Java client to do the same thing.