VMware Cloud Community
tdubb123
Expert
Expert

join domain kickstart

how do I join the domain in a kickstart file?

Reply
0 Kudos
22 Replies
lamw
Community Manager
Community Manager

Take a look here - http://www.virtuallyghetto.com/2011/02/automating-active-directory-domain-join.html

Remember this is unsupported by VMware, else you can use the vCLI and/or PowerCLI to do so remotely

Reply
0 Kudos
tdubb123
Expert
Expert

i put this under %firstboot but getting errors saying

urllib2.HTTPError: HTTP Error 401: Unauthorized

cat > /tmp/joinActiveDirectory.py << __JOIN_AD__
import sys,re,os,urllib,urllib2
# connection info to MOB
username = "root"
password = "xxxxx"
domainname = "xxx.com"
ad_username = "xxxx"
ad_password = "xxxx"
#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
Reply
0 Kudos
lamw
Community Manager
Community Manager

As the post mentions, leave the "password" variable empty, before the final init scripts run, ESXi host has no password. Give that a shot

Reply
0 Kudos
tdubb123
Expert
Expert

i removed the root password but in my kickstart file

I have rootpw --iscrypted

is this a problem?

I am still getting an error

urllibz.HTTPerror: Http Error 403: Forbidden: Posssible Cross-Site Request Forgery

Reply
0 Kudos
lamw
Community Manager
Community Manager

No, leave the actual root password you're setting for your ESXi host but remove the "password" variable in the python snippet

Reply
0 Kudos
tdubb123
Expert
Expert

where are the esxi install logs located? Its still not joinin the domain

here is partially what is in the kickstart file

%firstboot --unsupported --interpreter=busybox --level=998
cd /tmp
chmod +x post_install.cfg
ash post_install.cfg
# Join Domain
cat > /tmp/joinActiveDirectory.py << __JOIN_AD__
import sys,re,os,urllib,urllib2
# connection info to MOB
username = "root"
password = ""
domainname = "xxx.com"
ad_username = "xxxx"
ad_password = "xxxx"
#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
%firstboot --unsupported --interpreter=busybox --level=9999
cd /tmp
chmod +x patch_install.cfg
ash patch_install.cfg
Reply
0 Kudos
lamw
Community Manager
Community Manager

The ESXi installation logs are in /var/log/esxi_install.log but again you won't see much in there because this is going through a different method of calling the API.

Can you confirm that the credentials you're using do work? I also mentioned in the blog post that your ESXi host must have FQDN when trying to join to an AD domain else you'll get an error. Please verify both of these

Reply
0 Kudos
tdubb123
Expert
Expert

yes credentials do work. the server has a dns entry

in the post_install.cfg file before the python script

vim-cmd hostsvc/net/dns_set --hostname=xxxxx --domainname=domain.com --ip-addresses=x.x.x.x,y.y.y.y --searchdomain=domain.com

Reply
0 Kudos
lamw
Community Manager
Community Manager

Can you also run "hostname" command to ensure that it's getting the correct hostname and it's FQDN? If that works out, the only other thing I can suggest is try building your ESXi host and then manually running this script to ensure the domain join process works. When you're running it manually, you will need to update the "password" variable with the actual root password of your ESXi host. Give that a try and let me know if the domain join process works, you may also want to open vSphere Client so you can see the task and if there are errors.

Reply
0 Kudos
tdubb123
Expert
Expert

hostname does show the FQDN of the server.

/tmp # python joinActiveDirectory.py
Traceback (most recent call last):
  File "joinActiveDirectory.py", line 24, in <module>
    page = urllib2.urlopen(req).read()
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 121, in urlopen
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 380, in open
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 491, in http_response
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 412, in error
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 353, in _call_chain
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 817, in http_error_401
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 795, in http_error_auth_reqed
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 805, in retry_http_basic_auth
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 380, in open
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 491, in http_response
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 418, in error
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 353, in _call_chain
  File "/lib/python25-visor.zip/python2.5/urllib2.py", line 499, in http_error_default
urllib2.HTTPError: HTTP Error 401: Unauthorized
Reply
0 Kudos
tdubb123
Expert
Expert

I noticed that when i browse to

https://server/mob/?moid=ha-ad-auth&method=joinDomain

it is prompting for authentication. which is the local root password of the server.

Reply
0 Kudos
lamw
Community Manager
Community Manager

Yes, that is correct, it needs the local root password of the ESXi host which is exactly what the domain script is doing. Are you sure you're passing in the right credentials?

A 401 code generally means that you've not authenticated properly and you get the unauthorized error message.

Reply
0 Kudos
tdubb123
Expert
Expert

its failing to authenticate thats the problem.

the default password is not blank in my kickstart file. Its changed with the

rootpw --iscrypted $1$vP/2Aaii$p3x/2htjd5BragreMltBZ/

