Thursday, November 14, 2013

Windows Server 2012 - basic configuration with PowerShell

Besides configuration through the GUI, Windows Server 2012 can be configured with a variety of command line tools:
 
  • netsh for the IP address, default gateway and DNS server
  • netdom renamecomputer for the hostname
  • netdom join for joining the server to a domain
 
Of course, Microsoft now recommends the use of Powershell so I wanted to configure a fresh install of Windows Server 2012 using this tool.
 
When it came to enabling the Remote Desktop or setting Windows Firewall rules, the process was rather complex and seemed to confirm the idea in some cases, the GUI is the better tool. For example, setting time and date, not to mention the time zone, on a single server, is achieved much more efficiently by entering control timedate.cpl and adjusting settings in the resulting window. Likewise for regional settings, in which case we can use control intl.cpl.
 
Given the new interface, to which I am slowly adjusting however, I find it is often just as easy to type commands such as those above, rather than clicking multiple times to reach the appropriate icon in the Control Panel.
 
Otherwise, I've used Powershell as illustrated in the paragraphs that follow to configure...

  • IP address (IPv4 and IPv6)
  • Hostname
  • Domain join
  • Remote Powershell
  • Remote Desktop (RDP)
  • Firewall setting adjustments for Remote Desktop


IP address and related settings (default gateway, DNS servers)


Note: I've sometimes analyzed in some detail certain aspects of the addressing process. If you are most interested in the cmdlets themselves, please concentrate on the commands in bold and notably in red.
 
First, I want to see the name of the network adapter (or "network card") and the general status quo of the IP information.
 
As we can see below, using the Powershell cmdlets in question, the name of the first (and most often only) network adapter is "Ethernet" (rather than "Local Area Connection" as in previous versions of Windows).
 
Even before manual configuration of IP settings (assuming we do not use DHCP - less likely on a server), some IP addresses, notably IPv6, have been assigned automatically. We also have an IPv4 and IPv6 loopback address, respectively 127.0.0.1 and ::1
 
Since the output of some of the commands can be quite lengthy, I've used the format-list cmdlet to limit the output to values that I find most useful.


PS C:\> Get-NetAdapter | fl name,interfacedescription,macaddress,linkspeed

name                 : Ethernet
interfacedescription : Intel(R) 82574L Gigabit Network Connection
MacAddress           : 00-0C-29-06-55-CE
LinkSpeed            : 1 Gbps


PS C:\> Get-NetIPAddress | fl ipaddress,interfacealias,addressfamily,addressstate

ipaddress      : fe80::1fe:23d7:24dc:1847%12
interfacealias : Ethernet
AddressFamily  : IPv6
AddressState   : Preferred


ipaddress      : fd00::de17:18f7:578f:48ca
interfacealias : Ethernet
AddressFamily  : IPv6
AddressState   : Preferred


ipaddress      : fe80::5efe:169.254.24.71%19
interfacealias : Local Area Connection* 11
AddressFamily  : IPv6
AddressState   : Deprecated


ipaddress      : ::1
interfacealias : Loopback Pseudo-Interface 1
AddressFamily  : IPv6
AddressState   : Preferred


ipaddress      : 169.254.24.71
interfacealias : Ethernet
AddressFamily  : IPv4
AddressState   : Preferred


ipaddress      : 127.0.0.1
interfacealias : Loopback Pseudo-Interface 1
AddressFamily  : IPv4
AddressState   : Preferred


IPCONFIG is still useful - and less verbose: 

PS C:\> ipconfig

Windows IP Configuration

Ethernet adapter Ethernet:

   Connection-specific DNS Suffix  . :
   IPv6 Address. . . . . . . . . .        . : fd00::de17:18f7:578f:48ca
   Link-local IPv6 Address . . . . . : fe80::1fe:23d7:24dc:1847%12
   Autoconfiguration IPv4 Address. . : 169.254.24.71
   Subnet Mask . . . . . . . . . . . : 255.255.0.0
   Default Gateway . . . . . . . . . :

Tunnel adapter Local Area Connection* 11:
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :


 
Now we will configure an IPv4 address - this is roughly the equivalent of the netsh int ipv4 set address command. The command is at least as long to type as the parameters must be indicated, unlike netsh with which we can simply enter 10.1.1.12 255.0.0.0 10.1.1.2 at the end (using the same values as below).
 

PS C:\> New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress 10.1.1.12 -PrefixLength 8 -DefaultGateway 10.1.1.2

