Chapter 3
Managing Active Directory

Active Directory (AD) is at the heart of just about all modern organizations, both small and large. Microsoft first introduced AD with Windows 2000, where it replaced the domain structures previously implemented with Windows NT.

This chapter looks at how you can use PowerShell 7 to install, configure, and manage AD, as follows:

  • In “Establishing a Forest Root Domain,” you create the first domain in a new forest.
  • In “Installing a Replica DC,” you create a second domain controller in a domain.
  • In “Installing a Child Domain,” you create a child domain in the forest.
  • In “Creating a Cross‐Forest Trust,” you create another forest and implement and use a cross‐forest trust.
  • In “Managing AD Users, Computers, and OUs,” you add, remove, and manage AD users and AD computers and organize objects using organizational units.
  • In “Adding Users to AD via a CSV File,” you add users to the AD via a CSV file.
  • In “Configuring Just Enough Administration (JEA),” you set up delegated administration.

Since introducing Active Directory, Microsoft has expanded it to include a number of separate features you can install, as follows:

A full study of all these aspects could take up an entire book. This chapter covers the AD DS component and refers to it simply as “AD.”

Active Directory consists of a logical structure of forests and domains (which contain users and computers and other objects). AD also has a physical structure including AD sites, subnets, and replication partners. The physical architecture is transparent to end users; AD clients “just work,” via the magic of DNS name resolution and AD replication.

To enable administrators to deploy and manage AD, the Microsoft AD team has created two modules. You use commands in the AD Deployment module to build your forests and domains. You use the commands in the Active Directory module to manage the contents of the AD database (that is, managing users, computers, and so on).

In AD, a forest contains one or more domains in a hierarchy with a contiguous namespace (each child domain has a unique name plus the name of its parent). This contiguous namespace is known as a tree. A forest usually consists of a single tree but can contain multiple trees. The forest is a fundamental component of AD and is a key security boundary.

A domain is effectively a collection of objects including users, computers, groups, and so on. Domains enable you to support different sized organizations from small to large and globally distributed organizations. Best practice calls for a simple hierarchy with one or at most two levels of domains. This chapter demonstrates how to create and configure parent and child domains.

Each forest has a single forest root domain. That domain can have one or more subdomains, which in turn can contain subdomains. This allows you to have a forest with a forest root name; for example, Reskit.Org with a child domain such as UK.Reskit.Org.

Note that best AD naming practice suggests you name your AD forest root as a delegation of your registered Internet name. For example, if your organization has a registered name of Reskit.Org, then the AD should be named something like AD.Reskit.Org or Corp.Reskit.Org. That being said, the example code in this book uses shorter AD names, to produce shorter and simpler code. And although longer forest/domain names make scripts a bit easier to read and code, they also impose a small performance hit: every DNS/LDAP query is that little bit longer and the Distinguished Name of every object in the AD database is that little bit larger.

Best practice calls for each forest to be a single tree. But AD does allow you to add domain trees to a forest. This feature, first introduced with Windows Server 2000, was intended to assist large decentralized organizations. For example, you could add a separate domain tree, say Kapoho.Com, to the Reskit.Org forest. This would mean having two domain trees in a single AD Forest. You might consider this to support a subsidiary that needs a brand and email identity separate from the parent organization.

Placing noncontiguous namespace trees in a single forest is not a best‐practice solution, however, because AD has no prune‐and‐graft feature to enable you to prune a domain or domain tree from one forest and graft it into another, unrelated forest. Best practice, and a far simpler method, is to just deploy two independent forests and then connect them with a cross‐forest trust. These trusts are easy to remove as part of a company sale and allow the buyer to establish and utilize a new cross‐forest trust. This chapter also demonstrates how to create a cross‐forest trust.

Every domain in your forest has at least one domain controller (DC) and preferably more. A domain controller is a Windows Server system with the AD DS feature installed and configured that authenticates computers and users. DCs also provide user and computer Group Policy to domain client computers. DCs in each domain replicate with others so that any DC in a domain can authenticate a user or a computer. This chapter shows how to install a replica DC in a domain.

Within each domain you can define users and computers to represent individual computer users and the systems they access. AD enables an AD user to log on securely to any AD computer within the AD domain, subject to permissions. You can create AD groups, which can contain computers, users, and other groups. AD groups are invaluable for assigning permissions to resources in your domain as well as for delegating permissions in larger organizations. You can use organizational units to group users, computers, and group objects. This chapter shows how you can manage the user, group, and organizational unit objects.

AD is a complex subject. There are important aspects to AD that this book does not have space to cover, such as sites and subnets, AD replication, Global Catalog, and more. A great book on the wider subject is Brian Desmond's Active Directory: Designing, Deploying, and Running Active Directory (O'Reilly, 5th ed., 2013). Also, take a look at docs.microsoft.com/en-us/windows-server/identity/ad-ds/active-directory-domain-services for more information on AD.

Group Policy is an AD feature that enables you to define and deploy rich policies to configure a user or computer automatically. This book does not really cover Group Policy. An excellent book on the subject is Jeremy Moskowitz's Group Policy: Fundamentals, Security, and the Managed Desktop (Sybex, 3rd ed., 2015).

In this chapter, you use PowerShell 7 to build a forest with two domains (one of which has two DCs) and a second forest with one domain. You then create a cross‐forest trust between these two forests. Finally, you manage user, computer, and group objects and see how to add AD users to the domain with a comma‐separated value (CSV) file.

Systems Used in This Chapter

In this chapter, you use PowerShell 7 to install and manage Active Directory. This chapter makes use of the following host systems and forests/domains:

  • DC1.Reskit.Org and DC2.Reskit.Org: These are, initially, stand‐alone Windows Server 2019 hosts without any added features. You then install Active Directory on these two servers in a parent domain, Reskit.Org.
  • UKDC1.Reskit.Org: This is a single domain–joined host on which you install a child domain, UK.Reskit.Org. Once you complete the promotion of this server, the host name becomes UKDC1.UK.Reskit.Org.
  • KAPDC1.Kapoho.Com: This is a server you use to create the Kapoho.Com forest/domain to demonstrate establishing a cross‐forest trust.

Figure 3.1 shows the systems you use in this chapter and the two forests and their respective domains.

image

Figure 3.1: Forests, domains, and hosts used in this chapter

If you are using the Reskit build scripts (from github.com/doctordns/ReskitBuildScripts) to deploy your VMs, ensure you wait until DC1 is fully deployed as a DC before building DC2. The build scripts use setup XML for deploying DC2, UKDC1, and KAPDC1 that require the existence of the Reskit.Org forest.

Note that each host should have PowerShell 7, VS Code, and the Cascadia Code font installed. You can do that manually, using the scripts from Chapter 1, “Setting Up a PowerShell 7 Environment.”

Establishing a Forest Root Domain

To establish your first AD forest, you need to create a first domain controller in the forest. This DC is known as the forest root domain controller. Upon creation, this DC holds all the forest and domain Flexible Single Master Operation (FSMO) roles.

FSMO roles designate one specific domain controller to be the master for certain forest‐wide or domain‐wide operations. For example, the Schema Master FSMO role holder, initially the forest root DC, is where any AD Schema changes are first made. If you update your AD Schema, the tools you use make the updates on this DC. If a given FSMO role holder is offline, certain operations cannot take place until you either bring the role holder back online or move the FSMO role to another online DC. For more details on FSMO roles, see techgenix.com/fsmo-roles-in-active-directory/.

