diff --git a/src/python/dm/aps_bss/api/apsBssApi.py b/src/python/dm/aps_bss/api/apsBssApi.py
index fb87f28c963292f0ebe99c4236db02d1e404d485..11e4f934808806c2181a28f4ec0defc844b2a384 100755
--- a/src/python/dm/aps_bss/api/apsBssApi.py
+++ b/src/python/dm/aps_bss/api/apsBssApi.py
@@ -21,7 +21,7 @@ class ApsBssApi(DmApi):
         :type password: str
 
         :param loginFile: BSS login file (not used if username/password are provided; it can be replaced by environment variable DM_BSS_LOGIN_FILE)
-        :type password: str
+        :type loginFile: str
 
         :raises ConfigurationError: in case beamline name or username/password are not provided, and corresponding environment variables are not set
 
@@ -108,8 +108,8 @@ class ApsBssApi(DmApi):
         '''
         Get beamline proposal with a given id.
 
-        :param porposalId: proposal id
-        :type runName: str
+        :param proposalId: proposal id
+        :type proposalId: str
 
         :param runName: run name (if not provided, current run name will be used)
         :type runName: str
diff --git a/src/python/dm/aps_bss/cli/getProposalCli.py b/src/python/dm/aps_bss/cli/getProposalCli.py
index ad13b645073281e9bd81582cb9210a77636d1605..b2f215c4e7ab87b1a8395961e680f7a4c7bea34c 100755
--- a/src/python/dm/aps_bss/cli/getProposalCli.py
+++ b/src/python/dm/aps_bss/cli/getProposalCli.py
@@ -9,7 +9,7 @@ class GetProposalCli(DmCli):
         DmCli.__init__(self)
         self.addOption('', '--id', dest='proposalId', help='Proposal id.')
         self.addOption('', '--run', dest='runName', help='Run name. If not provided, current run will be used.')
-        self.addOption('', '--login-file', dest='loginFile', help='BSS login file. It may be provided via environment variable DM_BSS_LOGIN_FILE.')
+        self.addOption('', '--login-file', dest='loginFile', help='BSS login file. Login file  may also be specified via environment variable DM_BSS_LOGIN_FILE.')
 
     def checkArgs(self):
         if not self.options.proposalId:
diff --git a/src/python/dm/common/client/sessionManager.py b/src/python/dm/common/client/sessionManager.py
index 2e2dbc69f4b6ecb722c0657275247268134358a6..6becaa803cafe4338aaa08b140fa1069829af8e6 100755
--- a/src/python/dm/common/client/sessionManager.py
+++ b/src/python/dm/common/client/sessionManager.py
@@ -56,7 +56,9 @@ class SessionManager:
         try:
             # User will be asked for username/password if they are not
             # provided.
-            data = { 'username' : self.getUsername(username), 'password' : self.getPassword(password) }
+            self.username = self.getUsername(username)
+            self.password = self.getPassword(password)
+            data = { 'username' : self.username, 'password' : self.password }
             self.logger.debug('Establishing session for user %s @ %s)' % (username, url))
             (response,responseData) = self.sendRequest(url='%s%s' % (url, selector), method='POST', contentType='application/x-www-form-urlencoded', data=urllib.urlencode(data))
         except urllib2.URLError, ex:
@@ -66,7 +68,7 @@ class SessionManager:
         #self.logger.debug('Got headers: %s' % response.headers)
         # This will save session cookie.
         self.sessionCookie = self.checkResponseHeadersForErrorsAndSaveSession(response.headers)
-        self.logger.debug('User %s session cookie: %s' % (username, self.sessionCookie))
+        self.logger.debug('User %s session cookie: %s' % (self.username, self.sessionCookie))
 
     def getUsername(self, username):
         if username is None and self.requireSessionCredentials:
diff --git a/src/python/dm/common/utility/noopPlatformUtility.py b/src/python/dm/common/utility/noopPlatformUtility.py
new file mode 100755
index 0000000000000000000000000000000000000000..c7387b1197cc1da2c2e9339eba96a20e0494e5d1
--- /dev/null
+++ b/src/python/dm/common/utility/noopPlatformUtility.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+
+from dm.common.utility.loggingManager import LoggingManager
+
+class NoopPlatformUtility:
+
+    def __init__(self):
+        self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
+
+    def createGroup(self, name):
+        self.logger.debug('createGroup called for %s' % name)
+
+    def addUserToGroup(self, username, groupName):
+        self.logger.debug('addUserToGroup called for %s, %s' % (username, groupName))
+
+    def deleteUserFromGroup(self, username, groupName):
+        self.logger.debug('deleteUserFromGroup called for %s, %s' % (username, groupName))
+
+    def createLocalGroup(self, name):
+        self.logger.debug('createLocalGroup called for %s' % name)
+
+    def addLocalUserToGroup(self, username, groupName):
+        self.logger.debug('addLocalUserToGroup called for %s, %s' % (username, groupName))
+
+    def deleteLocalUserFromGroup(self, username, groupName):
+        self.logger.debug('adddeleteLocalUserFromGroup called for %s, %s' % (username, groupName))
+
+    def getGroupInfo(self, groupName):
+        self.logger.debug('getGroupInfo called for %s' % name)
+
+    def setGroupUsers(self, groupName, usernameList):
+        self.logger.debug('setGroupUsers called for %s, %s' % (groupName, usernameList))
+
+    def setPathReadExecutePermissionsForGroup(self, path, groupName):
+        self.logger.debug('setPathReadExecutePermissionsForGroup called for %s, %s' % (path, groupName))
+
+    def changePathGroupOwner(self, path, groupName):
+        self.logger.debug('changePathGroupOwner called for %s, %s' % (path, groupName))
+
+    def recursivelyChangePathGroupOwner(self, path, groupName):
+        self.logger.debug('recursivelyChangePathGroupOwner called for %s, %s' % (path, groupName))
+
+    def refreshNscdGroupCache(self):
+        self.logger.debug('refreshNscdGroupCache called')
+
+    def refreshAuthFiles(self):
+        self.logger.debug('refreshAuthFiles called')
+
+    def chmodPathForFilesInDirectory(self, directoryPath, fileMode):
+        self.logger.debug('chmodPathForFilesInDirectory called for %s, %s' % (directoryPath, fileMode))
+
+#######################################################################
+# Testing.
+
+if __name__ == '__main__':
+    pass
diff --git a/src/python/dm/ds_web_service/cli/addExperimentCli.py b/src/python/dm/ds_web_service/cli/addExperimentCli.py
index f509231d7b17a48b23177050927d6ebbad6627be..567b96e9b645871ffda480e0c43c2e3c2a99f3ef 100755
--- a/src/python/dm/ds_web_service/cli/addExperimentCli.py
+++ b/src/python/dm/ds_web_service/cli/addExperimentCli.py
@@ -1,6 +1,9 @@
 #!/usr/bin/env python
 
+from dm.aps_bss.api.apsBssApi import ApsBssApi
 from dm.ds_web_service.api.experimentRestApi import ExperimentRestApi
+from dm.ds_web_service.api.userRestApi import UserRestApi
+
 from dm.common.exceptions.invalidRequest import InvalidRequest
 from dm.common.utility.configurationManager import ConfigurationManager
 from dsWebServiceSessionCli import DsWebServiceSessionCli
@@ -22,6 +25,10 @@ class AddExperimentCli(DsWebServiceSessionCli):
         self.addOption('', '--description', dest='description', help='Experiment description.')
         self.addOption('', '--start-date', dest='startDate', help='Experiment start date in format DD-MMM-YY.')
         self.addOption('', '--end-date', dest='endDate', help='Experiment end date in format DD-MMM-YY.')
+        self.addOption('', '--users', dest='users', help='Comma-separated list of DM usernames to be added to the new experiment as users.')
+        self.addOption('', '--proposal-id', dest='proposalId', help='Beamline proposal id. If specified, all users listed on the proposal will be added to the new experiment.')
+        self.addOption('', '--run', dest='runName', help='Run name. If not specified, current run name is assumed for beamline proposal.')
+        self.addOption('', '--bss-login-file', dest='bssLoginFile', help='BSS login file. Login file may also be specified via environment variable DM_BSS_LOGIN_FILE.')
 
     def checkArgs(self):
         if self.options.experimentName is None:
@@ -64,6 +71,22 @@ class AddExperimentCli(DsWebServiceSessionCli):
     def getEndDate(self):
         return self.options.endDate
 
+    def getProposalId(self):
+        proposalId = self.options.proposalId
+        if proposalId:
+            proposalId = int(proposalId)
+        return proposalId
+
+    def getUsers(self):
+        users = self.options.users
+        if users:
+            # Remove duplicates by converting into set
+            users = list(set(users.split(',')))
+            
+        else:
+            users = []
+        return users
+
     def runCommand(self):
         self.parseArgs(usage="""
     dm-add-experiment 
@@ -71,13 +94,67 @@ class AddExperimentCli(DsWebServiceSessionCli):
         [--description=DESCRIPTION] 
         [--start-date=STARTDATE] 
         [--end-date=ENDDATE]
+        [--users=USERS]
+        [--proposal-id=PROPOSALID]
+        [--run=RUNNAME]
+        [--bss-login-file=BSSLOGINFILE]
 
 Description:
-    Add new experiment to the DM database. 
+    Add new experiment to the DM database. If list of users or proposal id is specified, this command will also add roles for all users listed on the proposal.
         """)
         self.checkArgs()
-        api = ExperimentRestApi(self.getLoginUsername(), self.getLoginPassword(), self.getServiceHost(), self.getServicePort(), self.getServiceProtocol())
-        experiment = api.addExperiment(self.getExperimentName(), self.getStationName(), self.getTypeName(), self.getDescription(), self.getStartDate(), self.getEndDate())
+        dsExperimentApi = ExperimentRestApi(self.getLoginUsername(), self.getLoginPassword(), self.getServiceHost(), self.getServicePort(), self.getServiceProtocol())
+        dsUserApi = UserRestApi(self.getLoginUsername(), self.getLoginPassword(), self.getServiceHost(), self.getServicePort(), self.getServiceProtocol())
+
+        description = self.getDescription()
+        proposalId = self.getProposalId()
+        experimenters = []
+        if proposalId:
+            bssApi = ApsBssApi(loginFile=self.options.bssLoginFile)
+            proposal = bssApi.getBeamlineProposal(proposalId=proposalId, runName=self.options.runName)
+            experimenters = proposal.get('experimenters', [])
+            if not description:
+                description = '%s (Proposal id: %s)' % (proposal['title'], proposalId)
+
+
+        users = self.getUsers()
+        pis = []
+        for experimenter in experimenters:
+            badge = int(experimenter['badge'])
+            if not badge:
+                #print 'Skipping user %s due to invalid badge.' % lastName 
+                continue
+            username = 'd%s' % badge
+
+            # Clasify user
+            if experimenter.get('piFlag') == 'Y':
+                if not pis.count(username):
+                    pis.append(username)
+                if users.count(username):
+                    users.remove(username)
+            else:
+                if not users.count(username):
+                    users.append(username)
+
+        for username in users+pis:
+            # Check that user exists
+            dsUserApi.getUserByUsername(username)
+           
+
+        # Everything looks good, add experiment and users
+        experiment = dsExperimentApi.addExperiment(self.getExperimentName(), self.getStationName(), self.getTypeName(), description, self.getStartDate(), self.getEndDate())
+
+        # Add pis.
+        experimentName = experiment['name']
+        roleName = 'PI'
+        for username in pis:
+            dsUserApi.addUserExperimentRole(username, roleName, experimentName)
+        roleName = 'User'
+        for username in users:
+            dsUserApi.addUserExperimentRole(username, roleName, experimentName)
+
+        if len(users+pis):
+            experiment = dsExperimentApi.getExperimentByName(experimentName)
         print experiment.getDisplayString(self.getDisplayKeys(), self.getDisplayFormat())
 
 #######################################################################