IPAddress         : 10.1.1.12
InterfaceIndex    : 12
InterfaceAlias    : Ethernet
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 8
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Tentative
...

IPAddress         : 10.1.1.12
InterfaceIndex    : 12
InterfaceAlias    : Ethernet
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 8
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Invalid
...


PS C:\> Get-NetIPAddress | where { $_.IPAddress -eq "10.1.1.12" } | fl

IPAddress         : 10.1.1.12
InterfaceIndex    : 12
InterfaceAlias    : Ethernet
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 8
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Preferred
...


Note that the state of the address changes. In the output that displayed automatically after the execution of the New-NetIPAddress, it is first "Tentative" and then (for some reason?) "Invalid" but finally "Preferred" - after verification with the cmdlet Get-NetIPAddress.


 
Here we configure the DNS server:

PS C:\> Set-DnsClientServerAddress "Ethernet" -ServerAddresses 10.1.1.10



So what's changed? Now, instead of the 169.254.24.71 address, we have "10.1.1.12".

PS C:\> Get-NetIPAddress | fl ipaddress,interfacealias,addressfamily,addressstate

[snip]
ipaddress      : ::1
interfacealias : Loopback Pseudo-Interface 1
AddressFamily  : IPv6
AddressState   : Preferred

ipaddress      : 10.1.1.12
interfacealias : Ethernet
AddressFamily  : IPv4
AddressState   : Preferred

ipaddress      : 127.0.0.1
interfacealias : Loopback Pseudo-Interface 1
AddressFamily  : IPv4
AddressState   : Preferred

 
 
Now we'll  configure an IPv6 address. Unlike the IPv4 address, that replaced the APIPA address (169.254.x.x), the manually assigned IPv6 address can coexist with the IPv6 link local address (and others).

PS C:\> New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress fd00::12

IPAddress         : fd00::12
InterfaceIndex    : 12
InterfaceAlias    : Ethernet
AddressFamily     : IPv6
Type              : Unicast
PrefixLength      : 128
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Tentative
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : ActiveStore

IPAddress         : fd00::12
InterfaceIndex    : 12
InterfaceAlias    : Ethernet
AddressFamily     : IPv6
Type              : Unicast
PrefixLength      : 128
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Invalid
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : PersistentStore




Once again, as for the IPv4 address, the status of the address goes from "Tentative" to "Invalid" to "Preferred" (see below).


PS C:\> Get-NetIPAddress | where { $_.IPAddress -eq "fd00::12" } | fl

IPAddress         : fd00::12
InterfaceIndex    : 12
InterfaceAlias    : Ethernet
AddressFamily     : IPv6
Type              : Unicast
PrefixLength      : 128
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Preferred
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : ActiveStore



We can use the following commands to verify the status of our network adapters:



PS C:\> ipconfig /all
Windows IP Configuration

   Host Name . . . . . . . . . . . . : WIN-6CSB0KQ27BN
   Primary Dns Suffix  . . . . . . . :
   Node Type . . . . . . . . . . . . : Hybrid
   IP Routing Enabled. . . . . . . . : No
   WINS Proxy Enabled. . . . . . . . : No


