commit
						5fa02f29d7
					
				@ -0,0 +1,5 @@ | 
				
			|||||||
 | 
					@echo off | 
				
			||||||
 | 
					pushd %~dp0 | 
				
			||||||
 | 
					powershell -ExecutionPolicy Bypass -File .\Setup-PSRemoting.ps1 | 
				
			||||||
 | 
					popd | 
				
			||||||
 | 
					@pause | 
				
			||||||
@ -0,0 +1,530 @@ | 
				
			|||||||
 | 
					#Requires -RunAsAdministrator | 
				
			||||||
 | 
					#Requires -Version 5 | 
				
			||||||
 | 
					#Requires -Modules Microsoft.PowerShell.LocalAccounts | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[CmdletBinding()] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Param ( | 
				
			||||||
 | 
					    # Whether or not to enable debugging messages | 
				
			||||||
 | 
					    [bool]$EnableDebug = $true, | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Username for remote administration account | 
				
			||||||
 | 
					    [String]$ServiceUser = "remote-admin", | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Default password for this account | 
				
			||||||
 | 
					    [String]$ServicePassword = "bootstrap123" | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Set-StrictMode -Version 2 # don't force v3 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[bool]$HasSSL = $false | 
				
			||||||
 | 
					$ServiceUserDescription = "Service user for remote administration" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Log($Message, [String]$Color = $null, $NoNewline = $false) { | 
				
			||||||
 | 
					    if ($Color) { | 
				
			||||||
 | 
					        $ExtraParms = @{"ForegroundColor" = $Color} | 
				
			||||||
 | 
					    } else { | 
				
			||||||
 | 
					        $ExtraParms = @{} | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Write-Host $Message @ExtraParms -NoNewline:$NoNewline | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Debug($Message) { | 
				
			||||||
 | 
					    if ($EnableDebug) { | 
				
			||||||
 | 
					        Log $Message -Color Cyan | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Change($Message) { | 
				
			||||||
 | 
					    Log "! $Message" -Color Yellow | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Error($Message) { | 
				
			||||||
 | 
					    Log "ERROR: $Message" -Color Red | 
				
			||||||
 | 
					    Exit | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Assert($Condition, $Message) { | 
				
			||||||
 | 
					    if (!$Condition) { | 
				
			||||||
 | 
					        Error -Message $Message | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Count($E) { | 
				
			||||||
 | 
					    return ($E | Measure).Count | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function New-Credential($User, $Password) { | 
				
			||||||
 | 
					    return New-Object System.Management.Automation.PSCredential($User, (ConvertTo-SecureString $Password -AsPlainText -Force)) | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Checks service account for compliance | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Verify-LocalUser($User) { | 
				
			||||||
 | 
					    $Name = $User.Name | 
				
			||||||
 | 
					    Debug "Verifying account `"$Name`"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Is it enabled? | 
				
			||||||
 | 
					    if (!$User.Enabled) { | 
				
			||||||
 | 
					        Change "Enabling account `"$Name`"" | 
				
			||||||
 | 
					        $User | Enable-LocalUser -ErrorAction Stop | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Check if account is set to never expire | 
				
			||||||
 | 
					    if ($User.AccountExpires -ne $null) { | 
				
			||||||
 | 
					        Change "Changing account expiration policy for `"$Name`"" | 
				
			||||||
 | 
					        $User | Set-LocalUser -AccountNeverExpires -ErrorAction Stop | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Do the same with its password | 
				
			||||||
 | 
					    if ($User.PasswordExpires -ne $null) { | 
				
			||||||
 | 
					        Change "Changing password expiration policy for `"$Name`"" | 
				
			||||||
 | 
					        $User | Set-LocalUser -PasswordNeverExpires -ErrorAction Stop | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Account description is not really important but it doesn't hurt to check | 
				
			||||||
 | 
					    if ($User.Description -ne $ServiceUserDescription) { | 
				
			||||||
 | 
					        Change "Changing description for `"$Name`"" | 
				
			||||||
 | 
					        $User | Set-LocalUser -Description $ServiceUserDescription -ErrorAction Stop | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Validate group membership for Administrators group | 
				
			||||||
 | 
					    if ((Get-LocalGroupMember -SID S-1-5-32-544 | select -ExpandProperty SID) -notcontains $User.SID.Value) { | 
				
			||||||
 | 
					        Change "Changing group membership for `"$Name`" - adding account to `"Administrators`" local group" | 
				
			||||||
 | 
					        Add-LocalGroupMember -SID S-1-5-32-544 -Member $User -ErrorAction Stop | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Do the same for RMS group, if it exists | 
				
			||||||
 | 
					    if ((Get-LocalGroupMember -SID S-1-5-32-580 | select -ExpandProperty SID) -notcontains $User.SID.Value) { | 
				
			||||||
 | 
					        if (Get-LocalGroup -SID S-1-5-32-580 -ErrorAction SilentlyContinue) { | 
				
			||||||
 | 
					            Change "Changing group membership for `"$Name`" - adding account to `"Remote Management Users`" local group" | 
				
			||||||
 | 
					            Add-LocalGroupMember -SID S-1-5-32-580 -Member $User -ErrorAction Stop | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Debug "Verification complete" | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PROCESS: | 
				
			||||||
 | 
					# Find, create, edit group membership of service account and validate it | 
				
			||||||
 | 
					# DESIRED STATE: an active service account on local machine | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-ServiceAccount { | 
				
			||||||
 | 
					    Debug "* Processing: Service account" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Verify if there is already an user | 
				
			||||||
 | 
					    $User = Get-LocalUser -Name $ServiceUser -ErrorAction SilentlyContinue | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ($User) { | 
				
			||||||
 | 
					        Log "Found existing service account: `"$ServiceUser`"" | 
				
			||||||
 | 
					    } else { | 
				
			||||||
 | 
					        # Create new user and verify it | 
				
			||||||
 | 
					        Debug "No service account found, will create one" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Handle passwordless user (may be useful for pure cert auth, not really the case now) | 
				
			||||||
 | 
					        if ($ServicePassword -eq $null -or $ServicePassword.Length -eq 0) { | 
				
			||||||
 | 
					            $PasswordSplat = @{ | 
				
			||||||
 | 
					                "NoPassword" = $true | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Log "Using passwordless login" | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            $PasswordSplat = @{ | 
				
			||||||
 | 
					                "Password" = ConvertTo-SecureString $ServicePassword -AsPlainText -Force | 
				
			||||||
 | 
					                "PasswordNeverExpires" = $true | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Create an user | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            $User = New-LocalUser -Name $ServiceUser ` | 
				
			||||||
 | 
					                                  -Description $ServiceUserDescription ` | 
				
			||||||
 | 
					                                  -AccountNeverExpires ` | 
				
			||||||
 | 
					                                  @PasswordSplat | 
				
			||||||
 | 
					        } catch { | 
				
			||||||
 | 
					            Error "Caught an exception while creating service account `"$ServiceUser`"" | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        Assert $User "Failed to create service account `"$ServiceUser`"" | 
				
			||||||
 | 
					        Change "Created service account `"$ServiceUser`"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Add this user to Administrators local group | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            Add-LocalGroupMember -SID S-1-5-32-544 -Member $User -ErrorAction Stop | 
				
			||||||
 | 
					        } catch { | 
				
			||||||
 | 
					            Error "Caught an exception while adding service account `"$ServiceUser`" to local group `"Administrators`"" | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        Change "Added account `"$ServiceUser`" to local group `"Administrators`"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Check if RMU group exists | 
				
			||||||
 | 
					        if (!(Get-LocalGroup -SID S-1-5-32-580 -ErrorAction SilentlyContinue)) { | 
				
			||||||
 | 
					            Log "`"Remote Management Users`" group is missing from this system - will not add user to this group" -Color Yellow | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Also add user to RMU local group | 
				
			||||||
 | 
					            try { | 
				
			||||||
 | 
					                Add-LocalGroupMember -SID S-1-5-32-580 -Member $User -ErrorAction Stop | 
				
			||||||
 | 
					            } catch { | 
				
			||||||
 | 
					                Error "Caught an exception while adding service account `"$ServiceUser`" to local group `"Remote Management Users`"" | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					            Change "Added account `"$ServiceUser`" to local group `"Remote Management Users`"" | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Verify user, no matter whether it was found or created | 
				
			||||||
 | 
					    Verify-LocalUser -User $User | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PROCESS: | 
				
			||||||
 | 
					# Ensure that local network connection profile category is set to Private, so firewall rules and Enable-PSRemoting should work correctly | 
				
			||||||
 | 
					# Network interface are selected by their DNS suffix | 
				
			||||||
 | 
					# DESIRED STATE: category of local network interface is set to Private | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-NetworkProfile { | 
				
			||||||
 | 
					    Debug "* Processing: Network Profiles" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $Interfaces = gwmi -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName . | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $Interfaces = $Interfaces | ? { $_.DNSDomain -match ".*corp.monroe.fitness$" } | 
				
			||||||
 | 
					    if (!$Interfaces) { | 
				
			||||||
 | 
					        # early return if there is no compatible network interface | 
				
			||||||
 | 
					        Log -Color Yellow "Failed to find local network interface with corp DNS suffix - skipping network profile check" | 
				
			||||||
 | 
					    } else { | 
				
			||||||
 | 
					        Debug "Found $(Count $Interfaces) compatible network interfaces" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $Interfaces.InterfaceIndex | % { | 
				
			||||||
 | 
					            if ((Get-NetConnectionProfile -InterfaceIndex $_).NetworkCategory -eq "Public") { | 
				
			||||||
 | 
					                Change "Setting network category of interface #$_ to Private" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                try { | 
				
			||||||
 | 
					                    Set-NetConnectionProfile -InterfaceIndex $_ -NetworkCategory "Private" -ErrorAction Stop | 
				
			||||||
 | 
					                } catch { | 
				
			||||||
 | 
					                    Error "Caught an exception when setting network profile category" | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PROCESS: | 
				
			||||||
 | 
					# Start up WinRM service and ensure that it has automatic start type | 
				
			||||||
 | 
					# DESIRED STATE: WinRM service is running and is set to auto-start on next boot | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-WinRM { | 
				
			||||||
 | 
					    Debug "* Processing: WinRM" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try { | 
				
			||||||
 | 
					        $Service = Get-Service -Name "WinRM" | 
				
			||||||
 | 
					        Assert $Service "WinRM service does not exist on this machine" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($Service.StartType -ne "Automatic") { | 
				
			||||||
 | 
					            Change "Setting WinRM startup type to Automatic" | 
				
			||||||
 | 
					            $Service | Set-Service -StartupType Automatic -ErrorAction Stop | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($Service.Status -in "Stopped", "StopPending") { | 
				
			||||||
 | 
					            Change "Starting WinRM service" | 
				
			||||||
 | 
					            $Service | Start-Service -ErrorAction Stop | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } catch { | 
				
			||||||
 | 
					        Error "Caught an exception while setting up WinRM service" | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PROCESS: | 
				
			||||||
 | 
					# Set LocalAccountTokenFilterPolicy registry value to 1 | 
				
			||||||
 | 
					# DESIRED STATE: LocalAccountTokenFilterPolicy = 1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-Registry { | 
				
			||||||
 | 
					    Debug "* Processing: Registry" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $Key = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" | 
				
			||||||
 | 
					    $Name = "LocalAccountTokenFilterPolicy" | 
				
			||||||
 | 
					    $Value = 1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $Prop = Get-ItemProperty $Key -ErrorAction SilentlyContinue | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!$Prop) { | 
				
			||||||
 | 
					        Error "Parent registry key for $Name does not exist, skipping this step" | 
				
			||||||
 | 
					    } else { | 
				
			||||||
 | 
					        $M = Get-Member -InputObject $Prop -name $Name -Membertype Properties -ErrorAction SilentlyContinue | 
				
			||||||
 | 
					        if (!$M -or $Prop.$Name -ne $Value) { | 
				
			||||||
 | 
					            Log "$Name is set to an incorrect value or is empty" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Remove-ItemProperty $Key -Name $Name -Force -ErrorAction SilentlyContinue | 
				
			||||||
 | 
					            Change "Removed $Name from $Key" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Assert ((New-ItemProperty $Key -Name $Name -PropertyType "DWord" -Value $Value).$Name -eq $Value) "Failed to create `"$Name`" registry property" | 
				
			||||||
 | 
					            Change "Added $Name to $Key with value $Value" | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PROCESS: | 
				
			||||||
 | 
					# Enable PS Remoting | 
				
			||||||
 | 
					# DESIRED STATE: there is at least one session configuration and a WSMan listener (their validity will be checked later) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-PSRemoting { | 
				
			||||||
 | 
					    Debug "* Processing: PS Remoting" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # This snippet was mostly taken from Ansible script | 
				
			||||||
 | 
					    # TODO: most of remoting stuff is already taken care of - this function is just a failsafe | 
				
			||||||
 | 
					    if (!(Get-PSSessionConfiguration -Verbose:$false) -or !(Get-ChildItem WSMan:\localhost\Listener)) { | 
				
			||||||
 | 
					        Log "No PS session configuration or listener found - enabling PS remoting" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Override local verbose preference | 
				
			||||||
 | 
					        $Pref = $VerbosePreference | 
				
			||||||
 | 
					        $VerbosePreference = "SilentlyContinue" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            Enable-PSRemoting -Force -ErrorAction Stop -Verbose:$false > $null | 
				
			||||||
 | 
					        } catch { | 
				
			||||||
 | 
					            Error "Caught an exception in Enable-PSRemoting" | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Change "PS Remoting enabled" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Set verbose preference back to its original value | 
				
			||||||
 | 
					        $VerbosePreference = $Pref | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create a HTTP listener | 
				
			||||||
 | 
					function Create-HTTPListener { | 
				
			||||||
 | 
					    New-Item 'WSMan:\localhost\Listener' -Transport HTTP -Address "*" -Force > $null | 
				
			||||||
 | 
					    Change "Created HTTP listener" | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Verify-Listener($Listener) { | 
				
			||||||
 | 
					    return ($Listener -and ` | 
				
			||||||
 | 
					            ($Listener.Keys -contains "Transport=HTTP" -or $Listener.Keys -contains "Transport=HTTPS") -and ` | 
				
			||||||
 | 
					            $Listener.Keys -contains "Address=*") | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PROCESS: | 
				
			||||||
 | 
					# Loop through PS listeners and ensure there's only one active HTTP listener | 
				
			||||||
 | 
					# (this ignores HTTPS listeners because they will be set up by Ansible later) | 
				
			||||||
 | 
					# DESIRED STATE: there is an active HTTP listener | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-Listeners { | 
				
			||||||
 | 
					    Debug "* Processing: WSMan listeners" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find valid listeners and also save all listeners | 
				
			||||||
 | 
					    $All = Get-ChildItem WSMan:\localhost\Listener | 
				
			||||||
 | 
					    $Valid = $All | ? {Verify-Listener -Listener $_} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((Count $All) -eq 1 -and (Count $Valid) -eq 1) { | 
				
			||||||
 | 
					        Log "Found 1 listener: `"$($Valid[0].Name)`"" # only one: either http (ok) or https (also ok) | 
				
			||||||
 | 
					    } else { | 
				
			||||||
 | 
					        # Remove all listeners | 
				
			||||||
 | 
					        $All | % { | 
				
			||||||
 | 
					            Change "Removing listener: `"$($_.Name)`"" | 
				
			||||||
 | 
					            try { | 
				
			||||||
 | 
					                $_ | Remove-Item -Force -Recurse | 
				
			||||||
 | 
					            } catch { | 
				
			||||||
 | 
					                # Continue even if an exception has happened | 
				
			||||||
 | 
					                Log -Color Yellow "Caught an exception while removing listener `"$($_.Name)`"" | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Create | 
				
			||||||
 | 
					        Create-HTTPListener | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Verify after creation | 
				
			||||||
 | 
					        $Valid = Get-ChildItem WSMan:\localhost\Listener | ? {Verify-Listener -Listener $_} | 
				
			||||||
 | 
					        Assert ((Count $Valid) -eq 1) "Listener was just created, but it's missing" | 
				
			||||||
 | 
					        Debug "Found listener after creation" | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $Listener = $Valid[0] | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PURPOSE: | 
				
			||||||
 | 
					# Search, validate and fix PS session configurations | 
				
			||||||
 | 
					# This also sets up a more secure SDDL | 
				
			||||||
 | 
					# DESIRED STATE: PS session configurations are validated and are now correct | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-SessionConfig { | 
				
			||||||
 | 
					    Debug "* Processing: PS Session Configuration" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $SDDL = "O:NSG:BAD:P(A;;GA;;;RM)(A;;GA;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)" | 
				
			||||||
 | 
					    Get-PSSessionConfiguration | ? {$_.Name -eq "microsoft.powershell" -or $_.Name -eq "microsoft.powershell32"} | % { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($_.SecurityDescriptorSddl -ne $SDDL) { | 
				
			||||||
 | 
					            Change "Changing SDDL on session configuration `"$($_.Name)`"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try { | 
				
			||||||
 | 
					                ($_ | Set-PSSessionConfiguration -SecurityDescriptorSddl $SDDL) > $null | 
				
			||||||
 | 
					            } catch { | 
				
			||||||
 | 
					                Log -Color Yellow "Caught an exception while changing SDDL on `"$($_.Name)`"" | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-PSRAuth { | 
				
			||||||
 | 
					    Debug "* Processing: PS Remoting Authentication" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $Auth = Get-ChildItem WSMan:\localhost\Service\Auth | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "Basic","Kerberos","Certificate" | % { | 
				
			||||||
 | 
					        if (($Auth | ? Name -eq $_).Value -eq $true) { | 
				
			||||||
 | 
					            Change "Disabling $_ authentication" | 
				
			||||||
 | 
					            Set-Item -Path "WSMan:\localhost\Service\Auth\$_" -Value $false | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (($Auth | ? Name -eq "CredSSP").Value -eq $false) { | 
				
			||||||
 | 
					        Change "Enabling CredSSP authentication" | 
				
			||||||
 | 
					        Enable-WSManCredSSP -Role Server -Force > $null | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Process-Firewall { | 
				
			||||||
 | 
					    Debug "* Processing: PS Remoting Firewall" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (Get-NetFirewallRule -Name "WINRM-HTTPS-In-TCP" -ErrorAction SilentlyContinue) { | 
				
			||||||
 | 
					        Debug "Found HTTPS rule, will disable HTTP rules" | 
				
			||||||
 | 
					        $Script:HasSSL = $true | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Get-NetFirewallRule -ErrorAction SilentlyContinue | ? {$_.Name -like "WINRM-HTTP-*" -and $_.Enabled -eq $true} | % { | 
				
			||||||
 | 
					            Change "Disabling firewall rule for PSR over HTTP: $($_.DisplayName)" | 
				
			||||||
 | 
					            Disable-NetFirewallRule -Name $_.Name | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } else { | 
				
			||||||
 | 
					        Debug "HTTPS rule is missing, will add HTTP rule" | 
				
			||||||
 | 
					        $Script:HasSSL = $false | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!(Get-NetFirewallRule -Name "WINRM-HTTP-In-TCP" -ErrorAction SilentlyContinue)) { | 
				
			||||||
 | 
					            Change "Adding firewall rule for PSR over HTTP" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            New-NetFirewallRule -Name "WINRM-HTTP-In-TCP" ` | 
				
			||||||
 | 
					                            -DisplayName "Windows Remote Management (HTTP-In)" ` | 
				
			||||||
 | 
					                            -Description "Inbound rule for Windows Remote Management via WS-Management. [TCP 5985]" ` | 
				
			||||||
 | 
					                            -Group "Windows Remote Management" ` | 
				
			||||||
 | 
					                            -Program "System" ` | 
				
			||||||
 | 
					                            -Protocol TCP ` | 
				
			||||||
 | 
					                            -LocalPort "5985" ` | 
				
			||||||
 | 
					                            -RemoteAddress "10.0.0.0/10" ` | 
				
			||||||
 | 
					                            -Action Allow ` | 
				
			||||||
 | 
					                            -Profile Domain,Private > $null | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $Rule = Get-NetFirewallRule -Name "WINRM-HTTP-In-TCP" -ErrorAction SilentlyContinue | 
				
			||||||
 | 
						if (!$Rule) { | 
				
			||||||
 | 
					            Error "HTTP rule is missing after its creation" | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($Rule.Enabled -eq $false) { | 
				
			||||||
 | 
					            Change "Enabling HTTP rule" | 
				
			||||||
 | 
					            $Rule | Enable-NetFirewallRule -ErrorAction Stop | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (($Rule | Get-NetFirewallAddressFilter).RemoteAddress -ne "10.0.0.0/255.192.0.0") { | 
				
			||||||
 | 
					            Change "Changing HTTP rule remote address" | 
				
			||||||
 | 
					            $Rule | Set-NetFirewallRule -RemoteAddress "10.0.0.0/10" -ErrorAction Stop | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($Rule.Profile -ne "Domain,Private") { | 
				
			||||||
 | 
					            Change "Changing HTTP rule profile" | 
				
			||||||
 | 
					            $Rule | Set-NetFirewallRule -Profile Domain,Private -ErrorAction Stop | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Test-PSR { | 
				
			||||||
 | 
					    Debug "* Processing: PS Remoting Test" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try { | 
				
			||||||
 | 
					        if ($Script:HasSSL) { | 
				
			||||||
 | 
					            Debug "Creating PS session through HTTPS" | 
				
			||||||
 | 
					            $Session = New-PSSession -UseSSL -ComputerName "localhost" -SessionOption (New-PSSessionOption -SkipRevocationCheck -SkipCNCheck) -Credential (New-Credential -User $ServiceUser -Password $ServicePassword) | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            Debug "Creating PS session through HTTP" | 
				
			||||||
 | 
					            $Session = New-PSSession -ComputerName "localhost" -Credential (New-Credential -User $ServiceUser -Password $ServicePassword) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } catch { | 
				
			||||||
 | 
					        Error "Caught an exception while setting up PS session: $_" | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Assert $Session "Failed to initiate local PS Remoting session" | 
				
			||||||
 | 
					    Assert ((Invoke-Command -Session $Session -ScriptBlock {Write-Output "test"}) -eq "test") "Received wrong output from local PS remoting session" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $Session | Remove-PSSession | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Log "PS remoting preparation script" -Color Green | 
				
			||||||
 | 
					Log "Debug mode is $(("off","on")[$EnableDebug])" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Process-ServiceAccount -Name $ServiceUser -Password $ServicePassword | 
				
			||||||
 | 
					Process-NetworkProfile | 
				
			||||||
 | 
					Process-Registry | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Process-WinRM | 
				
			||||||
 | 
					Process-PSRemoting | 
				
			||||||
 | 
					Process-Listeners | 
				
			||||||
 | 
					Process-SessionConfig | 
				
			||||||
 | 
					Process-PSRAuth | 
				
			||||||
 | 
					Process-Firewall | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Process-WinRM | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Test-PSR | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Log "Completed" -Color Green | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue