This is the forth (and probably final!) post in a series on building/setting up/improving a PXE boot server with CentOS.
By the end of this post, we’ll have modified our kickstart file to allow copying of scripts and files to automate the process of our VM joining Active Directory. We’ll also automate creation of an AD group to allow specific users sudo
access on our VM.
Why would we want to join our VM to Active Directory? For a number of reasons! Joining an AD Domain allows us to authenticate with an AD account on the VM instead of using local accounts on the VM. It also allows us to control who has sudo
access via AD security groups, which I’m going to cover in this post.
A requirement for this to all work properly is of course a working Active Directory environment – we’ll need to have AD DS installed on a Windows Server VM, and DNS pointing towards our Domain Controller. We’ll also need an account within AD that is either a Domain Admin (not ideal) or a service account with delegated permissions to create computer objects and create group objects (ideal).
Creating our AD service account
To get things started, we’re going to want to create our AD service account for joining our VM’s to AD.
In my example, I’m creating a user with the username and full name set to LinuxADJoinService
. The user is created in the following location: ad.goatfarm.co.uk > Goatfarm Users > Service accounts. The users password is set to not expire and the user cannot change password.
Make note of the users password, we’ll need that soon. I generally use passwordsgenerator.net to generate passwords.

Next, we need to delegate control so the LinuxADJoinService
account can create computer objects. Inside of ADUC, find or create your OU for where the VM’s computer objects will be placed, right-click it and select Delegate Control…
Click Next on the first step of the wizard, and when prompted, click Add and find the account you just created. Hit Next again.

When prompted to select Tasks to Delegate select Create a custom task to delegate and click Next.
On the next page of the wizard, select Only the following objects in the folder, and then tick Computer Objects. At the bottom of the wizard, select Create selected objects in this folder and then click Next.

On the next page of the wizard, tick General and Creation/deletion of specific child objects. Within the Permissions box, find and tick Create all Child Objects and click Next.