To promote DC1 to a domain controller in a new domain, you log on to the DC1 server as a local administrator. Then you run the PowerShell code shown in this section. Once you have installed AD, you need to reboot the server and log back in using the username RESKIT\Administrator along with the password you used when you installed DC1 (such as Pa$$w0rd or whatever password you chose to use for DC1's local administrator).

Before You Start

In this section you create the forest root domain controller in a new domain/forest. You perform this on DC1.Reskit.Org, a Windows Server 2019 Data Center workgroup host with the Desktop Experience option installed and a working Internet connection.

You also need to have installed PowerShell 7 on this host (and possibly more tools) using the scripts in Chapter 1.

Importing the Server Manager Module

The Server Manager module does not work natively in PowerShell 7, but you can use it using the Windows PowerShell compatibility solution discussed in Chapter 2, “PowerShell 7 Compatibility with Windows PowerShell.” To access the commands in the module, you first import it manually using the Import‐Module command.

# 1. Explicitly Load the Server Manager Module
Import-Module ServerManager -WarningAction SilentlyContinue

With this command, you import the Server Manager module and avoid generating the warning message that Import‐Module generates. Once imported, the commands in the module are available for use.

Installing the AD Domain Services Feature

With the Server Manager module imported, you can now add the AD Domain Services feature to DC1. Note that doing so does not configure DC1 as a domain controller. Rather, it installs the components that enable you to install a DC in whatever role is appropriate (as the first DC in a forest, the first DC in a child domain, or a DC in an existing domain).

Use the Install‐WindowsFeature command to add the necessary components to the server.

# 2. Install the AD Domain Services feature and management tools
$FEATUREHT = @{
  Name                   = 'AD-Domain-Services'
  IncludeManagementTools = $True
  WarningAction          = 'SilentlyContinue'
}
Install-WindowsFeature @FEATUREHT

This code produces the output shown in Figure 3.2.

image

Figure 3.2: Installing AD DS Domain Services

As you can see in the figure, you do not need to reboot the server before continuing.

Loading the AD DS Deployment Module Explicitly

In the previous step, you installed the ADDSDeployment module as part of installing the AD‐Domain‐Services feature. This module is not supported by PowerShell 7 natively but does work using the compatibility mechanism described in Chapter 2. You load the module with the Import‐Module command.

# 3. Import the AD DS Deployment Module
Import-Module -Name ADDSDeployment -WarningAction SilentlyContinue

Because this module is supported only via the Windows PowerShell compatibility mechanism, importing the module generates by default a warning message explaining that the module has been loaded using the compatibility mechanism. But this is a message you can safely ignore. To avoid this message, you set the value of WarningAction to SilentlyContinue.

Creating a Forest Root Domain Controller

With the AD DS feature installed, you create a new AD forest by promoting your server, DC1, to be a DC in a new forest/domain, Reskit.Org. Since this is the first DC in the domain, the DC becomes the forest root DC. You initiate the promotion process by performing the following:

# 4. Install Forest Root Domain and DC
$ADINSTALLHT = @{
  String      = 'Pa$$w0rd'
  AsPlainText = $True
  Force       = $True
}
$SECUREPW = ConvertTo-SecureString @ADINSTALLHT
$ADHT = @{
  DomainName                    = 'Reskit.Org' # Forest Root
  SafeModeAdministratorPassword = $SECUREPW
  InstallDNS                    = $True
  DomainMode                    = 'WinThreshold' # latest
  ForestMode                    = 'WinThreshold' # Latest
  Force                         = $True
  NoRebootOnCompletion          = $True
  WarningAction                 = 'SilentlyContinue'
}
Install-ADDSForest @ADHT

By setting the WarningAction parameter to SilentlyContinue, the Install‐ADDSForest command generates no warning messages. The command normally generates a number of warning messages, but they are benign. If you are using this snippet in production, you may want to view the error messages if only to satisfy yourself that they are indeed benign in your environment.

You can see the output from this code in Figure 3.3. These commands promote DC1 to be a DC, but you need to reboot to complete the installation. This would enable you to carry out any other necessary configuration on the DC before the required reboot. Depending on your environment, you could instead not set the NoRebootOnCompletion parameter and the reboot would occur automatically.

This example shows a great use of hash tables with PowerShell commands. A hash table is a set of key/value pairs that PowerShell treats as a single object. You add parameter names (keys) and their values to the hash table and then call the cmdlet passing only the hash table object. This makes the source code a lot easier to read (and is a common feature this book uses for code snippets). For more details on hash tables, see docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-7.

image

Figure 3.3: Promoting DC1

Restarting the Computer

To complete the installation of the new forest, you need to reboot the server. You do this using Restart‐Computer as follows:

# 5. Restart computer
Restart-Computer -Force

Viewing the Directory Server Entry ( DSE)

After the server, DC1.Reskit.Org, has rebooted, log in to the server (as the domain administrator Reskit\Administrator) and examine details of the forest by viewing the Root Directory Services Entry, or Root DSE. The Root DSE is the root of your directory tree, and Get‐ADRootDSE provides information about a directory server.

# 6. After reboot, log back into DC1 as Reskit\Administrator
Get-ADRootDSE |
  Format-Table -Property DNS*, *Functionality 

You can get the Root DSE for any standards‐based LDAP server, including Windows Active Directory. You can think of the DSE as a road map of what's inside this DC and the features it supports.

An important security point is that the Root DSE is provided without requiring any authentication. If you have DCs exposed to the Internet, they provide their information anonymously. This could enable an attacker to learn more about your domain structure, and for that reason exposing DCs on the Internet is to be avoided.

As you can see in Figure 3.4, our host now has a fully qualified host name of DC1.Reskit. Org, is a DC in a Windows domain and forest, and has both domain and forest functionality set at the Windows 2016 level. There were no additional AD features in Windows Server 2019, so this setting means that all existing features are available.

image

Figure 3.4: Viewing the DSE

Viewing Details of the New AD DS Forest

You can use the Get‐ADForest command to view details of the new Reskit.Org forest, including forest‐level FSMOs, the servers presently acting as a Global Catalog (GC), and the set of domains in the forest by using the following:

# 7. Examine ADDS forest
Get-ADForest | 
  Format-Table -Property *master*, global*, Domains

Figure 3.5 shows the output of this code.

image

Figure 3.5: Viewing the Reskit.Org forest

A GC provides access to a partial replica of all objects in a given forest and is used for lookups. Exchange Server, for example, makes heavy use of GC servers. This page provides some additional detail on the role of a GC: docs.microsoft.com/en-us/windows/win32/ad/global-catalog.

In this figure, you can see that all forest‐level FSMO roles are held by DC1.Reskit.Org. As you deploy more DCs, you can move the forest‐level FSMOs as needed. In general, forest‐level FSMOs should be held by DCs at your network core.

Getting Details of the Domain

You can view details of the Reskit.Org domain using the Get‐ADDomain command. You may find that some of the default output provided by Get‐ADDomain is not very useful, so you can refine the information displayed as follows:

# 8. View details of the domain
Get-ADDomain | 
  Format-Table -Property DNS*, PDC*, *master, Replica*

You can view the output from this step in Figure 3.6. This shows that, so far, there is only one domain in the forest and one DC in that domain.

image

Figure 3.6: Viewing the Reskit.Org domain

In this figure, you can see that all domain‐level FSMO roles are held by DC1.Reskit.Org. As you deploy more DCs, you can move FSMO roles as needed. As with forest‐level FSMOs, you probably want the domain FSMOs to be held centrally. If you have multiple levels of domains, you may want to move domain‐level FSMOs “nearer” to the center of each domain. Ultimately, you need to ensure that DCs can locate and connect to all FSMO role holders.

Viewing DNS Settings

When you promoted DC1.Reskit.Org as a DC, in “Creating a Forest Root Domain Controller,” the promotion process added a DNS server on DC1, created a zone for your domain, and populated a number of DNS resource records (RRs). You can view the DNS details by using the following steps:

# 9. View DNS Settings 
Get-Service -Name DNS
Get-DnsServerZone 
Get-DnsServerResourceRecord -ZoneName 'Reskit.Org'

As you can see in Figure 3.7, the DNS Server service is up and running on DC1. You can see the zones created in this DNS server, including the Reskit.Org DNS domain. Finally, you can see the various DNS RRs that the AD service creates and uses. If you are using multiple virtual NICs in your VM, you will see more resource records.

image

Figure 3.7: Viewing DNS settings and configuration

In AD, the Netlogon service writes the AD‐related DNS RRs to DNS each time the service starts and every 24 hours thereafter. You can test this self‐healing feature by stopping the Netlogon service, removing the AD‐related RRs, and then restarting the service. You should see that the necessary RRs are now restored. Of course, be careful when testing this on a production domain controller.

Installing a Replica DC

In “Establishing a Forest Root Domain,” you created an AD domain with a single DC. In test environments, this might be more than adequate. For production, best practice suggests having more than one DC. If you are using virtualization to host domain controller VMs, you should ensure that AD VMs are hosted on independent virtualization hosts (irrespective of the virtualization platform). This avoids a single point of failure.

Before You Start

In this section you add a domain‐joined server, DC2.Reskit.Org (built using Windows Server 2019), to the Reskit.Org domain. If you are using the Reskit build scripts to deploy this DC, make sure you do not build the DC2 VM until after you have finished promoting DC1 to be a domain controller.

As with other servers you use in this chapter, you should have installed PowerShell 7 on DC2. To install PowerShell 7 and optionally VS Code, you can use the scripts in Chapter 1.

Importing the Server Manager Module

You start the process of promoting DC2 by loading the Server Manager module. This module is not supported natively by PowerShell 7. The command, however, works using the Windows PowerShell compatibility mechanism mentioned in Chapter 2.

You install it using the Import‐Module command.

# 1. Import the Server Manager module
Import-Module -Name ServerManager -WarningAction SilentlyContinue

By specifying the ‐WarningAction parameter, you avoid seeing the warning message that would be generated to warn you that this module is being used in a compatibility session.

Checking Network Connectivity

Before promoting the server DC2.Reskit.Org to be a DC, you need to ensure that another DC is online and can be reached. If this fails, it means that promotion is not going to succeed. Tests you can run prior to promoting the server include these:

# 2. Check DC1 can be resolved and can be reached from DC2
Resolve-DnsName -Name DC1.Reskit.Org -Type A
Test-NetConnection -ComputerName DC1.Reskit.Org -Port 445
Test-NetConnection -ComputerName DC1.Reskit.Org -Port 389

You can see the output of these commands in Figure 3.8.

image

Figure 3.8: Testing connectivity with DC1

In the figure, you can see that DC2 is able to connect to resolve DC1's IP address and then connect over ports 445 and 389. Typical issues are related to IP addressing/configuration and DNS. And if you are using virtualization, the virtualization network configuration can also be an issue.

Adding the AD DS Features on DC2

Another prerequisite step is getting the relevant features onto your host, using Install‐WindowsFeature as follows:

# 4. Add the AD DS features on DC2
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools

Running the Install‐WindowsFeature cmdlet generates the output you see in Figure 3.9.

image

Figure 3.9: Installing Windows features

Promoting DC2

To promote DC2, you run the following commands:

4. Promote DC2
Import-Module -Name ADDSDeployment -WarningAction SilentlyContinue
$URK    = "Administrator@Reskit.Org" 
$PW     = 'Pa$$w0rd'
$PSS    = ConvertTo-SecureString -String $PW -AsPlainText -Force
$CredRK = [PSCredential]::New($URK,$PSS)
$INSTALLHT = @{
  DomainName                    = 'Reskit.Org'
  SafeModeAdministratorPassword = $PSS
  SiteName                      = 'Default-First-Site-Name'
  NoRebootOnCompletion          = $true
  InstallDNS                    = $false
  Credential                    = $CredRK
  Force                         = $true
  } 
Install-ADDSDomainController @INSTALLHT | Out-Null 

These commands promote DC2 to be a domain controller. Once the command has completed its work, you must reboot the system to complete the promotion process. The installation command you use to promote DC2 can generate warning messages that refer to Knowledge Base article 942564. In general, these are benign errors, and you can ignore them.

Rebooting DC2

To finalize the promotion process, restart DC2, like this:

# 5. Reboot manually
Restart-Computer -Force

This command restarts the system. If you had unsaved work, possibly in another window, you may have lost it.

Reviewing DCs in Reskit.Org Domain

After the reboot process has completed, you can log in to DC2 as the domain administrator. One of the first things you can do is to check to see which computers are now in the Domain Controllers organizational unit, with these commands:

# 6. Check DCs in Reskit.Org
$SB = 'OU=Domain Controllers,DC=Reskit,DC=Org'
Get-ADComputer -Filter * -SearchBase $SB |
  Format-Table -Property DNSHostname, Enabled

The output, which you can see in Figure 3.10, shows that you now have two DCs in the Domain Controllers OU and by implication in the domain.

image

Figure 3.10: Viewing DCs in the Reskit.Org domain

Viewing the Reskit.Org Domain

In addition to verifying that you have a second DC, another useful test is to view the AD's domain details using Get‐ADDomain, like this:

# 7. View Reskit.Org Forest
Get-ADDomain |
  Format-Table -Property Forest, Name, Replica*

These commands return the name of the forest, the name of the domain, and the DCs in the domain, as shown in Figure 3.11.

image

Figure 3.11: Viewing DNS settings and configuration

As you can see in the figure, you now have a second working domain controller in the Reskit.Org domain. There are a variety of other tests you might want to do before proceeding, including checking the various network ports on DC2, validating DNS entries, and ensuring that replication is happening between the two DCs.

Installing a Child Domain

For many organizations, a single domain forest with multiple DCs is adequate and has the benefit of being simple to manage. But in some cases, having one or more child domains can be advantageous. For example, you might have two parts of your organization that are geographically distant. An advantage of using a child domain is that some parts of AD are not replicated between domains, which can reduce WAN usage. For geographically dispersed organizations, you also have the option of using a single domain and placing DCs in separate AD sites.

In this section, you create a new child domain, UK.Reskit.Org, as a child of the Reskit.Org domain you set up earlier in this chapter.

Before You Start

In this section you extend the Reskit.Org forest (set up in “Establishing a Forest Root Domain”) by adding a child domain named UK.Reskit.Org with a single DC, named UKDC1.UK.Reskit.Org. This host is built using Windows Server 2019. You also need to install and configure PowerShell 7 on the host and optionally install VS Code using the scripts in Chapter 1.

Importing the Server Manager Module

To add the AD DS tools onto the host, you need to use the Server Manager module. This module is one that Import‐Module detects as not supported natively in PowerShell 7 and makes use of the compatibility solution described in Chapter 2. You load the module as follows:

# 1. Import the ServerManager module
Import-Module ServerManager -WarningAction SilentlyContinue

This command loads the Server Manager module using the Windows PowerShell compatibility mechanism discussed in Chapter 2. It creates a PowerShell remoting session, loads the module in that remote session, and creates local proxy functions for the commands in the Server Manager module. You can view the remoting session details using the Get‐PSSession command.

Verifying That DC1 Can Be Resolved

To create the new subdomain, your server ( UKDC1) needs to have connectivity to the parent domain. In particular, this host needs access to the Domain Naming Master FSMO forest role holder in the parent domain. If you are following the steps so far in this chapter, that server would be DC1.Reskit.Org. Before promoting the server, it is useful to test that you can connect to that server, using Test‐NetConnection as follows:

# 2. Check DC1 can be resolved and can be reached over 445 and 389 from UKDC1
Resolve-DnsName -Name DC1.Reskit.Org -Type A
Test-NetConnection -ComputerName DC1.Reskit.Org -Port 445
Test-NetConnection -ComputerName DC1.Reskit.Org -Port 389

You can see the output from these commands in Figure 3.12. It shows that you can resolve the parent DC and can connect to it via both port 445 and port 389.

image

Figure 3.12: Verifying connectivity to DC1

If for any reason you cannot connect to the parent domain, you need to troubleshoot and resolve that issue. Without connectivity to the parent, the new domain cannot be created. Typical issues with connection failure include network configuration and DNS. And in production, ensure that you have and use the correct credentials.

Adding the AD DS Features to UKDC1

Before you can make UKDC1 a domain controller, you must add the AD Domain Services features to the server, as follows:

# 3. Add the AD DS features on UKDC1
$Features = 'AD-Domain-Services'
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools

You can view the output from installing this feature in Figure 3.13.

In production, the output from the Install‐WindowsFeature command is probably not very helpful. If anything, it's more useful when it fails. When it succeeds, you can largely ignore the output and proceed to the next step. If it fails, you need to discover why and resolve the issue. Once the issue is resolved, you can retry this step.

image

Figure 3.13: Adding features to UKDC1

Creating the Child Domain

You create the new domain as follows using the Install‐ADDSDomain command, like this:

# 4. Create New Domain
Import-Module -Name ADDSDeployment -WarningAction SilentlyContinue
$URK    = "Administrator@Reskit.Org" 
$PW     = 'Pa$$w0rd'
$PSS    = ConvertTo-SecureString -String $PW -AsPlainText -Force
$CredRK = [PSCredential]::New($URK,$PSS)
$INSTALLHT    = @{
  NewDomainName                 = 'UK'
  ParentDomainName              = 'Reskit.Org'
  DomainType                    = 'ChildDomain'
  SafeModeAdministratorPassword = $PSS
  ReplicationSourceDC           = 'DC1.Reskit.Org'
  Credential                    = $CredRK
  SiteName                      = 'Default-First-Site-Name'
  InstallDNS                    = $false
  Force                         = $true
}
Install-ADDSDomain @INSTALLHT

These commands promote UKDC1 to be a new DC in a child domain. Once the installation is complete, the machine reboots to complete the configuration of UKDC1.

Viewing the Updated AD Forest

After the server reboots, you can log in. You have the option of logging in as either Reskit\Administrator or UK\Administrator. That demonstrates the ability to log into a child domain with credentials from another domain in the forest.

If you log in as UK\Administrator, you can view the details of the UK.Reskit.Org domain as follows:

# 5. Look at AD forest
Get-ADForest -Server UKDC1.UK.Reskit.Org

It's worth taking a moment to study the output from this step, which you can see in Figure 3.14.

image

Figure 3.14: Viewing the Reskit forest

In this figure, you see that the forest now has two domains ( Reskit.Org and UK.Reskit.Org). Additionally, both forest‐wide FSMO roles are held by DC1.Reskit.Org (in the parent domain).

Viewing the Child Domain

You can also view the details of the newly created UK.Reskit.Org domain using the Get‐ADDomain command, as follows:

# 6. Look at the UK domain
Get-ADDomain -Server UKDC1.UK.Reskit.Org

Figure 3.15 shows the details for the UK.Reskit.Org domain.

This output provides more details about the newly created child domain. You can see that the domain‐wide FSMO role holders all point to this newly created DC. In production you would want a second DC (or more as appropriate) and may need to move the FSMO roles. This output also shows further domain‐wide configuration information.

Once you have completed these steps, you have a working child domain ( UK.Reskit.Org) in addition to the parent domain ( Reskit.Org).

image

Figure 3.15: Viewing the child domain information

Configuring a Cross‐Forest Trust

In this chapter thus far, you have installed a forest with two domains, namely, Reskit.Org and the child domain UK.Reskit.Org. In most organizations a one‐ or two‐level domain structure is best practice. That means either a single‐domain forest or a forest with a single root domain and one or more child domains.

Having a two‐level domain structure with multiple child domains has some advantages for large distributed organizations. In particular, separate child domains can help to avoid replication between domains (which typically means additional WAN traffic).

While you can have multiple domain trees within a single forest, that is usually not a good idea. In most cases, the desire for multiple noncontiguous domain trees arises from the need of different parts of the organization to have their own domains (and therefore email and FQDN server names). For example, if a large conglomerate bought a company with a strong brand identity, it might make commercial sense to keep that separate with a separate domain tree.

However, because you cannot prune and graft parts of your AD forest between other external forests, when you buy or sell that strongly named subsidiary, a buyer might face challenges integrating the old attached domain/forest into the new environment. A much better approach is to create two separate and independent forests and then implement a cross‐forest trust. A cross‐forest trust means that you can use accounts in one domain in the access control list (ACL) of a resource in a different domain to support resource access between different forests.

In this section, you create a new forest ( Kapoho.Com) on a newly installed server ( KAPDC1). You create this server initially as a workgroup server and then promote it to be a DC in a new forest. Once this new forest is created, you can create and leverage the cross‐forest trust.

Before You Start

In this section, you create a cross‐forest trust between the Kapoho.Com and Reskit.Org forests and then use this trust to update ACLs to facilitate cross‐forest resource access. To achieve this, you need to have created the domain controllers in the two forests ( DC1.Reskit.Org and KAPDC1.Kapoho.Org). You run the steps in this section on KAPDC1. As with other servers, you need to install PowerShell 7 and optionally VS Code on this server. You can use the scripts in Chapter 1 to do so.

Importing the Server Manager Module

To add the AD DS tools onto the host, you need to use the Server Manager module. This module is one that Import‐Module detects as not supported natively in PowerShell 7 and makes use of the compatibility solution described in Chapter 2. You load the module using Import‐Module as follows:

# 1. Import the ServerManager module on KAPDC1
Import-Module ServerManager -WarningAction SilentlyContinue

This command loads the module using the Windows PowerShell compatibility mechanism described in Chapter 2. In doing so, it creates a PowerShell remoting session to the local host, loads the module in that remote session, and creates local proxy functions for the commands in the Server Manager module. You can view the remoting session details by using the Get‐PSSession command.

Installing the AD Domain Services Feature and Management Tools

You can now install the AD Domain Services feature, including the necessary management tools, as follows:

# 2. Install the AD Domain Services feature and Management Tools
$Features = 'AD-Domain-Services'
Install-WindowsFeature -Name $Features -IncludeManagementTools 

This step produces the output you can see in Figure 3.16.

image

Figure 3.16: Installing the AD DS feature

This command adds the AD Domain Services to the host but does not promote the host to be a DC. You promote KAPDC1 to be a new DC in a separate step (“Promoting KAPDC1”).

Testing Network Connectivity with DC1

To create the cross‐forest trust, KAPDC1.Kapoho.Com needs to be able to connect with DC1.Reskit.Org. So, before trying to create the trust, you test this connectivity between the two DCs, as follows:

# 3. Test Network Connectivity with DC1
Test-NetConnection -ComputerName DC1

Assuming DC1 and KAPDC1 are both online and working, the output you see should look like Figure 3.17.

image

Figure 3.17: Testing network connectivity

You can extend these tests to verify full network connectivity using relevant ports such as 389 and 445.

Importing the AD DS Deployment Module

To promote KAPDC1 to be a DC in the Kapoho.Com domain, you need to import the AD DS Deployment module as follows:

# 4. Import the AD DS Deployment Module
Import-Module -Name ADDSDeployment -WarningAction SilentlyContinue

This command imports the module using the Windows Compatibility mechanism described in Chapter 2. Once you load the module, you can use Get‐Command to view the proxy commands created.

Promoting KAPDC1

Next, you promote KAPDC1 to be a forest root DC in the Kapoho.Com forest/domain, as follows:

# 5. Promote KAPDC1 to be DC in its own forest
$ADINSTALLHT = @{
  String      = 'Pa$$w0rd'
  AsPlainText = $True
  Force       = $True
}
$SECUREPW = ConvertTo-SecureString @ADINSTALLHT
$ADINSTALLHT = @{
  DomainName                    = 'Kapoho.Com' # Forest Root
  SafeModeAdministratorPassword = $SecurePW
  InstallDNS                    = $True
  DomainMode                    = 'WinThreshold' # latest
  ForestMode                    = 'WinThreshold' # Latest
  Force                         = $True
  WarningAction                 = 'SilentlyContinue'
}
Install-ADDSForest @ADINSTALLHT | Out-Null

This step produces no output. Because you did not use the ‐NoRebootOnCompletion parameter, once the promotion process has completed, the cmdlet reboots to finalize the server to be a DC. Specifying this parameter means that no reboot happens automatically; thus, you can control when the reboot happens. In production, you might be doing other operations in parallel with promoting the server, such as copying some files or configuring applications on the server. This parameter allows you to reboot when appropriate. Note that until you reboot, the new domain is not available.

View Kapoho.Com Forest Details

After rebooting, you can log in as the administrator in the new domain using either Administrator@Kapoho.Com or Kapoho\Administrator. Once logged in, it is useful to review and verify details of the new forest. You can view the forest details as follows:

# 6. View Kapoho.Com Forest Details
Get-ADForest

The output from this command, which you see in Figure 3.18, shows key details about the new forest. It is useful to validate that the DC has been created and promoted as you expect and that the relevant FSMO roles are set.

image

Figure 3.18: Viewing details of the Kapoho.Com forest

With the steps so far in this section, you have created another forest and domain. At present, there is no relationship between the two forests; thus, there is no cross‐forest resource access yet. There are a number of further steps you need to take to implement a cross‐forest trust.

Adjusting the DNS to Resolve Reskit.Org from KAPDC1

With two independent forests, you need to ensure that DNS clients in both forests are able to resolve hosts in the other forest. A simple way to do that is to create a conditional DNS forwarder on each forest's DNS server(s). That way, a DHCP client in Reskit.Org can resolve details of hosts in the Kapoho.Com zone by way of the forwarder. To do this locally on KAPDC1, you run the following:

# 7. Adjust DNS on KAPDC1 to resolve Reskit.Org from DC1
$CFHT = @{
   Name          = 'Reskit.Org'
   MasterServers = '10.10.10.10' 
   Passthru      = $True
}
Add-DnsServerConditionalForwarderZone @CFHT

You can see the output of this command in Figure 3.19.

By performing this step, you configure the DNS server on KAPDC1 to resolve Reskit.Org‐related addresses by forwarding requests to the DNS server at 10.10.10.10, which is DC1.Reskit.Org.

image

Figure 3.19: Adding a DNS conditional forwarder

This step demonstrates one of two methods you can deploy to implement cross‐forest DNS lookups. The other method is to use stub zones. Each method has some advantages and disadvantages; for more information, see this article: winadmin.org/questions-answers/2-windows/2-windows/5-what-is-the-difference-between-stub-zone-and-conditional-forwarders-when-are-they-used-2.

Testing Conditional DNS Forwarding

With conditional DNS forwarding set up on KAPDC1, you test it using Resolve‐DNSName. To attempt to resolve the IP address of the DC1.Reskit.Org server, enter the following:

# 8. Test Conditional Forwarding
Resolve-DNSName -Name DC1.Reskit.Org -Type A

As you can see in the output in Figure 3.20, DC1 has an A resource record for the IPv4 address of DC1.

image

Figure 3.20: Testing conditional DNS forwarding

Setting Up a Conditional Forwarder on Reskit.Org

With the conditional forwarder set up on KAPDC1.Kapoho.Com, you can now set up and validate a forwarder on DC1.Reskit.Org to forward queries for Kapoho.Com to KAPDC1.Kapoho.Com, as follows:

# 9. Create a Script Block to Add Conditional Forwarder on DC1
$SB = {
  # Add CF zone
  $CFHT = @{
    Name          = 'Kapoho.Com'
    MasterServers = '10.10.10.131' 
   }
  Add-DnsServerConditionalForwarderZone @CFHT
  # Test it
  Resolve-DNSName -Name KAPDC1.Kapoho.Com | Format-Table
}  

These instructions create a script block that you use to add a conditional forwarder on DC1 to the DNS service on KAPDC1.

Create Credentials to Run a Command on DC1

Because DC1 is in a separate AD forest, you have to create a credentials object you can use to run the script block on DC1, as follows:

# 10. Create Credentials to Run A Command on DC1
$URK   = 'Reskit\Administrator'
$PRK   = ConvertTo-SecureString 'Pa$$w0rd' -AsPlainText -Force
$CREDRK =  [PSCredential]::New($URK,$PRK)

Setting WinRM

To run the script block remotely, on a system in another Kerberos realm, you need to adjust the WinRM service. To enable WinRM to run the script block, you need to update the Trusted Hosts list. This list defines which hosts you can connect to (without using Kerberos). You do this as follows:

# 11. Set WinRM
$PATH = 'WSMan:\localhost\Client\TrustedHosts'
Set-Item -Path $PATH -Value '*.Reskit.Org' -Force

After running this snippet, your system trusts any server in the Reskit.Org domain as being who it says it is. This is a potential security risk, and you should consider whether wildcard values like this are acceptable. Once you've demonstrated the capability for yourself, you may want to disable it on your system.

Invoking the Script Block on DC1

Now that you have created a script block and configured the environment, you can run the script block on the remote server as follows:

# 12. Run the Script Block on DC1
$NZHT = @{
  Computername = 'DC1.Reskit.Org'
  Script       = $SB
  Credential   = $CREDRK
}
Invoke-Command @NZHT 

In the output, shown in Figure 3.21, you can see that after setting up conditional forwarding, a DNS client on DC1 is able to resolve a RR for a host in the Kapoho.Com domain. Depending on how you have configured Internet access and whether you have IPv6 enabled, you may see additional RRs resolved.

image

Figure 3.21: Setting up a conditional forwarder on DC1

Getting the Domain Detail Objects

The AD modules do not support creating a cross‐forest trust. To set up a cross‐forest trust, you must make use of .NET objects and their methods.

To set up the cross‐forest trust, first get the NET objects representing each of the two forests, as follows:

# 13. Get Reskit.Org and Kapoho.Com details
$Reskit       = 'Reskit.Org'
$User         = 'Administrator'
$UserPW       = 'Pa$$w0rd'
$Type         = 'System.DirectoryServices.' +
                'ActiveDirectory.DirectoryContext'
$RKFHT = @{
  TypeName     = $Type
  ArgumentList = 'Forest',$Reskit,$User,$UserPW
}                
$RKF          = New-Object @RKFHT
$ReskitForest = 
  [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($RKF)
$KapohoForest = 
  [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()

This snippet creates variables that contain details of the two forests you are working with. These forest objects also have useful methods that are not available via PowerShell commands.

Since you store the output from these last two method calls in variables, you see no output.

The .NET namespace System.DirectoryServices.ActiveDirectory contains objects that represent the key AD components, including forest, domain, site, subnet, partition, and schemas. These classes have properties and methods that perform a variety of AD tasks. In many cases, these classes and methods overlap with commands in the Active Directory module. The classes do, however, provide properties and methods not available in the AD modules.

Viewing the Reskit Forest Details

Now that you have obtained the forest details for the Reskit.Org forest, you can display the information as follows:

# 14. View Reskit.Org Forest Details 
$ReskitForest

This produces the output shown in Figure 3.22.

image

Figure 3.22: Viewing the Reskit.Org forest

Viewing the Kapoho Forest Details

You can also view the details of the Kapoho.Com forest, using similar syntax:

# 15. View Kapoho Forest Details
$KapohoForest

The output, which you can see in Figure 3.23, is similar to that of the Reskit forest.

image

Figure 3.23: Viewing the Kahopo.Com forest

Establishing a Cross‐Forest Trust

Now that you have the objects representing the two forests, you can establish a trust between the forests, using the CreateTrustRelationship() method as follows:

# 16. Establish a trust
$KapohoForest.CreateTrustRelationship($ReskitForest,"Bidirectional")

In this case, you are establishing a bidirectional trust, meaning that both domains now trust each other; you can now specify that security principals in either domain can access resources in the other domain.

Creating a Script Block to Adjust the ACL of a File on DC1

With the cross‐forest trust set up, you can now make use of the trust. In particular, the trust means you can create a file (on DC1) and adjust the ACL to allow access both from members of the Reskit.Org forest and from users in the Kapoho.Com forest.

The simplest way to set ACLs on NFTS files and folders is to use the external NTFSSecurity module. You download this module from the PowerShell Gallery using the Install‐Module command. After installing the module, you can create a file (on DC1) and configure the ACL to enable cross‐forest access.

You do this by first creating a script block, as follows:

# 17. Create SB to Adjust ACL on DC1
$SB2 = {
  # Ensure NTFSSecurity module is loaded on DC1
  Install-Module -Name NTFSSecurity -Force -ErrorAction SilentlyContinue
  # Create a file in C:\Foo
  'XFT Test' | Out-File -FilePath 'C:\Foo\XFTTEST.Txt'
  # Test ACL
  Get-NTFSaccess -Path C:\Foo\XFTTEST.Txt | Format-Table
  # Add Kapoho\Administrators into ACL for this file
  $NTHT = @{
    Path         = 'C:\Foo\XFTTEST.TXT'
    Account      = 'Administrator@Kapoho.Com'
    AccessRights = 'FullControl'
  }
  Add-NTFSAccess @NTHT
  # Retest ACL
  Get-NTFSaccess -Path C:\Foo\XFTTEST.Txt | Format-Table
}

This code snippet creates a script block (on KAPDC1), which creates a new file (on the remote system) and then looks at the initial ACL. After adding the Kapoho\Administrator to the ACL of this file, the script block retrieves and displays the updated ACL.

Running the Script Block on DC1 to Demonstrate the Cross‐Forest Trust

To view the cross‐forest trust in action, you can run the script block as follows:

# 18. Run the ScriptBlock on DC1 To Demonstrate X-Forest Trust
$PHT = @{
  ComputerName = 'DC1.Reskit.Org'
  Credential   = $CREDRK
  ScriptBlock  = $SB2
}
Invoke-Command  @PHT

Note that you need to ensure that connectivity between KAPDC1 and DC1. Figure 3.24 shows the output of this step. The NTFS Security cmdlets produce an extra white space in the output. You can avoid that by piping the output of, for example, Get‐NTFSAccess to Format‐Table and specifying ‐AutoSize.

In practice, you should be using the AGDLP (account, global, domain, local permissions) approach to setting permissions (see en.wikipedia.org/wiki/AGDLP for more information on this approach). The mechanism shown in this section works well but may not be as scalable. For larger enterprises, the AGDLP approach is easier to deploy and manage.

With AGDLP, you place user and group accounts that need to access resources into global groups in each forest/domain. Then you add those global groups into Domain Local groups on each domain. Once these Domain Local groups are created, you can assign permissions to a resource in either forest based on the Domain Global groups in each forest. In this book's scenario, you create a Domain Local group in the Reskit domain and then add a global group from the Kapoho domain to it.

image

Figure 3.24: Updating the ACL and viewing its details

Managing AD Users, Computers, and OUs

Once you have your Active Directory infrastructure built, the next step in deployment is to add objects to the directory and subsequently manage them.

There are four sets of AD objects you are most likely to use.

  • Users
  • Computers
  • Groups
  • Organizational units

An AD user object represents a user account that can be used to log on and can be used in resource ACLs. An AD computer object represents a computer that can log in to the domain and onto which a user can log. AD computers can also be used in ACLs. An AD group is an account that contains other user, computer, or group objects. To simplify ACL management, you use a group in an ACL instead of individual users/computers. An organizational unit (OU) is an AD object that contains other AD objects, including other OUs. You also use OUs for two main purposes: to delegate administration and to support group policies.

You use the commands in the Active Directory module to manage the objects in your AD. This module is supported natively in PowerShell 7.

Before You Start

You run the code snippets in this section on the Windows Server domain controller, DC1.Reskit.Org. You created this server in “Establishing a Forest Root Domain” and have used it throughout this chapter.

Creating a Hash Table for General User Attributes

Creating a user object requires specifying a number of parameters, which can lead to long lines of code (that can be harder to troubleshoot). To simplify the creation of AD user (and other) objects, you can use a hash table to hold the properties you want to set when creating the object.

You create this hash table by assigning values to the $NewUserHT variable as follows:

# 1. Create a hash table for general user attributes
$PW  = 'Pa$$w0rd'
$PSS = ConvertTo-SecureString -String $PW -AsPlainText -Force
$NewUserHT = @{
  AccountPassword       = $PSS
  Enabled               = $true
  PasswordNeverExpires  = $true
  ChangePasswordAtLogon = $false
}

This hash table contains values for all the users to be added to AD in this section.

Creating Two Users

Now that you have the basic user properties hash table set up, you extend it to include user‐specific details and then add the user to the AD as follows:

# 2. Create two users - adding to basic hash table
# First user
$NewUserHT.SamAccountName    = 'ThomasL'
$NewUserHT.UserPrincipalName = 'ThomasL@reskit.Org'
$NewUserHT.Name              = 'ThomasL'
$NewUserHT.DisplayName       = 'Thomas Lee (IT)'
New-ADUser @NewUserHT  # add first user
# Second user
$NewUserHT.SamAccountName    = 'RLT'
$NewUserHT.UserPrincipalName = 'RLT@Reskit.org'
$NewUserHT.Name              = 'Rebecca Lee-Tanner'
$NewUserHT.DisplayName       = 'Rebecca Lee-Tanner (IT)'
New-ADUser @NewUserHT  # Add second user

This snippet creates two new users in the AD. By default, these users are added to the Users container in AD. This container is not an OU.

This snippet shows some basic user settings. As noted earlier, you may want to extend the hash table to set additional properties on the user object being created.

Creating an OU for IT

An OU is a container object inside AD. When you added the two users to AD, it added them into the Users container. This is not helpful, as you can only apply group policies to an OU. Most organizations therefore use OUs to hold user, computer, and group objects.

It is important to note that both an OU and the Users container are container‐type objects (AD objects that can contain other objects). The difference is that you can only apply group policies to an OU. It is best practice to have your AD computers, groups, and users in OUs.

To create an OU, in this case for the Reskit IT team, you can do the following:

# 3. Create an Organizational Unit for IT
$OUHT = @{
    Name        = 'IT'
    DisplayName = 'Reskit IT Team'
    Path        = 'DC=Reskit,DC=Org'
}
New-ADOrganizationalUnit @OUHT 

This code creates a new OU at the top level of the Reskit.Org domain. Initially, it contains no objects.

Moving Users into an OU

With the IT OU created, you can now move each of the two users created earlier in this section into this OU, as follows:

# 4. Move the two users into the OU
$MHT1 = @{
    Identity   = 'CN=ThomasL,CN=Users,DC=Reskit,DC=ORG'
    TargetPath = 'OU=IT,DC=Reskit,DC=Org'
}
Move-ADObject @MHT1
$MHT2 = @{
    Identity = 'CN=Rebecca Lee-Tanner,CN=Users,DC=Reskit,DC=ORG'
    TargetPath = 'OU=IT,DC=Reskit,DC=Org'
}
Move-ADObject @MHT2

This snippet illustrates how to move an AD object. In this case, you move two specific users contained in the Users container into the IT OU. Once you have moved these user objects, the next time either user logs in, any OU‐specific GPOs are applied.

Creating a User in an OU

As an alternative to creating an AD user in the Users container (and then moving the user to an appropriate OU), you can specify an OU path to the OU in which to create the new user, like this:

# 5. Create a third user directly in the IT OU
$NewUserHT.SamAccountName    = 'JerryG'
$NewUserHT.UserPrincipalName = 'jerryg@reskit.org'
$NewUserHT.Description       = 'Virtualization Team'
$NewUserHT.Name              = 'Jerry Garcia'
$NewUserHT.DisplayName       = 'Jerry Garcia (IT)'
$NewUserHT.Path              = 'OU=IT,DC=Reskit,DC=Org'
$NewUserHT.PasswordNeverExpires  = $true
$NewUserHT.ChangePasswordAtLogon = $false
New-ADUser @NewUserHT

You use this user in “Configuring Just Enough Administration (JEA).” In this example, you explicitly set the account password to never expire and to enable the user to log in a first time without changing passwords. That might be a security risk in a larger organization but is useful for this book.

Adding Two Additional Users

To demonstrate removing AD objects, begin by creating two users that you will later delete, as follows:

# 6. Add two users who are then removed
# First user to be removed
$NewUserHT.SamAccountName    = 'TBR1'
$NewUserHT.UserPrincipalName = 'tbr1@reskit.org'
$NewUserHT.Name              = 'TBR1'
$NewUserHT.DisplayName       = 'User to be removed'
$NewUserHT.Path              = 'OU=IT,DC=Reskit,DC=Org'
New-ADUser @NewUserHT
# Second user to be removed
$NewUserHT.SamAccountName     = 'TBR2'
$NewUserHT.UserPrincipalName  = 'tbr2@reskit.org'
$NewUserHT.Name               = 'TBR2'
New-ADUser @NewUserHT

This snippet creates two new users ( TBR1 and TBR2) in the IT OU.

Viewing Existing Users

With an OU and several users created, you can use Get‐ADUser to view the existing AD users, as follows:

# 7. See the users that exist so far
Get-ADUser -Filter * -Properties DisplayName | 
  Format-Table -Property Name, DisplayName, SamAccountName

You can see the output of this command in Figure 3.25. Depending on your needs, you may want to add more parameters to this display.

image

Figure 3.25: Viewing users in Reskit.Org

As you can see in the figure, there are not many users in this domain. Also, note that the users added by default lack a DisplayName property, whereas the users you added did have a value set for that property.

Removing a User with a Get | Remove Pattern

There are at least two ways you can remove AD objects, including AD users. The first uses a Get | Remove pattern. In this pattern you first get objects (using, for example, Get‐ADUser) and then pipe them to a command (for example, Remove‐ADUser) to remove the objects (in this case, users). This pattern is particularly useful interactively. You use the Get portion to get the specific objects you need, verify that you are getting the right objects, and only then remove them. You can do this as follows:

# 8. Remove via a Get | Remove Pattern
Get-ADUser -Identity 'CN=TBR1,OU=IT,DC=Reskit,DC=Org' |
    Remove-ADUser -Confirm:$false

Removing a User Directly

A second way to remove a user is to use Remove‐ADUser and specify the identity for the user to be removed. There are several ways to specify the identity of the object; in this case, you use the full distinguished name, as follows:

# 9. Remove user directly
$RUHT = @{
  Identity = 'CN=TBR2,OU=IT,DC=Reskit,DC=Org'
  Confirm  = $false}
Remove-ADUser @RUHT

This demonstrates how you can remove a specific user directly. This approach is possibly quicker than using the Get | Remove pattern, but arguably less safe in the case of typos.

Updating and Displaying a User Object

Once you create an object, whether a user, a group, or whatever, you may need to update it. You may need to update a user object with a revised phone number or office name. You can update and view a user object with the Set‐ADUser and Get‐ADUser commands, as follows:

# 10. Update and display a user
$TLHT =@{
  Identity     = 'ThomasL'
  OfficePhone  = '4416835420'
  Office       = 'Marin Office'
  EmailAddress = 'ThomasL@Reskit.Org'
  GivenName    = 'Thomas'
  Surname      = 'Lee' 
  HomePage     = 'Https://tfl09.blogspot.com'
}
Set-ADUser @TLHT
Get-ADUser -Identity ThomasL -Properties  DisplayName, Office,
                                          OfficePhone, EmailAddress  |
  Format-Table -Property DisplayName, Name, Office,
                         OfficePhone, EmailAddress 

You can see the output of this step in Figure 3.26.

As in any script that outputs user details, you are likely to want to manage, retrieve, and view many different object properties; thus, you may want to extend the properties displayed by Format‐Table. Also, remember that if you want to display a property, you may need to extend the value of the ‐Properties parameter in Get‐ADUser as well so as to ensure you retrieve the property in the first place before displaying it. Note that retrieving additional properties adds performance overhead. Retrieving all properties can take two to three times as long as retrieving only the default set. Best practice is to retrieve only the properties you need.

image

Figure 3.26: Viewing updated user details

Creating an AD Group

You create an AD group similarly to creating an AD user, with the New‐ADGroup command as follows:

# 11. Create a new group for RK DNS Admins
$NGHT1 = @{
 Name        = 'RKDnsAdmins'
 Path        = 'OU=IT,DC=Reskit,DC=org'
 Description = 'Reskit DNS Universal admins'
 GroupScope  = 'Universal'
}
New-ADGroup @NGHT1

This creates a new group, RKDnsAdmins, which is stored in the IT OU. An AD group object is similar to an AD user object in that you can move it, remove it, or update it.

AD supports two types of groups: distribution groups and security groups. A security group can be used when adjusting ACLs. A distribution group, on the other hand, cannot be used for that. Even though the distribution group object actually contains a value for the SID attribute (a little‐known fact), the AD UI prevents you from using distribution groups in ACLs.

For more details on how security groups are used in access control, see this document: docs.microsoft.com/en-us/windows/win32/ad/how-security-groups-are-used-in-access-control.

Creating and Viewing Group Membership

Once you create the group, you need to populate it (and view the results). To do that, you use the Add‐ADGroupMember and Get‐ADGroupMember commands, as follows:

# 12. Add a user to the DNS Admins group and view group members
Add-ADGroupMember -Identity 'RKDnsAdmins' -Members 'JerryG' | Out-Null
Get-ADGroupMember -Identity 'RKDnsAdmins'

The output, shown in Figure 3.27, shows the new user, JerryG, is the only member of the RKDnsAdmins group.

image

Figure 3.27: Viewing updated user details

Note that you use this group (and the JerryG user) in “Configuring Just Enough Administration (JEA).”

Make a New Group for the IT Team

You next make a further Universal group that is to contain all members of the IT team.

# 13. Make a group for the IT Team
$NGHT2 = @{
  Name        = 'IT Team'
  Path        = 'OU=IT,DC=Reskit,DC=org'
  Description = 'All members of the IT Team'
  GroupScope  = 'Universal'
 }
 New-ADGroup @NGHT2

Make All Users in IT Members of the IT Team Group

Having created the group, you now add all members of the IT OU into this new group, with this code:

# 14. Make all Users in IT a Member Of This Group
$SB = 'OU=IT,DC=Reskit,DC=Org'
$ItUsers = Get-ADUser -Filter * -SearchBase $SB
Add-ADGroupMember -Identity 'IT Team' -Members $ItUsers

Displaying Group Membership

It is often useful to view the membership of a group after you update the members. The output of the following code, using Get‐ADGroupMember, could be useful in documenting membership of key groups:

# 15. Display Group Members of the IT Team Group
Get-ADGroupMember -Identity 'IT Team' | 
  Format-Table -Property SamAccountName, DistinguishedName

The output, which you can see in Figure 3.28, shows the SamAccountName and the DistinguishedName of the members of the IT Team group.

image

Figure 3.28: Viewing group membership

As you run the scripts in this section, it can also be handy to have the Active Directory Users and Computers MMC console open and available. This makes it easy to view the results of your scripts. It also allows you to quickly undo anything done incorrectly due, for example, to a typo in your code. As an alternative, you could also use the Active Directory Administrative Center (ADAC) for this purpose. See docs.microsoft.com/en-us/windows-server/identity/ad-ds/ad-ds-getting-started for details on the ADAC.

Adding a Computer to the AD

Adding a computer to an AD is simple and similar to adding a new user or group, as follows:

# 16. Add a computer to the AD
$NCHT = @{
  Name                   = 'Wolf' 
  DNSHostName            = 'Wolf.Reskit.Org'
  Description            = 'One for Jerry'
  Path                   = 'OU=IT,DC=Reskit,DC=Org'
  OperatingSystemVersion = 'Windows Server 2019 Data Center'
}
New-ADComputer @NCHT

This code adds a single computer, called Wolf.Reskit.Org, into the IT OU.

For the computer to utilize the AD domain, you also need to update the computer to be a member of the domain. Adding the computer first, known as pre‐staging, requires administrative privilege, but once a computer has been added to AD, you can then join that computer to the AD without needing elevated credentials. You can find more details in this article: websistent.com/how-to-prestage-a-computer-in-active-directory/. The article is several years old, but the principles it covers remain valid.

Displaying Computers in an AD Domain

The final step in this demonstration of managing AD users, computers, and OUs is to get and output the names of the computers in your AD domain and the last time they were logged on, as follows:

# 17. See the computer accounts
Get-ADComputer -Filter * -Properties DNSHostName,LastLogonDate | 
  Format-Table -Property Name, DNSHostName,LastLogonDate

The output from this step, in Figure 3.29, shows the computers currently contained in the Reskit.Org domain. Depending on your needs, you may want to adjust the properties you display.

image

Figure 3.29: Viewing computers in the AD

Note that the Wolf computer you added in the previous step shows no last logon date, since no one has yet logged on to this host. Also remember that the value for the LastLogonDate property can be off by up to 14 days. For more information, see blogs.technet.microsoft.com/askds/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works/.

The code snippets in this section have demonstrated how you can use the PowerShell cmdlets to add/remove/update objects in the AD including computers, OUs, and users.

Adding Users to AD via a CSV

In “Managing AD Users, Computers, and OUs,” you saw how you can use PowerShell 7 to add/remove/update objects in the AD. In production, you may need to automate the regular changes to AD. In almost all organizations, users and computers come, change, and go. Most organizations try to automate those regular changes.

How to automate the regular changes is a popular question in many support forums, such as the PowerShell forum on Spiceworks (community.spiceworks.com/programming/powershell). A common question is how to add users to AD using a CSV file.

In many organizations, an enterprise resource planning (ERP) tool, such as SAP, might hold the master copy of user details. Any changes to that information are made within the ERP system, which can then create a CSV file containing the information to be updated in other systems (such as AD). In a smaller organization, those details might be contained in a CSV file originating from the HR department using an Excel spreadsheet.

The basic concept is that the CSV file contains a collection of users to be added (or changed/removed). When you import the CSV file into PowerShell, you create objects that represent user properties, including name, password, and so on.

There are some user properties that are always required to add a user into the AD; the password, for example. Others, such as office name or phone number, are optional. Some organizations make use of a large number of AD User attributes; others use only the bare minimum. You can easily tailor your CSV files and the scripts that process them to meet the needs of your organization.

Before You Start

In this example you build a simple CSV file and then use it to create users in the Reskit.Org domain. You run the script on the DC1.Reskit.Org host. This host is a domain controller you created in “Establishing a Forest Root Domain” and have used throughout this chapter.

Creating a CSV File

The first step in this process is to create a CSV file. This file contains the basic information about each user to be added to the AD. It looks like this:

# 1 Create CSV
$CSVDATA = @'
Firstname, Initials, Lastname, UserPrincipalName, Alias, Description, Password
S,K,Masterly, SKM, Sylvester, Data Team, Christmas42
C,B, Smith, CBS, Claire, Receptionist, Christmas42
Billy, Bob, JoeBob, BBJB, BillyBob, A Bob, Christmas42
Malcolm, Dudley, Duelittle, Malcolm, Malcolm, Mr Danger, Christmas42
'@
$CSVDATA | Out-File -FilePath C:\Foo\Users.Csv

This CSV file contains seven properties, including a plain‐text password to be assigned to the user. If you want to include more information about each user in the AD, such as office phone number, you can add additional columns to the CSV file.

Importing and Viewing the CSV

With the CSV file created, the next step is to import it and, optionally, display the input data, as follows:

# 2. Import a CSV file containing the details of the users you 
#    want to add to AD:
$Users = Import-CSV -Path C:\Foo\Users.Csv | 
  Sort-Object -Property Alias
$Users | Format-Table

This creates the $Users array that contains the values for properties of the users to be added. You can see the output generated by this step in Figure 3.30.

image

Figure 3.30: Viewing users to be added to AD

When you import a CSV file, PowerShell converts the file into an array of the type System.Management.Automation.PSCustomObject. Each object is created with a NoteProperty representing each column in the CSV file. These properties are defined as being strings set to the appropriate values from the CSV file. You can view these details by piping the variable $Users to Get‐Member.

Adding Users to AD

To add the users to AD, you just iterate over the array of users, and for each user you create a hash table of user properties and then call New‐ADuser to add the user, as follows:

# 3. Add the users using the CSV
$Users | 
  ForEach-Object -Parallel {
    $User = $_ 
    #  Create a hash table of properties to set on created user
    $Prop = @{}
    #  Fill in values
    $Prop.GivenName         = $User.Firstname
    $Prop.Initials          = $User.Initials
    $Prop.Surname           = $User.Lastname
    $Prop.UserPrincipalName = $User.UserPrincipalName + "@Reskit.Org"
    $Prop.Displayname       = $User.FirstName.Trim() + " " +
                              $User.LastName.Trim()
    $Prop.Description       = $User.Description
    $Prop.Name              = $User.Alias
    $PW = ConvertTo-SecureString -AsPlainText $User.Password -Force
    $Prop.AccountPassword   = $PW
    $Prop.ChangePasswordAtLogon = $true
    $Prop.Path                  = 'OU=IT,DC=Reskit,DC=ORG'
    $Prop.Enabled               = $true
    #  Now Create the User
    New-ADUser @Prop
    # Finally, Display User Created
    "Created $($Prop.Name)"
}

You can see the results of this step in Figure 3.31. The New‐ADUser command does not produce output, so the output is generated by this snippet explicitly.

Viewing All Users in Reskit.Org

Having created some additional users, you can view all the users in the Reskit.Org domain, as follows:

# 4. Show All Users in AD (Reskit.Org)
Get-Aduser -Filter * |
  Format-Table -Property Name, UserPrincipalName
image

Figure 3.31: Creating new users

You can view the output from this snippet in Figure 3.32.

image

Figure 3.32: Viewing all users

CSV files are flexible as input to scripts that manage AD objects. You can adopt the approach shown in this section when removing users such as a user who has left the organization. Create a file of users to be removed, and your script can remove them or move them to an “Ex‐Employee” group as dictated by your policies. You can also use this technique for adding groups to the AD. Just create a CSV file of groups and then adjust this script to add the new AD groups.

This snippet uses an important new PowerShell 7 feature, which is the ‐ Parallel parameter to Foreach‐Object. This snippet runs user creation in parallel. In this case, where you are adding just four users to AD on the DC, using this construct is actually just a bit slower than not using ‐Parallel. However, this construct can be more useful were you to run this script on a client system where there were network latencies to consider, or where each iteration did more than just add a new user (such as adding the user to a group, creating files, and so on).

Configuring Just Enough Administration (JEA)

Managing rights and permissions can be complex even in smaller organizations. In all too many organizations, administrators of all sorts are just dropped into high‐privilege groups such as Domain Administrators or Enterprise Administrators. That can make jobs easier, but it also opens up all sorts of potential security holes.

Just Enough Administration (JEA) is a tool to enable you to implement fine‐grained administrative delegation, ensuring that a user has just enough privilege to do their job and not a bit more. JEA uses PowerShell remoting to define which users are in a specific role and what that role can do within a remoting session.

With JEA, for example, you can allow your DNS administrators to enter a remoting session on a domain controller and manage the DNS service (and very little more). Even though a user is in a remoting session on a DC, with JEA they would be unaware that other commands exist and would not be able to use them.

There are three distinct objects involved with JEA. To deploy JEA you need to develop each of these:

  • JEA role capabilities file: This file, essentially a PowerShell hash table stored with the extension .PSRC, defines a role in terms of the aliases, commands, functions, providers, and external programs an administrator can use within a JEA session. A PSRC files resembles a PowerShell module manifest.
  • JEA session configuration file: This file, stored with the extension .PSSC, defines how a JEA endpoint is configured. It states the users (groups) that can use the endpoint, a JEA session, and the roles to which they have access.
  • JEA‐based remoting endpoint: This is a remoting endpoint that your restricted user accesses. The endpoint enables role users to access a constrained remoting endpoint based on the session configuration file.

Once you have the two files in place, you register the JEA‐based endpoint to the server based on the session configuration.

A JEA user can enter a PowerShell remoting session or invoke commands in a remoting session specifying the constrained endpoint. PowerShell uses the user's group membership to determine the role involved and configures the PowerShell session based on the role capabilities file for that role.

Before You Start

In this example, you implement JEA on a domain controller, DC1, which you have used throughout this chapter. You created this host as a domain controller in “Establishing a Forest Root Domain.”

You also need to create or use the AD user JerryG. You also need to make this user a member of the RKDnsAdmins group. You created the user and added the account to the group in “Managing AD Users, Groups, and Computers.”

Creating a Transcript Folder

A useful feature of JEA is the use of PowerShell transcripts. With JEA, PowerShell can create a session transcript for any JEA session automatically. You first create a new folder to hold the transcripts, as follows:

# 1. Create transcripts folder
New-Item -Path C:\JEATranscripts -ItemType Directory | Out-Null

Creating a Role Capabilities Folder

The role capabilities file defines the capabilities of a role. While you can store that file anywhere, it's good security to store it in a specific folder, as follows:

# 2. Create capabilities folder
$JEACF = "C:\JEACapabilities"
New-Item -Path $JEACF -ItemType Directory | Out-Null

You might also want to restrict access to files in this folder.

Creating a Role Capabilities File

Next, you create a role capabilities file. The role capabilities file defines what a user in a role is allowed to do within a remoting session. You create a new JEA role capabilities file as follows:

# 3. Create Role Capabilities File
$RCF = Join-Path -Path $JEACF -ChildPath "RKDnsAsmins.psrc"
$RCHT = @{
  Path            = $RCF
  Author          = 'Reskit Administration'
  CompanyName     = 'Reskit.Org' 
  Description     = 'Defines RKDnsAdmins role capabilities'
  AliasDefinition = @{Name='gh';Value='Get-Help'}
  ModulesToImport = 'Microsoft.PowerShell.Core','DnsServer'
  VisibleCmdlets  = ("Restart-Service",
                     @{ Name       = "Restart-Computer"; 
                        Parameters = @{Name = "ComputerName"}
                        ValidateSet = 'DC1, DC2'},
                      'DNSSERVER\*')
  VisibleExternalCommands = ('C:\Windows\System32\whoami.exe')
  VisibleFunctions = 'Get-HW'
  FunctionDefinitions = @{
    Name = 'Get-HW'
    Scriptblock = {'Hello JEA World'}}
}
New-PSRoleCapabilityFile @RCHT 

The role capabilities defined in this file enable a DNS admin to do all of the following:

  • Use gh as an alias to Get‐Help.
  • Automatically have the remoting session started with the DNSServer module imported.
  • Use the Restart‐Service command (but not Get‐Service).
  • Use the Restart‐Computer, but only to restart DC1 or DC1.
  • Use any command in the DNSServer module.
  • Use the console application whoami.exe.
  • Use a new function called Get‐HW (whose definition you can see in the role capabilities file).

Creating a JEA Session Configuration File

The JEA session configuration file is used by PowerShell to associate groups (such as the RKDnsAdmins group) whose members use a specific role (such as the RKDNSAdmins role). You can also configure some additional aspects of a JEA remoting session, as follows:

# 4. Create a JEA Session Configuration file
$SCF = 'C:\JEASessionConfiguration'
New-Item -Path $SCF -ItemType Directory | Out-Null
$P   = Join-Path -Path $SCF -ChildPath 'RKDnsAdmins.pssc'
$RDHT = @{
  'Reskit\RKDnsAdmins' = @{'RoleCapabilityFiles' = 
                           'C:\JEACapabilities\RKDnsAsmins.psrc'}
}
$PSCHT= @{
  Author              = 'DoctorDNS@Gmail.Com'
  Description         = 'Session Definition for RKDnsAdmins'
  SessionType         = 'RestrictedRemoteServer'   # ie JEA!
  Path                = $P                 # the output file
  RunAsVirtualAccount = $true
  TranscriptDirectory = 'C:\ JeaTranscripts'
  RoleDefinitions     = $RDHT     # RKDnsAdmins role mapping
}
New-PSSessionConfigurationFile @PSCHT 

The session configuration file's session type indicates that the remoting session is based on JEA. The configuration also tells PowerShell that the user identity in the session is based on a temporary account unique to a specific user and valid for only the duration of a JEA session.

For more details on the session configuration file, see docs.microsoft.com/powershell/scripting/learn/remoting/jea/session-configurations.

This session configuration file also sets up a Transcripts folder. This enables PowerShell to create a transcript of all commands within a JEA session and store that in the specified folder. These transcripts are of the same type as created by the Start‐Transcript command (see docs.microsoft.com/powershell/module/microsoft.powershell.host/Start-Transcript?view=powershell-7 for more details on Start‐Transcript). With JEA, PowerShell creates a transcript covering the entire JEA session.

Testing the Session Configuration File

It is useful to use Test‐PSSessionConfiguration to test the session configuration file to ensure that it is properly formatted. This command ensures that you have valid keys in the session configuration file and that values are the correct type. You can do this as follows:

# 5. Test the session configuration file
Test-PSSessionConfigurationFile -Path $P

You can see the output of this command in Figure 3.33.

image

Figure 3.33: Testing the session configuration file

As you can see, Test‐PSSessionConfigurationFile does not return a lot of information about what has been checked. However, if there are issues in the session configuration file, these are noted when you test the file.

Enabling Remoting and Creating the JEA Session Endpoint

Now that you have your JEA role and session configuration specified, you can create the JEA session endpoint. Before doing that, you enable PowerShell remoting explicitly using Enable‐PSRemoting. Then you create the JEA endpoint using Register‐PSSessionConfiguration, like this:

# 6. Enable Remoting and register the JEA Session Definition
Enable-PSRemoting -Force | Out-Null
$SCHT = @{
  Path  = $P
  Name  = 'RKDnsAdmins' 
  Force =  $true 
}
Register-PSSessionConfiguration @SCHT

Figure 3.34 shows the output from running this snippet.

image

Figure 3.34: Registering the session configuration file

This command creates a new remoting endpoint that provides a JEA environment for DNS administration.

Checking What the User Can Do

You can use the command Get‐PSSessionCapability to determine what a user (in the RKdnsAdmins group) can do within a JEA remoting session, like this:

# 7. Check What the User Can Do
Get-PSSessionCapability -ConfigurationName RKDnsAdmins -Username 'Reskit\JerryG' |
  Sort-Object -Property Module

You can see the output from these commands in Figure 3.35.

image

Figure 3.35: Determining session capabilities

To save space in this book, the output shows only some of the commands available in the JEA session.

Creating Credentials for JerryG

With JEA set up, you can test it by running script blocks in a JEA session; but to do that, you need a credential object. You create credentials for a user who is in the RKDNSAdmins group using the following commands:

# 8. Create Credentials for user JerryG
$U    = 'JerryG@Reskit.Org'
$P    = ConvertTo-SecureString 'Pa$$w0rd' -AsPlainText -Force
$Cred = [PSCredential]::New($U,$P)

Creating Three Script Blocks to Test JEA

To test that JEA is delivering what you expected, you can try to run some simple scripts inside a JEA session, as follows:

# 9. Define Three Script Blocks and an Invocation Splatting Hash Table
$SB1   = {Get-Command}
$SB2   = {Get-HW}
$SB3   = {Get-Command -Module 'DNSSERVER'}
$ICMHT = @{
  ComputerName      = 'DC1.Reskit.Org'
  Credential        = $Cred 
  ConfigurationName = 'RKDnsAdmins' 

This snippet creates three script blocks:

$SB1 This script block gets all the commands (that is, all the commands the JEA session provides to the user).
$SB2 This script block runs the Get‐HW function.
$SB3 This block shows the commands that are in the DNSServer module and that are available to the JEA user.

These three script blocks test the capabilities that are provided to a DNS admin within the JEA session on DC1.

How Many Commands Exist in a JEA Session?

A test of JEA is to show the commands that are available within a JEA session. To achieve that, you execute the $SB1 script block within a remoting session using the newly created JEA endpoint. You can do that as follows:

# 10. Get commands available within the JEA session
Invoke-Command -ScriptBlock $SB1 @ICMHT |
  Sort-Object -Property Module |
    Select-Object -First 15

This snippet gets details of all the commands available within the JEA session, sorts them by module name, and then selects the first 15 commands. You can view the output in Figure 3.36.

image

Figure 3.36: Checking on commands in the JEA session

You can see all the non‐DNS commands available plus the first few from the DNSServer module. You can see in this list the Get‐HW function defined only for this endpoint.

Invoking a JEA‐Defined Function

In the role capabilities file, you defined a function, Get‐HW, which is to be available within the JEA session. You can do this by invoking $SB2 in a JEA session, as follows:

# 11. Invoke a JEA Defined Function in a JEA Session as JerryG
Invoke-Command -ScriptBlock $SB2 @ICMHT

You can see the output in Figure 3.37.

image

Figure 3.37: Invoking a JEA‐defined function

Get the DNSServer Command Available in JEA Session

As a final test of JEA, you can obtain a count of how many commands are available in the JEA session and provided by the DNSServer module. You do that by invoking the $SB3 script block, as follows:

# 12. Get DNSServer commands available to JerryG
$C = Invoke-Command -ScriptBlock $SB3 @ICMHT 
"$($C.Count) DNS commands available"

You can see the output of this command in Figure 3.38.

image

Figure 3.38: Counting DNS server commands available

Viewing the Transcripts Folder

In the session configuration file, you instructed PowerShell to create PowerShell transcripts for each JEA session. You can view the contents of the transcript folder as follows:

# 13. Examine the Contents of the Transcripts Folder:
Get-ChildItem -Path $PSCHT.TranscriptDirectory

You can view the output of this snippet in Figure 3.39.

image

Figure 3.39: Viewing the JEA Transcripts folder

As you can see, there are three transcript files in the folder, one for each of the three script blocks you just ran. Because each session was short, these transcripts are also quite short. If users run long sessions, these transcript files can grow. If you are going to generate transcripts, you should ensure that older transcript files are removed.

Examining a JEA Transcript

Each transcript in the transcript folder holds details of everything that happens within the JEA session. You can examine the transcript generated when you ran the first script block, as follows:

# 14. Examine a transcript
Get-ChildItem -Path $PSCHT.TranscriptDirectory | 
  Select-Object -First 1  |
     Get-Content 

You can see the output of this snippet in Figure 3.40.

Depending on how many transcripts you have, you may need to adjust this snippet to ensure you are looking at the correct transcript.

In the figure, you see the results of running $SB2 in the JEA session. That script block called the Get‐HW function, which in turn displayed the text Hello JEA World. In the transcript, you can see the start and end times for the session, the user who ran the script, and details about the environment and about every command run in the session.

If the JEA endpoint is used heavily, then the size of the transcript directory can grow. As with all logging, you should have a strategy for managing the JEA transcripts.

image

Figure 3.40: Viewing a JEA transcript

Summary

In this chapter, you examined some of the key AD‐related actions you might need to perform using PowerShell 7. Two modules, Server Manager and the AD Deployment, both do not natively load PowerShell 7. Instead, you need to use the Windows PowerShell compatibility mechanism discussed in Chapter 2. You use Import‐Module to load these two modules, allowing access to the commands they contain. The Active Directory module, on the other hand, works natively in PowerShell 7.