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.
dconvery says
Will -
Great post, as always! Is there a way to encrypt the AD Admin password? This would most likely be important in some enterprises where the ESXi admin is not allowed to see the AD admin secrets.
Dave
William says
Dave,
The issue is the vSphere method only supports clear text. This means even if you encrypted the password, during the kickstart you still had to decrypt it and if the vSphere admins had access to the ks.cfg, they could easily figure out the password. I was thinking about something like that but I couldn't come up with anything that wouldn't allow someone to easily reverse engineer it. The limitation is with VMware only supported clear text, ideally an encrypted hash or certificate would be a better approach
jasgrif11 says
Thanks Will for this script. I added it to my esxi kickstart but it doesn't seem to run. However if I run it manually on the host it works.
Any ideas?
It is part of my %firstboot.
Thanks Again
Jason
William says
@jasgrif11,
Have you taken a look at vSphere Client while this is occurring to see what errors it is throwing?
Jeremy says
Hi,
I'm just reading this post, but can't see the snippet for the %firstboot section. Is it missing?
Cheers,
Jeremy.
William says
@Jeremy,
It looks like the hosting for syntaxhighlighting might be down. You can view the source of this blog post to get the details. I'm hoping the hosting site will be back soon, sorry for inconvenience.
jhonathan barrios says
hello my name is Jhonathan 'I am using this script in my kickstart. in firstboot% for ESX (i) 5.0 and 5.1 and does not work throws no error might help if the commands are the same or if I should make any changes..