Ethernet adapter Ethernet:

   Connection-specific DNS Suffix  . :
   Description . . . . . . . . . . . : Intel(R) 82574L Gigabit Network Connection
   Physical Address. . . . . . . . . : 00-0C-29-06-55-CE
   DHCP Enabled. . . . . . . . . . . : No
   Autoconfiguration Enabled . . . . : Yes
   IPv6 Address. . . . . . . . . . . : fd00::12(Preferred)
   IPv6 Address. . . . . . . . . . . : fd00::f0a7:570b:489b:a320(Preferred)
   Lease Obtained. . . . . . . . . . : Wednesday, November 6, 2013 8:16:55 PM
   Lease Expires . . . . . . . . . . : Thursday, November 21, 2013 6:58:10 AM
   Link-local IPv6 Address . . . . . : fe80::1fe:23d7:24dc:1847%12(Preferred)
   IPv4 Address. . . . . . . . . . . : 10.1.1.12(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.0.0.0
   Default Gateway . . . . . . . . . : 10.1.1.2
   DHCPv6 IAID . . . . . . . . . . . : 251661353
   DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-1A-0C-C5-57-00-0C-29-06-55-CE
   DNS Servers . . . . . . . . . . . : 10.1.1.10
   NetBIOS over Tcpip. . . . . . . . : Enabled



PS C:\> Get-NetIPAddress | fl IPAddress,SuffixOrigin,AddresState

IPAddress    : fe80::1fe:23d7:24dc:1847%12
SuffixOrigin : Link

IPAddress    : fd00::7f64:7932:81f9:dc56
SuffixOrigin : Dhcp

IPAddress    : fd00::12
SuffixOrigin : Manual

IPAddress    : fe80::5efe:10.1.1.12%13
SuffixOrigin : Link

IPAddress    : ::1
SuffixOrigin : WellKnown

IPAddress    : 10.1.1.12
SuffixOrigin : Manual

IPAddress    : 127.0.0.1
SuffixOrigin : WellKnown


PS C:\> Get-DnsClientServerAddress | fl interfacealias,ServerAddresses

interfacealias  : Ethernet
ServerAddresses : {10.1.1.10}

interfacealias  : Ethernet
ServerAddresses : {fd00::10}

[snip]





Hostname


The netdom renamecomputer command requires the designation of the current name of the computer. After experimenting with the first option below, I discovered that Powershell allows us to rename the computer without indicating the current name.

In either case, the cmdlet is Rename-Computer.


Option 1

PS C:\> $oldname = hostname
PS C:\> $oldname
WIN-6CSB0KQ27BN

PS C:\> Rename-Computer -NewName SVR-00X -ComputerName $oldname

WARNING: The changes will take effect after you restart the computer WIN-6CSB0KQ27BN.

PS C:\> Restart-Computer


Option 2

PS C:\>Rename-Computer -NewName SVR-004 -force -restart

Note: There are parameters that allow us to perform the operation using specific credentials, local or domain:

-DomainCredential
-LocalCredential


Otherwise, the credentials of the user performing the operation are used by default.

The -force parameter eliminates the confirmation prompt.

The -restart parameter restarts the computer automatically.





Join computer to domain


Note: the command assumes that the computer account and organizational unit in question have already been created in Active Directory.

Creation of OU:

PS C:\> New-ADOrganizationalUnit "Servers"

By default, the OU is created at the domain root. If that is where we want to create the OU, no further parameters are necessary.

Creation of computer account for server:

PS C:\> New-ADComputer SVR-004 -Path "OU=Servers,DC=machlinkit,DC=biz"

Joining server to domain:

PS C:\> Add-Computer -DomainName machlinkit.biz -restart

Note: if logged on with credentials that allow joining a computer to the domain, it is not necessary to enter them.





Enable Remote Powershell

In fact, there is not much to do here.

On Windows 2012, remote access via PowerShell is enabled by default.

In this case, we want to be able to execute Powershell commands on member server SVR-004 from domain controller DC-001.

We can run the following command on previous operating systems, Windows 2008 R2 in particular, or use it to enable PS remoting on a machine where it was disabled:


PS C:\> Enable-PSRemoting -force
WinRM is already set up to receive requests on this computer.
WinRM is already set up for remote management on this computer.
PS C:\>


Since the function is already enabled, we have the output shown above.

Note: if necessary, we would run this command on SVR-004 - the target computer.

------------------------------------------------------------------------------------------

Note: the -force parameter eliminates the rather verbose confirmation that we would otherwise see. I'll post that output here since it does describe what the command accomplishes:

PS C:\> Enable-PSRemoting

WinRM Quick Configuration

Running command "Set-WSManQuickConfig" to enable remote management of this computer by using the Windows Remote Management (WinRM) service.

 This includes:

    1. Starting or restarting (if already started) the WinRM service
    2. Setting the WinRM service startup type to Automatic
    3. Creating a listener to accept requests on any IP address
    4. Enabling Windows Firewall inbound rule exceptions for WS-Management traffic (for http only).


Do you want to continue?

[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): A

WinRM is already set up to receive requests on this computer.
WinRM has been updated for remote management.
Configured LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.


Confirm

Are you sure you want to perform this action?

Performing operation "Set-PSSessionConfiguration" on Target "Name: microsoft.powershell SDDL:
O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;RM)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD). This will allow selected users to remotely run Windows PowerShell commands on this computer".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): A


---------------------------------------------------------------------------------------------------------


From DC-001 we can first test the WinRM service on SVR-004: is it running? responding to requests?


PS C:\> Test-WsMan SVR-004

wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor   : Microsoft Corporation
ProductVersion  : OS: 0.0.0 SP: 0.0 Stack: 3.0



