Example: Add members to the portal
This example takes an input text file with information about users and adds them to your portal in bulk. You can define which role each of the users will be granted in your portal.
You're required to enter the following information when running the script:
- Path to the text file containing the user information, for example, /home/admin/documents/users.txt.
- Type of users you want to add to the portal, for example, built-in or enterprise. To learn more about user types, see Managing access to your portal.
- Fully-qualified domain name of the machine hosting your portal, for example, myportal.acme.com. The script will access your portal over HTTPS on port 7443, bypassing the ArcGIS Web Adaptor. In this manner, the script does not need to handle web tier authentication if your portal is configured to use it.
- Username and password of an account that has administrative privileges to the portal. If you're adding built-in accounts, you can provide the credentials of any member of the portal with administrative privileges. If you're adding enterprise accounts, you must use the initial administrator account created after installing Portal for ArcGIS.

Demonstrating how to make Python scripts handle web tier authentication is beyond the scope of this example.
Adding built-in portal accounts
When adding built-in portal accounts, the format for each entry in the text file is as follows:
<username>|<password>|<email address>|<name>|<role>|<description>
- username—The user name to be used for the built-in account. User names must be unique; no two members can have the same user name.
- password—This is a password to be assigned to the account. Users can use this password the first time they sign in to the portal, and then they can change their password by editing their profile.
- email address—Provide an email address for this account. This parameter is required; therefore, you must provide a value for the email address even if it is not a valid address.
- name—The name is the alias for the account that will be used in your ArcGIS organization. When the user is connected to the portal website, this name appears at the top of the portal website.
- role—This is the role the account will have in the ArcGIS organization. Valid role values are org_user, org_publisher, or org_admin.
- description—Optionally, you can include text to describe the account. Descriptions cannot exceed 250 characters.
This example adds a built-in portal account with the user name pub1 for Barbara Williams, with an email account of bwilliams@domain.com. It also adds pub1 to the publisher role and describes it as a member of the planning department:
pub1|changepasswordlater|bwilliams@domain.com|Barbara Williams|org_publisher|planning department
Registering enterprise accounts
When registering enterprise accounts, the format for each entry in the text file is as follows:
<username>|<email address>|<name>|<role>|<description>
- username—The user name is the enterprise account to be registered. If you are using Active Directory, the user name should be in the form sAMAccountName@DOMAIN. The domain name must be in all capital letters. If you are using LDAP, the login should match the value of the userNameAttribute you specified when configuring the identity store.
- email address—The email address should be the email associated with the account and match the value in the identity store. If the user account does not have an email address, provide a false or generic value.
- name—The name is the alias for the login that will be used in your ArcGIS organization. Most identity stores use the user's full name as the default alias. When the user is connected to the portal website, this name appears at the top of the website.
- role—This is the role the account will have in the ArcGIS organization. Valid role values are org_user, org_publisher, or org_admin.
- description—Optionally, you can include text to describe the account. This value does not correspond to any attribute in the identity store. Descriptions cannot exceed 250 characters.
You're required to provide a value for the username, email address, name, and role. The description is optional. For each account listed in the file, verify the values you entered for the login, email address, and name exactly match the values in your identity store. The portal will not connect to your identity store to validate these values.
The following is an example of an entry to register an enterprise account for login jcho111, with an email address of jcho@domain.com and a full name of Jon Cho. The login is placed in the user role (org_user) and is described as a user in department b:
jcho111@DOMAIN|jcho@domain.com|Jon Cho|org_user|department b
#!/usr/bin/env python
# Requires Python 2.7+
# Demonstrates how to add users to Portal for ArcGIS in bulk
# For Http calls
import httplib, urllib2, urllib, json
# For system tools
import sys, os
# For reading passwords without echoing
import getpass
# Other utilities
import Queue
# Defines the entry point into the script
def main(argv):
print "This script adds users in bulk into a portal. \n"
#Get parameters
parameters = getParametersFromUser ()
portalURL = parameters['portalURL']
provider = parameters['provider']
userName = parameters['userName']
password = parameters['password']
inUserFile = parameters['inUserFile']
#Get user data from file
usersData = getUserDataFromFile(inUserFile,provider)
#Create users
createUsers (userName,password, portalURL,provider, usersData)
raw_input('Press ENTER to close the script.')
return
# This function loads all the user data in the input text file into a Python Queue.
# This usersQueue can be later passed to the createUsers function
def getUserDataFromFile(inUserFile,provider):
usersQ = Queue.Queue()
keyParams = ['username', 'password', 'email', 'fullname','role','description']
inFileHandle = open(inUserFile, 'r')
userCount = 0
print '...Processing input users file at: ' + inUserFile
entryCount = 1;
for line in inFileHandle.readlines():
userParams = line.split('|')
userParamDict = {}
if provider=="webadaptor":
if len(userParams) == 5:
for i in range (0,5):
userParamDict[keyParams[0]] = userParams[0] # login
userParamDict[keyParams[1]] = ""
userParamDict[keyParams[2]] = userParams[1] # email address
userParamDict[keyParams[3]] = userParams[2] # name
userParamDict[keyParams[4]] = userParams[3] # role
userParamDict[keyParams[5]] = userParams[4].replace('\n','') # description
usersQ.put (userParamDict)
userCount = userCount + 1
else:
print ' The format for entry %s is invalid. The format for enterprise accounts should be <login>|<email address>|<name>|<role>|<description>. \n '% (entryCount)
raise SystemExit( 'When registering enterprise accounts, the format for each entry is as follows: <login>|<email address>|<name>|<role>|<description>')
elif provider=="arcgis":
if len(userParams) == 6:
for i in range (0,6):
userParamDict[keyParams[0]] = userParams[0] # account
userParamDict[keyParams[1]] = userParams[1] # password
userParamDict[keyParams[2]] = userParams[2] # email address
userParamDict[keyParams[3]] = userParams[3] # name
userParamDict[keyParams[4]] = userParams[4] # role
userParamDict[keyParams[5]] = userParams[5].replace('\n','') # description
usersQ.put (userParamDict)
userCount = userCount + 1
else:
print ' The format for entry %s is invalid. The format for built-in portal accounts should be <account>|<password>|<email address>|<name>|<role>|<description>. \n '% (entryCount)
raise SystemExit( 'When registering built-in portal accounts, the format for each entry is as follows: <account>|<password>|<email address>|<name>|<role>|<description>')
else:
print ' The provider is incorrect. Script ended. \n'
raise SystemExit( 'The value for the user type is invalid. ')
entryCount = entryCount +1
if not ((userParamDict[keyParams[4]].lower()== "org_user") or (userParamDict[keyParams[4]].lower()=="org_publisher") or (userParamDict[keyParams[4]].lower()== "org_admin")):
raise SystemExit( 'The value for the user role %s in users text file is invalid. Accepted values are org_user or org_publisher or org_admin. ' % (userParamDict[keyParams[4]]))
inFileHandle.close()
# Create users and report results
print '...Total members to be added: ' + str(userCount)
return usersQ
# This function connects to the portal and adds members to it from a collection
def createUsers(username,password, portalUrl, provider,userParamsQ):
print '...Connecting to ' + portalUrl
token = generateToken(username,password, portalUrl)
print '...Adding users '
usersLeftInQueue = True
while usersLeftInQueue:
try:
userDict = userParamsQ.get(False)
userDict['f'] = 'json'
userDict['token'] = token
userDict['provider'] = provider
params = urllib.urlencode(userDict)
request = urllib2.Request(portalUrl + '/portaladmin/security/users/createUser?',params)
# POST the create request
response = urllib2.urlopen(request).read()
responseJSON = json.loads(response)
# Log results
if responseJSON.has_key('error'):
errDict = responseJSON['error']
if int(errDict['code'])==498:
message = 'Token Expired. Getting new token... Username: ' + userDict['username'] + ' will be added later'
token = generateToken(username,password, portalUrl)
userParamsQ.put(userDict)
else:
message = 'Error Code: %s \n Message: %s' % (errDict['code'],
errDict['message'])
print '\n' + message
else:
# Success
if responseJSON.has_key('status'):
resultStatus = responseJSON['status']
print '\n' + 'User: %s account created' % (userDict['username'])
print 'User: %s account created' % (userDict['username'])
except Queue.Empty:
usersLeftInQueue = False
# This function gets a token from the portal
def generateToken(username, password, portalUrl):
'''Retrieves a token to be used with API requests.'''
parameters = urllib.urlencode({'username' : username,
'password' : password,
'client' : 'referer',
'referer': portalUrl,
'expiration': 60,
'f' : 'json'})
try:
response = urllib.urlopen(portalUrl + '/sharing/rest/generateToken?',
parameters).read()
except Exception as e:
raise SystemExit( 'Unable to open the url %s/sharing/rest/generateToken' % (portalUrl))
responseJSON = json.loads(response.strip(' \t\n\r'))
# Log results
if responseJSON.has_key('error'):
errDict = responseJSON['error']
if int(errDict['code'])==498:
message = 'Token Expired. Getting new token... '
token = generateToken(username,password, portalUrl)
else:
message = 'Error Code: %s \n Message: %s' % (errDict['code'],
errDict['message'])
raise SystemExit(message)
token = responseJSON.get('token')
return token
# This function gets gets parameters from the user in interactive mode
def getParametersFromUser():
parameters = {}
# Get Location of users file
inUserFile = raw_input ("Enter path to users text file: ")
if not os.path.exists(inUserFile):
print ' File does not exist. Script ended. \n'
raise SystemExit( 'Input file: %s does not exist' % (inUserFile))
parameters['inUserFile'] = inUserFile
# Enteprise logins or built-in accounts?
userInput = raw_input ("What type of users do you want to add to the portal? Accepted values are built-in or enterprise: ")
if userInput.lower()=="built-in":
parameters['provider'] = 'arcgis'
print ' Built-in accounts will be added to the portal. \n'
elif userInput.lower()=="enterprise":
parameters['provider'] = 'webadaptor'
print ' Enterprise accounts will be added to the portal. \n'
else:
print ' The type of users is incorrect. Script ended. \n'
raise SystemExit( 'The value entered for the user type %s is invalid. Accepted values are built-in or enterprise. ' % (userInput))
# Get Portal URL
hostname = raw_input("Enter the fully qualified portal hostname (for example myportal.acme.com): ")
parameters['portalURL'] = 'https://' + hostname + ':7443/arcgis'
print ' Users will be added to portal at: ' + parameters['portalURL'] + '\n'
# Get a username and password with portal administrative privileges
parameters['userName'] = raw_input("Enter a built-in user name with portal administrative privileges:")
parameters['password'] = raw_input("Enter password: ")
print '\n'
return parameters
# Script start
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))