Here's the script I made after following Jonathan's advice. This script iterates through all .mxd files in a given folder and publishes a map service with each document. I modified the script to get the map service properties as JSON via the Admin API. Then I modified the properties in .sddraft to match what came back from the Admin API request. This basically works the same as overwriting a map service while keeping the same service properties.
Some details specific to this script that you may have to modify:
- This script only works with map services, not feature services or hosted feature services.
- This uses the 'requests' module which you would have to add to your Python installation. There's other modules out there that do similar things if you want to avoid using this one. I use this module for HTTP requests because I like the easy-to-read syntax.
- You may want to modify lines 78-85 (where it obtains the service properties from the Admin API) to first write the JSON to a file, then look at the output file in a text editor so you know where the properties you care about are. For example, min instances is not nested but schemaLockingEnabled is nested under 'properties'. I'm only overwriting the properties we have that are not default.
- I've added an extra check to the part of the script that checks for publishing errors, warnings, and messages that will cause the publishing job to fail if the warning "24011: Data source is not registered with the server and data will be copied to the server" appears, because for us this usually means there's something wrong with one of the registered data sources in the map document.
Hope this helps!
import arcpy, glob, os, sys, json, requests
import xml.dom.minidom as DOM
from arcpy import env
svrName = arcpy.GetParameterAsText(0)
username = arcpy.GetParameterAsText(1)
password = arcpy.GetParameterAsText(2)
wrkspc = arcpy.GetParameterAsText(3)
serverFolder = arcpy.GetParameterAsText(4)
if svrName == 'DEV':
server_url = 'https://dev-server-machine.domain.com/arcgis/admin/'
token_url = 'https://dev-portal-machine.domain.com:7443/arcgis/sharing/rest/generateToken'
elif svrName == 'TEST':
server_url = 'https://test-server-machine.domain.com/arcgis/admin/'
token_url = 'https://test-portal-machine.domain.com:7443/arcgis/sharing/rest/generateToken'
elif svrName == 'UAT':
server_url = 'https://uat-server-machine.domain.com/arcgis/admin/'
token_url = 'https://uat-portal-machine.domain.com:7443/arcgis/sharing/rest/generateToken'
else:
server_url = 'https://prod-server-machine.domain.com/arcgis/admin/'
token_url = 'https://prod-portal-machine.domain.com:7443/arcgis/sharing/rest/generateToken'
if serverFolder == '[root]':
serverFolder = ''
out_folder_path = wrkspc
con = 'tmp.ags'
use_arcgis_desktop_staging_folder = True
staging_folder_path = None
arcpy.mapping.CreateGISServerConnectionFile('PUBLISH_GIS_SERVICES',
out_folder_path,
con,
server_url,
'ARCGIS_SERVER',
use_arcgis_desktop_staging_folder,
staging_folder_path,
username,
password,
'SAVE_USERNAME')
requests.packages.urllib3.disable_warnings()
payload = { 'username': username, 'password': password, 'expiration': '60', 'referer': server_url, 'request': 'gettoken', 'f': 'json' }
r = json.loads(requests.post(token_url, data=payload, verify=False).content)
if 'token' in r:
token = r.get('token', None)
else:
arcpy.AddError('Error: Unable to generate token')
sys.exit()
env.workspace = wrkspc
os.chdir(wrkspc)
for mxd_str in glob.glob('*.mxd'):
arcpy.AddMessage('***Publishing {0}***'.format(mxd_str))
mapDoc = arcpy.mapping.MapDocument(mxd_str)
filename = os.path.splitext(mxd_str)[0]
service = filename
sddraft = filename + '.sddraft'
sd = filename + '.sd'
arcpy.mapping.CreateMapSDDraft(mapDoc, sddraft, service, 'FROM_CONNECTION_FILE', con, False, serverFolder)
if serverFolder == '':
url = server_url + 'services/' + service + '.MapServer'
else:
url = server_url + 'services/' + serverFolder + '/' + service + '.MapServer'
payload = {'f':'json',
'token':token}
r = json.loads(requests.post(url, data=payload).content)
xml = sddraft
doc = DOM.parse(xml)
keys = doc.getElementsByTagName('Key')
for key in keys:
if key.hasChildNodes():
if key.firstChild.data == 'maxDomainCodeCount':
key.nextSibling.firstChild.data = r['properties']['maxDomainCodeCount']
if key.firstChild.data == 'antialiasingMode':
key.nextSibling.firstChild.data = r['properties']['antialiasingMode']
if key.firstChild.data == 'maxRecordCount':
key.nextSibling.firstChild.data = r['properties']['maxRecordCount']
if key.firstChild.data == 'schemaLockingEnabled':
key.nextSibling.firstChild.data = r['properties']['schemaLockingEnabled']
if key.firstChild.data == 'MinInstances':
key.nextSibling.firstChild.data = r['minInstancesPerNode']
if key.firstChild.data == 'MaxInstances':
key.nextSibling.firstChild.data = r['maxInstancesPerNode']
if key.firstChild.data == 'IdleTimeout':
key.nextSibling.firstChild.data = r['maxIdleTime']
newSddraft = filename + 'New.sddraft'
f = open(newSddraft, 'w')
doc.writexml(f)
f.close()
analysis = arcpy.mapping.AnalyzeForSD(newSddraft)
badDataSource = False
arcpy.AddMessage('The following information was returned during analysis of the MXD:')
for key in ('messages', 'warnings', 'errors'):
arcpy.AddMessage('----{0}----'.format(key.upper()))
vars = analysis[key]
for ((message, code), layerlist) in vars.iteritems():
if code == 24011 or code == 24012:
badDataSource = True
arcpy.AddMessage(' {0} (CODE {1})'.format(message, code))
arcpy.AddMessage(' applies to:')
for layer in layerlist:
arcpy.AddMessage(' {0}'.format(layer.name))
if (analysis['errors'] == {} and not badDataSource):
arcpy.StageService_server(newSddraft, sd)
arcpy.UploadServiceDefinition_server(sd, con)
arcpy.AddMessage('Service successfully published')
os.remove(sd)
else:
arcpy.AddMessage('Service could not be published because errors were found during analysis.')
os.remove(newSddraft)
os.remove(sddraft)
arcpy.AddMessage(arcpy.GetMessages())
os.remove(con)
arcpy.AddMessage('All done!')