To verify (one step further), I'll stop and later start the WinRM service.

Right now it is on.

PS C:\> Get-Service WinRM | fl

Name                : WinRM
DisplayName         : Windows Remote Management (WS-Management)
Status              : Running
DependentServices   : {}
[snip]


PS C:\> Stop-Service WinRM


We try Test-WsMan again... this time, failure:

PS C:\> Test-Wsman SVR-004

test-wsman : <f:WSManFault xmlns:f="http://schemas.microsoft.com/wbem/wsman/1/wsmanfault" Code="2150859046" Machine="DC-001.machlinkit.biz"><f:Message>WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM firewall exception for public profiles limits access to remote computers within the same local subnet. </f:Message></f:WSManFault>


If I enable the WinRM on SVR-004 (with Start-Service), the test succeeds once again.


Now I'll start a remote Powershell session on SVR-004 from DC-001 and execute some basic file system commands.


PS C:\> Enter-PSSession -ComputerName SVR-004
[snip]
[SVR-004]: PS C:\>
[SVR-004]: PS C:\> gci


    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----         7/26/2012   3:44 AM            PerfLogs
d-r--         11/6/2013   8:14 PM            Program Files
d----         7/26/2012   4:04 AM            Program Files (x86)
d----        11/13/2013   4:50 PM            TeamDocuments
d-r--        11/13/2013   5:12 PM            Users
d----         11/6/2013  10:45 PM            Windows


[SVR-004]: PS C:\> sl .\TeamDocuments
[SVR-004]: PS C:\TeamDocuments> New-Item -Name NewFile1.txt -Type File


[snip]

We can also execute I single command on the remote computer using the Invoke-Command cmdlet as shown below. First we need to exit from the current session.

[SVR-004]: PS C:\TeamDocuments> Exit
PS C:\>
PS C:\> Invoke-Command SVR-004 -ScriptBlock {gci C:\TeamDocuments}


    Directory: C:\TeamDocuments

Mode                LastWriteTime     Length  Name          PSComputerName
----                -------------                  ------    ----                  --------------
-a---        11/13/2013   5:13 PM          0    NewFile.txt            SVR-004
-a---        11/13/2013   5:13 PM          0    NewFile1.txt          SVR-004


Note that in both cases, I am able to omit the parameter -ComputerName because it is a positional parameter and do not need to use the -Credential parameter because I am using a domain administrator account that has complete access to both machines.

Note (once again) that "Windows Remote Management" is enabled by default in Windows 2012. It was not necessary for me to adjust firewall settings.




Enable Remote Desktop

This is where I find Powershell to be most lacking. There is not a simple cmdlet that performs the two necessary operations that must be accomplished to enable remote desktop:
 
1. Enable Remote Desktop itself
2. Configure Windows Firewall in consequence.
 
In my opinion, the simplest - or least complicated way - to achieve this is to manipulate the registry settings that govern Remote Desktop with the Set-ItemProperty cmdlet and then the firewall rules with the Enable-NetFirewallRule cmdlet. As I proceeded by trial and error, until I found the commands that seemed most appropriate (to me), I'll place the "best" cmdlets (in my opinion) in bold red font, the others in simple bold.

I found an alternate method that uses WMI to manipulate the Remote Desktop setting as well. I will post this alternative at the end (of this already very long post).

***


The parameter that governs Remote Desktop is "fDenyTSConnections" and can be found in the Windows registry in the location below. I will limit the output with the format-list cmdlet.

PS C:\> Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections | fl fDenyTSConnections

fDenyTSConnections : 1


Since the parameters are apparently both positional (meaning they can be omitted), we can abbreviate as follows:

PS C:\> Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' fDenyTSConnections | fl fDenyTSConnections

fDenyTSConnections : 1

The value "1" indicates that Terminal Connections (or Remote Desktop connections) are indeed denied. We will need to change this. 
 
We also need to manage user authentication (enable it or not). It is more secure (thus generally preferable) to enable it. However, older versions of Windows (XP,2003) may not be able to connect this way.

PS C:\> Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' UserAuthentication | fl UserAuthentication

UserAuthentication : 1


So, Remote Desktop is disabled and User Authentication is enabled. The latter, of course, has no effect, since the former is not enabled.

Now let's look at the status of the Windows Firewall, concerning Remote Desktop access.


PS C:\> Get-NetFirewallRule | where {$_.Name -like "*Desktop*"} | fl Name,Enabled,Profile,Direction