Finally, click Finish and we’re done! We just need to do this one more time to allow creation of groups within our Security Groups OU – the process is the same, however instead of selecting Computer Objects, we select Group Objects.
Modifying our kickstart file
Time to SSH into our PXE server! Once again, we need to modify our kickstart file. This time, we need to add the required packages for domain joining and authentication to the packages section.
%packages
@base
@core
...
# Required for AD Authentication
sssd
realmd
oddjob
oddjob-mkhomedir
adcli
samba-common
samba-common-tools
krb5-workstation
openldap-clients
policycoreutils-python
...
%end
Next, we need to add a few lines to our post section to wget
our domain join script.
In the previous post, we added a line to edit the root crontab – we need to remove this and replace it with another line to call our domainjoin
script instead. The buildcleanup
script will be added to the root crontab as part of the domainjoin
script. Our post section should look something like the below:
%post --log=/tmp/post-build.log
...
#########################################
# Copy our domain join script
wget -O /tmp/build/domainjoin.sh ftp://10.176.40.10/centos7/config/domainjoin.sh
chmod +x /tmp/build/domainjoin.sh
echo "@reboot sleep 60 && /tmp/build/domainjoin.sh && /sbin/shutdown -r now" > /var/spool/cron/root
#########################################
# Copy our build cleanup script
wget -O /tmp/build/buildcleanup.sh ftp://10.176.40.10/centos7/config/buildcleanup.sh
chmod +x /tmp/build/buildcleanup.sh
...
%end
Create domain join script
We now need to create the domain joining script.
The script is basically pre-creating the computer account within AD in the location specified in the domain-ou
option with the machine password specified by one-time-password
, and then verifying the location of the pre-created computer object with the computer-ou
option then completing the join.
We then copy the sssd.conf
and domainsudoers
files. After this the domainsudoers
file gets edited via sed
with an entry for the security group that’s created so we can manage sudoers permissions via AD.
Finally, the root crontab is modified to run the buildcleanup
script after the next reboot.
Note that the adcli preset-computer
command requires the full OU path within AD (both the Organisational Unit and Domain Component parts), whereas the realm join
command only requires a partial OU path (Organisational Unit only).
Similarly, the adcli create-group
command requires the full OU path within AD (both the Organisational Unit and Domain Component parts)
#!/bin/bash
# Pre-create the computer account with a password
/sbin/adcli preset-computer --domain-ou="OU=Colo,OU=Goatfarm Servers,DC=ad,DC=goatfarm,DC=co,DC=uk" --login-user=LinuxADJoinService --stdin-password --one-time-password='SuperTempPassword' --domain=ad.goatfarm.co.uk $HOSTNAME.ad.goatfarm.co.uk <<< 'LinuxADJoinServicePassword'
# Complete the AD join process with the one-time-password and place the computer account in a specific location
/sbin/realm join --computer-ou="OU=Colo,OU=Goatfarm Servers" --one-time-password='SuperTempPassword' ad.goatfarm.co.uk
sleep 10
systemctl restart sssd
sleep 10
# Copy sssd.conf file
wget -O /tmp/build/sssd.conf ftp://10.176.40.10/centos7/config/sssd.conf
cp -rf /tmp/build/sssd.conf /etc/sssd/sssd.conf
# Copy and fix our domainsudoers file
wget -O /tmp/build/domainsudoers ftp://10.176.40.10/centos7/config/domainsudoers
sed -i s^'HOSTNAME'^"$HOSTNAME"^ /tmp/build/domainsudoers
cp -rf /tmp/build/domainsudoers /etc/sudoers.d/domainsudoers
# Create the AD sudoers security group related to this specific VM in a specific OU within AD
/sbin/adcli create-group ""$HOSTNAME" SUDOERS" --domain=ad.goatfarm.co.uk --domain-ou="OU=CentOS Sudoers,OU=Goatfarm Groups,DC=ad,DC=goatfarm,DC=co,DC=uk" --login-user=LinuxADJoinService --stdin-password <<< 'LinuxADJoinServicePassword'
# Add our buildcleanup script back into the root crontab
echo "@reboot sleep 60 && /tmp/build/buildcleanup.sh && /sbin/shutdown -r now" > /var/spool/cron/root
Our sssd.conf
and domainsudoers
files contain the following:
[sssd]
domains = ad.goatfarm.co.uk
config_file_version = 2
services = nss, pam
[domain/ad.goatfarm.co.uk]
ad_domain = ad.goatfarm.co.uk
krb5_realm = AD.GOATFARM.CO.UK
realmd_tags = manages-system joined-with-adcli
cache_credentials = True
id_provider = ad
krb5_store_password_if_offline = True
default_shell = /bin/bash
ldap_id_mapping = True
use_fully_qualified_names = False
fallback_homedir = /home/%u
access_provider = ad
%AD\\HOSTNAME\ SUDOERS ALL=(ALL) NOPASSWD:ALL
%AD\\CentOS\ Sudoers\ ALL ALL=(ALL) NOPASSWD:ALL
The sssd.conf
file is default apart from the fallback_homedir
value changed to not include %d
(domain).
The domainsudoers
file is modified by the sed
command inside the domainjoin
script to point towards the VM specific AD group that the domainjoin
script created earlier, and also an overall CentOS Sudoers ALL
AD group that exists within my AD.
We could probably get away with sed
-ing the sssd.conf
file, however replacing the file with our modified one accomplishes the same thing.
Bringing it all together
Once again, we’ve modified our kickstart a few times, so now would be a good time to run ksvalidator
against it to make sure there’s no errors.
PXE boot a VM, give it a hostname and IP details, and wait for it to complete the build – the VM should reboot twice, and once everything has complete, we’ll receive an email containing the randomly generated root password, and also to say the build has completed.

We should also see a new AD computer object created within our specified OU, and a new AD group created within the specified OU named after the VM’s hostname.


If we now try and login to the VM with an Active Directory account we’ll successfully login and have our home directory created for us.
After logging in, I’ve run the id
command to show that the user ad\centosadmin
is in the CentOS Sudoers ALL group, and in theory should have sudo/root permissions.

We can verify if we have sudo permissions by running sudo whoami
, if we have root returned, we’ve got sudo and everything is working as expected!

Similarly, if we was to login with an AD account that was in the B-PXEBOOT SUDOERS
AD group, we’d find that we also have sudo permissions.
Wrapping up
In this post I’ve covered creating and delegating permissions to an AD service account, modifying our kickstart file once more to install packages required for AD authentication, and creating a script to automate the process of joining a CentOS VM to Active Directory.
We’re now able to PXE boot a VM, select our kickstart file, feed it a hostname and IP details, and then leave it to build. Once the build has completed, we’re able to login via an AD account and gain sudo permissions (assuming the user is in the correct security group), and our randomly generated root password will be emailed to us.