How to Install Active Directory on a Windows Server 2016 Core using PowerShell

How to add a freshly installed Windows Server 2016 Core to a domain and promote it as Domain Controller in a few easy steps.
In this scenario we assume that there already is an existing domain in an existing forest and the new Domain Controller should just be added to the existing domain.

Rename the Computer properly

1
Rename-Computer -NewName dc-w2016

Restart the computer to make the renaming effective

1
Restart-Computer

Configure an IP Adresse
Therefore you need to know the name of the network interface you want to use. Get a list of all network interfaces using:

1
Get-NetAdapter

Configure the IP Address

1
New-NetIPAddress -InterfaceAlias MyInterface -IPAddress 192.168.1.20 -PrefixLength 24 -AddressFamily IPv4 -DefaultGateway 192.168.1.1

Configure the DNS Server
To successfully join the new computer to the domain and promote it to a Domain Controller, the DNS Server entries should point to already existing Domain Controllers

1
Set-DnsClientServerAddress -InterfaceAlias MyInterface -ServerAddresses [IP_of_existing_DC]

Add the computer as a member to the specific domain

1
Add-Computer -DomainName lab.mydaomain.com

Insert the credentials of a Domain administrator account when prompted.

Restart the computer to make the domain join effective

1
Restart-Computer

Note: from this point on you can easily remote administer your computer using the Remote Server Administration Tools (RSAT) from any computer in your network

Now install the Active Directory Domain Services (ADDS) Features
Using the switch -IncludeManagementTools installs the management tools.

1
Install-WindowsFeature AD-Domain-Services -IncludeManagementTools

Finally promote the computer as a Domain Controller
The switch -InstallDns installs the DNS Server Role on the computer and integrates it with Active Directory

1
Install-ADDSDomainController -DomaiName lab.mydaomain.com -InstallDns

After restarting the computer (there will be a prompt), the computer has successfully been joined to the domain and promoted as a Domain Controller.

certutil – Generate a Certificate Template to OID Hashtable

In order to use certutil to list certificates issued from a specific certificate template as shown below, you have to know the templates OID.

1
certutil -view -restrict 'Certificate Template=<certificate_template_OID>'

The following PowerShell script returns a hashtable with the template name as the key and the OID as the value for each template found either on the Active Directory (-adtemplate switch) or on the local Certification Authority (CA) (-catemplates switch).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Prepare variables
$certutil = "$($env:SystemRoot)\system32\certutil.exe"
$templateHash = @{}
$currentTemplateName = $false
 
# Get certutil stdout, select the displayname and msPKI-Cert-Template-OID strings and walk through them in a loop
Invoke-Expression "$certutil -adtemplate -v" | select-string displayname, msPKI-Cert-Template-OID | foreach {
 
    # Check if the template name variable is set and the current value is an OID
    if($currentTemplateName -ne $false -and $_ -match 'msPKI-Cert-Template-OID'){
 
        $oid = $_ -replace '([\w|-]*)\s=\s([\d|.]*)(.*)','$2'
        $templateHash.add($currentTemplateName, $oid)
        $currentTemplateName = $false
    }
 
    # Check if the current value is a template name
    elseif($_ -match 'displayname'){
 
        $currentTemplateName = $_ -replace 'displayname = ',''
    }
}
 
# Print Hastable
$templateHash | fl

Password Complexity Validation with PowerShell

Sometimes you need to verify if a password meets some minimal requirements such as length, upper- and lowercase characters and special chars.

This simple script validates if a string / password matches at least these minimum requirements:

  • At least one uppercase character
  • At least one lowercase character
  • At least one digit
  • At least one special char
  • At least 10 characters in length
1
2
3
4
5
6
7
8
9
10
11
12
13
$password = 'mySecure!P@ssword$'
 
$counter = 0
@('[A-Z]', '[a-z]', '\d', '[\41-\57\100\133-\140]', '.{10,}') | foreach {
    if(($str -creplace $_, '') -ne $str) { $counter++ }
}
 
if($counter -ge 5){
    Write-Host "Strong password"
}
else {
    Write-Host "Weak password"
}

How to loop through the Properties of a PowerShell Object

If you have want to iterate through a hashtable to make use of the key/value pairs, you can do that using the GetEnumerator() function and foreach.

1
2
3
4
5
6
7
8
9
10
11
12
# Preparing the hastable
$myHastable = @{
    "AnimalType" = "Dog"
    "Name" = "Bella"
    "Owner" = "Mr. Smith"
}
 
# Looping through the hashtable
$myHastable.GetEnumerator() | foreach { 
    $_.Name
    $_.Value
}

To access the values of a PowerShell object you have to know the name of the object property to access it, which looks like this: $myObject.PropertyName

I had to make use of all the properties of a PowerShell object without knowing its property names beforehand. Since GetEnumerator() does not work on objects I had to come up with another solution, which is to access the properties using the PSMemberSet of my custom object.

1
2
3
4
5
6
7
8
9
10
11
12
# Preparing the object
$myObject = New-ObjectType PSObject
$myObject | Add-MemberType NoteProperty –Name "AnimalType" –Value "Dog"
$myObject | Add-MemberType NoteProperty –Name "Name" –Value "Bella"
$myObject | Add-MemberType NoteProperty –Name "Owner" –Value "Mr. Smith"
 
 
# Looping through the object
$myObject.PSObject.Properties | foreach { 
    $_.Name
    $_.Value
}

Works as easy as if it were a hastable.

PowerShell Datatype Declaration

PowerShell is a scripting and not a programming language, that much I know. Sometimes I am still missing a strict(er) datatype declaration to prevent a lot of mistakes caused by none or (automatically) false declared variables.

Have a look at the examples below and let me know what you think of it 🙂

1
2
3
4
2+"5" # => 7 
"2"+5 # => 25
[int]"2"+5 # => 7
[string]2+"5" # => 25

In my opinion all of the above examples should throw an error while the following ones should not:

1
2
3
4
2+5 # => 7
"2"+"5" # => 25
[int]2+5 # => 7
[string]"2"+"5" # => 25