could this be a problem.

I tried adding the password to the python script (clear text) but still no go

Reply
0 Kudos
lamw
Community Manager
Community Manager

As I had already mentioned, the password is irreleveant during the build process, there is a post script that VMware runs to actually take the encrypted hash and update the actual root password. During the installation, there is no root password and thats why you're authenticating to the MOB with a blank for the root pass. To me it sounds like something is still occuring that is causing your 401 issue

Here is the kickstart snippet in my dev enviornment:

%firstboot --unsupported --interpreter=busybox
vim-cmd hostsvc/enable_remote_tsm
vim-cmd hostsvc/start_remote_tsm
vim-cmd hostsvc/enable_local_tsm
vim-cmd hostsvc/start_local_tsm

cat > /tmp/joinActiveDirectory.py << __JOIN_AD__
import sys,re,os,urllib,urllib2

# connection info to MOB
url = "https://localhost/mob/?moid=ha-ad-auth&method=joinDomain"
username = "root"
password = ""
domainname = "primp-industries.com"
ad_username = "administrator"
ad_password = "SuperDuperSecretPasswordYo"

#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__

python /tmp/joinActiveDirectory.py

Reply
0 Kudos
klich
Enthusiast
Enthusiast

Has anybody tried this on ESX Classic?

Seems to work fine on ESXi, but on classic I get this error "raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 403: Forbidden: Possible Cross-Site Request Forgery"

Thanks.

Reply
0 Kudos
tdubb123
Expert
Expert

i still cannot get it to work on esxi. For esxi I got the error you are getting in classic.

I am using 4.1u1 build 348481

Reply
0 Kudos
klich
Enthusiast
Enthusiast

I didn't track it to the exact patch, but one of the pre v4.1 Update 1 patches  added detection for "Possible Cross-Site Request Forgery"

I setup my mob to run over http, did some network captures, and determined that the following needed to be included:

1. Session cookie

2. Hidden form / value for 'vmware-session-nonce'

I'm not a python guru, so if you have a better way to do what I've done, please contribute back to this thread.

The following code is working for me (at least post-install, I'll trying during scripted installation shortly).

I sure hope this helps somebody else, as it was a challenge to resolve.

Enjoy!

import sys,re,os,urllib,urllib2,base64

### Define Variables ###

print 'Begin executing join domain script'

# 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
domainname = "my.domain.com/VMWare Server OU"

# Active Directory credentials with rights to join a computer to the domain
# create encoded password with the command: python -c "import base64; print base64.b64encode('mypassword')"
# note: escape special characters in your password with a \
# Feel free to improve this solution, but it at least keeps cleartext out of the file.
ad_username = "user@my.domain.com"
encodedpassword = "cGFzc3dvcmQ="
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
try:
  passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
  passman.add_password(None,url,username,password)
  authhandler = urllib2.HTTPBasicAuthHandler(passman)
  opener = urllib2.build_opener(authhandler)
  urllib2.install_opener(opener)
except IOError, e:
    if hasattr(e, 'reason'):
        print 'FAIL: HTTP Authentication'
        print 'Reason: ', e.reason
        sys.exit(1)
    elif hasattr(e, 'code'):
        print 'FAIL: HTTP Authentication'
        print 'Error code: ', e.code
        sys.exit(1)
else:
    print 'SUCCESS: Build HTTP Basic Authentication Handler'


### Code to capture required page data and cookie required for post back to meet CSRF requirements  ###
try:
  req = urllib2.Request(url)
  page = urllib2.urlopen(req)
  page_content= page.read()
except IOError, e:
    if hasattr(e, 'reason'):
        print 'FAIL: MOB Page Request'
        print 'Reason: ', e.reason
        sys.exit(1)
    elif hasattr(e, 'code'):
        print 'FAIL: MOB Page Request'
        print 'Error code: ', e.code
        sys.exit(1)
else:
  print 'SUCCESS: Request MOB page data'

# 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

try:

  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()
except IOError, e:
    if hasattr(e, 'reason'):
        print 'FAIL: MOB Page Request'
        print 'Reason: ', e.reason
        sys.exit(1)
    elif hasattr(e, 'code'):
        print 'FAIL: MOB Page Request'
        print 'Error code: ', e.code
        sys.exit(1)
else:
  print 'SUCCESS: Host joined to the domain'

Reply
0 Kudos
virtuallysi
Enthusiast
Enthusiast

Good work klich!  This worked for me.  Also, its nice that the AD password is no longer in clear text and you can specify an OU Smiley Happy

Reply
0 Kudos
lamw
Community Manager
Community Manager

Hi klich,

Thanks for sharing this information, I've gone ahead and updated my original log post with your modified code - http://www.virtuallyghetto.com/2011/02/automating-active-directory-domain-join.html

Reply
0 Kudos