I recently received an email asking if it was possible to automate the joining of an ESXi 4.1 host to Active Directory within a kickstart installation. The simple answer is yes; you can easily do so using the same trick found in tip #7 in Automating ESXi 4.1 Kickstart Tips & Tricks post which connects locally to the vSphere MOB using python.
Having said that, there is a small caveat to this solution in which the credentials used to join the ESX(i) host must be exposed in clear-text, this may or may not be acceptable. A potential way of getting this to work is to create a dedicated windows service account with limited privileges to add only ESXi hosts to your domain.
The reason for the use of clear-text password is the vSphere API method that is used to join an ESX(i) host to AD domain, joinDomain(), only supports plain text.
Ideally, the password parameter should accept either an encrypted hash or some type of certificate which can be validated by Active Directory server. The actual solution is pretty straight forward, by crafting the appropriate call to vSphere MOB, we are able to generate a python script during the %firstboot section of ESX(i) kickstart installation which will execute upon the initial boot up of the host.
Update (03/26/11): Thanks to VMTN user klich who has found a solution to embed the password of the Active Directory user in a base64 encoding so that it is not visible in plain text for anyone who has access to the kickstart configuration file. To create the encoded hash, you will need access to either a ESX(i) or UNIX/Linux system which has python interpreter installed.
You will need to run the following command and substitute the password you wish to encode.
python -c "import base64;
print base64.b64encode('MySuperSecurePasswordYo')"
The output will be your encoded hash:
Note: Make sure your ESX(i) hostname is configured with a FQDN (Fully Qualified Domain Name), else you will get an error when trying to join to AD domain.
The following snippet should be added to your %firstboot section of your kickstart. Remember to replace the variables: domainname, ad_username and encodedpassword with your configurations and make sure to leave the password variable blank.
cat > /tmp/joinActiveDirectory.py << __JOIN_AD__ import sys,re,os,urllib,urllib2 # MOB url url = "https://localhost/mob/?moid=ha-ad-auth&method=joinDomain" # mob login credentials -- use password = "" for build scripting username = "root" password = "" # which domain to join, and associated OU # e.g. # "primp-industries.com" # "primp-industries.com/VMware Server OU" domainname = "primp-industries.com" # active directory credentials using encoded base64 password ad_username = "*protected email*" encodedpassword = "TXlTdXBlclNlY3VyZVBhc3N3b3JkWW8" ad_password = base64.b64decode(encodedpassword) #auth passman = urllib2.HTTPPasswordMgrWithDefaultRealm() passman.add_password(None,url,username,password) authhandler = urllib2.HTTPBasicAuthHandler(passman) opener = urllib2.build_opener(authhandler) urllib2.install_opener(opener) #execute method params = {'domainName':domainname,'userName':ad_username,'password':ad_password} e_params = urllib.urlencode(params) req = urllib2.Request(url,e_params) page = urllib2.urlopen(req).read() __JOIN_AD__ #execute python script to Join AD python /tmp/joinActiveDirectory.py
If you are using ESX(i) 4.1 Update 1 or if you run into the "urllib2.HTTPError: HTTP Error 403: Forbidden: Possible Cross-Site Request Forgery" you will need to use the following snippet below which includes the session cookie as part of the request to the vSphere MOB due to a recent CSRF patch from VMware identified by VMTN user klitch.
ESX(i) 4.1 Update 1
cat > /tmp/joinActiveDirectory.py << __JOIN_AD__ import sys,re,os,urllib,urllib2,base64 # mob url url = "https://localhost/mob/?moid=ha-ad-auth&method=joinDomain" # mob login credentials -- use password = "" for build scripting username = "root" password = "" # which domain to join, and associated OU # e.g. # "primp-industries.com" # "primp-industries.com/VMware Server OU" domainname = "primp-industries.com" # active directory credentials using encoded base64 password ad_username = "*protected email*" encodedpassword = "TXlTdXBlclNlY3VyZVBhc3N3b3JkWW8" ad_password = base64.b64decode(encodedpassword) # Create global variables global passman,authhandler,opener,req,page,page_content,nonce,headers,cookie,params,e_params # Code to build opener with HTTP Basic Authentication passman = urllib2.HTTPPasswordMgrWithDefaultRealm() passman.add_password(None,url,username,password) authhandler = urllib2.HTTPBasicAuthHandler(passman) opener = urllib2.build_opener(authhandler) urllib2.install_opener(opener) ### Code to capture required page data and cookie required for post back to meet CSRF requirements ### req = urllib2.Request(url) page = urllib2.urlopen(req) page_content= page.read() # regex to get the vmware-session-nonce value from the hidden form entry reg = re.compile('name="vmware-session-nonce" type="hidden" value="?([^\s^"]+)"') nonce = reg.search(page_content).group(1) # get the page headers to capture the cookie headers = page.info() cookie = headers.get("Set-Cookie") # Code to join the domain params = {'vmware-session-nonce':nonce,'domainName':domainname,'userName':ad_username,'password':ad_password} e_params = urllib.urlencode(params) req = urllib2.Request(url, e_params, headers={"Cookie":cookie}) page = urllib2.urlopen(req).read() __JOIN_AD__ #execute python script to Join AD python /tmp/joinActiveDirectory.py
You can also verify by clicking on Configurations->Authentication Services and verify that your ESX(i) host is now part of the Active Directory domain.
In the next post, I will go over the details of adding domain users within the ESX(i) kickstart.
As a side note, it is quite unfortunate that ESXi only supports Microsoft Active Directory and does not integrate with other well known directory services such as NIS/NIS+, Kerberos, OpenLDAP, eDirectory, etc; this was actually possible with classic ESX. VMware has continued to focus on Windows-centric solutions and neglect the UNIX/Linux community by not supporting OS agnostic management tools that integrate with their vSphere platform. I hope this will change in the future for true interoperability.