Name      : RemoteDesktop-UserMode-In-TCP
Enabled   : False
Profile   : Public
Direction : Inbound


Name      : RemoteDesktop-UserMode-In-UDP
Enabled   : False
Profile   : Public
Direction : Inbound


This only applies to the Public profile which has no effect on the Domain (or Private) profile which interests me.

Let's try this:

PS C:\> Get-NetFirewallRule -DisplayGroup "Remote Desktop" | fl Name,Enabled,Profile,Direction

Name      : RemoteDesktop-UserMode-In-TCP
Enabled   : False
Profile   : Public
Direction : Inbound

Name      : RemoteDesktop-UserMode-In-UDP
Enabled   : False
Profile   : Public
Direction : Inbound

Name      : {12954380-D013-432D-A2A1-646FC09BB918}
Enabled   : False
Profile   : Domain, Private
Direction : Inbound

Name      : {9CD18D97-6B13-4463-BA5E-A2794D7237E9}
Enabled   : False
Profile   : Domain, Private
Direction : Inbound



For some reason, in the Powershell output, the name of the firewall rule is a long GUID. I'll deal with that in a moment...


Enable Remote Desktop

OK, that is the status quo. Now let's enable remote desktop with this cmdlet:

PS C:\> Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' fDenyTSConnections -value 0

This is the result:

PS C:\> Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' fDenyTSConnections | fl fDenyTSConnections

fDenyTSConnections : 0

User Authentication is at "1" (enabled) already and we will leave it as that because all clients likely to connect to the Windows 2012 servers are running Windows Vista / Server 2008 or above.


Enable Windows Firewall rules

I found - but am not satisfied - with the following command since it enables Remote Desktop in the Public profile. Although one could argue that it does not matter if we are behind a perimeter firewall anyway.

PS C:\> Enable-NetFirewallRule -DisplayGroup "Remote Desktop"

PS C:\> Get-NetFirewallRule -DisplayGroup "Remote Desktop" | fl Name,Enabled,Profile,Direction

Name      : RemoteDesktop-UserMode-In-TCP
Enabled   : True
Profile   : Public
Direction : Inbound

Name      : RemoteDesktop-UserMode-In-UDP
Enabled   : True
Profile   : Public
Direction : Inbound

Name      : {12954380-D013-432D-A2A1-646FC09BB918}
Enabled   : True
Profile   : Domain, Private
Direction : Inbound



Although more complex, the command that follows (in bold red) provides exactly what I prefer.

I first test to see what rules the following cmdlet will produce...

PS C:\> Get-NetFirewallRule | where {$_.DisplayGroup -eq "Remote Desktop" -and $_.Profile -match "Domain"} | fl Name,DisplayGroup,Profile,Enabled

Name         : {12954380-D013-432D-A2A1-646FC09BB918}
DisplayGroup : Remote Desktop
Profile      : Domain, Private
Enabled      : False

Name         : {9CD18D97-6B13-4463-BA5E-A2794D7237E9}
DisplayGroup : Remote Desktop
Profile      : Domain, Private
Enabled      : False




And then pipeline the result to the Enable-NetFirewallRule cmdlet. This allows Remote Desktop access. I will refrain from lengthening this post any longer by posting the result.

PS C:\> Get-NetFirewallRule | where {$_.DisplayGroup -eq "Remote Desktop" -and $_.Profile -match "Domain"} | Enable-NetFirewallRule

To open the Remote Desktop tool from the command line, we can simply type (and then presse Enter):

mstsc.exe

And then provide necessary credentials.



***


Alternate method of enabling Remote Desktop using the Get-WMIObject cmdlet with the SetAllowTsConnections method:


PS C:\> (Get-WmiObject -Class "Win32_TerminalServiceSetting" -NameSpace root\cimv2\terminalservices).AllowTsConnections
0


PS C:\> (Get-WmiObject -Class "Win32_TerminalServiceSetting" -NameSpace root\cimv2\terminalservices).SetAllowTsConnections(1)

[snip]

Here we have allowed TsConnections by setting the value to "1".


The result:

PS C:\> (Get-WmiObject -Class "Win32_TerminalServiceSetting" -NameSpace root\cimv2\terminalservices).AllowTsConnections
1




--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------

In conclusion...

This was a useful though time-consuming adventure with Powershell but if you have the "Full" install of Windows Server 2012, as opposed to the Server Core install, and want to configure (among others) Remote Desktop, just use the GUI.

No comments:

Post a Comment