-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated Bootstrap-RemoteHostViaSSHWithPSRemoting.ps1
- Loading branch information
Showing
1 changed file
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
<# | ||
# Get a Windows 11 ISO ready to install | ||
# Create new bootable Windows 11 ISO via D:\ drive with all of the files generated by Rufus | ||
New-VHD -Path "E:\HDDs\Win11InstallerViaRufus\Win11_22H2_v2.vhdx" -Fixed -SizeBytes 10GB | ||
# Use the Disk Management GUI to initialize the new disk and create a new simple NTFS volume and mount it as D:\ (or whatever letter) | ||
# Use the Rufus GUI to create a bootable Windows 11 D:\ drive with all desired customizations | ||
# Copy the boot files to a folder on a different letter drive like C:\ | ||
$ISOFilesDir = "C:\Win1122H2v2_ISO_via_Rufus" | ||
$ISOFileOutputPath = "C:\Users\adminuser\Downloads\Custom_Win11_22H2_v2_adminuser_via_Rufus.iso" | ||
if (!(Test-Path $ISOFilesDir)) {$null = New-Item -Path $ISOFilesDir -ItemType Directory -Force} | ||
Copy-Item -Path D:\* -Destination $ISOFilesDir -Recurse -Force | ||
# Make sure you have Windows ADK installed and run the following commands in an elevated PowerShell prompt | ||
cd "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg" | ||
Invoke-Expression ".\oscdimg.exe -m -o -u2 -udfver102 -bootdata:2#p0,e,b$ISOFilesDir\boot\etfsboot.com#pEF,e,b$ISOFilesDir\efi\microsoft\boot\efisys.bin -lWinBoot $ISOFilesDir $ISOFileOutputPath" | ||
#> | ||
|
||
# Bootstrap new Windows Workstation | ||
# You Just need to have SSHD installed and running on the remote host and default shell must be powershell.exe | ||
# To get SSHD installed, on the new Windows 11 machine, lauch Powershell as admin and run the following command: | ||
# Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/pldmgg/misc-powershell/master/MyScripts/Install-SSHD.ps1')) | ||
|
||
# Next, from your local workstation, run: | ||
$ScriptsDir = "C:\Scripts" | ||
@("$ScriptsDir\temp", "$ScriptsDir\logs", "$ScriptsDir\bin", "$ScriptsDir\powershell") | foreach { | ||
if (!(Test-Path $_)) { | ||
$null = New-Item -Path $_ -ItemType Directory -Force | ||
} | ||
} | ||
|
||
$ModuleBaseUri = "https://raw.githubusercontent.com/pldmgg/misc-powershell/master/MyModules" | ||
$ScriptsBaseUri = "https://raw.githubusercontent.com/pldmgg/misc-powershell/master/MyScripts" | ||
@( | ||
"$ModuleBaseUri/BootstrapRemoteHost.psm1" | ||
"$ModuleBaseUri/MiniServeModule.psm1" | ||
"$ModuleBaseUri/TTYDModule.psm1" | ||
"$ScriptsBaseUri/Install-ZeroTier.ps1" | ||
) | foreach { | ||
if (!(Test-Path "$ScriptsDir\powershell\$(Split-Path $_ -Leaf)")) { | ||
$null = Invoke-WebRequest -Uri $_ -OutFile "$ScriptsDir\powershell\$(Split-Path $_ -Leaf)" | ||
} | ||
} | ||
|
||
$ScriptsDir = "C:\Scripts" | ||
Import-Module "$ScriptsDir\powershell\BootstrapRemoteHost.psm1" | ||
$NewComputerName = "win11services1" | ||
$RemoteIPAddress = "192.168.2.68" | ||
$RemoteUserName = "adminuser" | ||
$SSHUserAndHost = $RemoteUserName + "@" + $RemoteIPAddress | ||
$SSHPrivateKeyPath = "C:\Users\adminuser\.ssh\id_rsa_for_remote_commands" | ||
$SSHPublicKeyPath = $SSHPrivateKeyPath + ".pub" | ||
$ZTScriptPath = "$ScriptsDir\powershell\Install-ZeroTier.ps1" | ||
$ZTNetworkID = 'id' | ||
$ZTToken = 'token' | ||
Invoke-ScaffoldingOnRemoteHost -RemoteUserName $RemoteUserName -RemoteIPAddress $RemoteIPAddress | ||
|
||
$SendKeyParams = @{ | ||
RemoteUserName = $RemoteUserName | ||
RemoteIPAddress = $RemoteIPAddress | ||
SSHPrivateKeyPath = $SSHPrivateKeyPath | ||
SSHPublicKeyPath = $SSHPublicKeyPath | ||
} | ||
Send-SSHKeyToRemoteHost @SendKeyParams | ||
|
||
# Set Execution Policy to RemoteSigned so that scripts created locally can run | ||
ssh -i $SSHPrivateKeyPath $SSHUserAndHost "powershell.exe -ExecutionPolicy Bypass -Command `"Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`"" | ||
|
||
# Set profile.ps1 | ||
# The below line at the top of profile.ps1 ensures that $env:Path does not have any repeated entries | ||
#The final line within profile.ps1 looks like - $env:Path = ($env:Path -split ';' | Sort-Object | Get-Unique) -join ';' | ||
ssh -i $SSHPrivateKeyPath $SSHUserAndHost "cmd /c echo `"```$env:Path = (```$env:Path -split ';' | Sort-Object | Get-Unique) -join ';'`" > C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1; (Get-Content C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1).Trim('`"') | Out-File C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1" | ||
|
||
# Install Pwsh | ||
$PwshScriptPath = "$ScriptsDir\powershell\Install-Pwsh.ps1" | ||
$SCPPwshRemoteLocationString = $RemoteUserName + '@' + $RemoteIPAddress + ':' + $PwshScriptPath | ||
scp.exe -i $SSHPrivateKeyPath $PwshScriptPath $SCPPwshRemoteLocationString | ||
ssh -i $SSHPrivateKeyPath $SSHUserAndHost "powershell.exe -ExecutionPolicy Bypass -Command `"& $PwshScriptPath`"" | ||
# Optionally set the default sshd shell to pwsh in the registry. | ||
ssh -i $SSHPrivateKeyPath $SSHUserAndHost "powershell.exe -ExecutionPolicy Bypass -Command `"Set-ItemProperty -Path HKLM:\SOFTWARE\OpenSSH -Name DefaultShell -Value ((Get-Command pwsh).Source); Restart-Service sshd`"" | ||
# NOTE: If you're going to use Invoke-Command/New-PSSession over ssh, then the below sshd_config subsystem changes are required | ||
$SSHDConfigPath = "C:\ProgramData\ssh\sshd_config" | ||
$PwshSubsystemString = "Subsystem powershell c:/progra~1/powershell/7/pwsh.exe -sshs -nologo" | ||
$OverrideSubsystemsString = "# override default of no subsystems" | ||
$RemoteSSHDConfig = ssh -i $SSHPrivateKeyPath $SSHUserAndHost "powershell.exe -ExecutionPolicy Bypass -Command `"Stop-Service sshd; if ((Get-Content '$SSHDConfigPath') -notcontains '$PwshSubsystemString') {(Get-Content '$SSHDConfigPath') -replace [Regex]::Escape('$OverrideSubsystemsString'), ('$OverrideSubsystemsString' + [Environment]::NewLine + '$PwshSubsystemString') | Set-Content '$SSHDConfigPath'}; Start-Service sshd; Get-Content '$SSHDConfigPath'`"" | ||
|
||
# Now you can use pwsh remoting commands. Create a PSSession and then we can use Invoke-Command | ||
try { | ||
$PSSession1 = New-PSSession -HostName $RemoteIPAddress -UserName $RemoteUserName -IdentityFilePath $SSHPrivateKeyPath -ErrorAction Stop | ||
} catch { | ||
Write-Output "Failed to create PSSession1 to $RemoteIPAddress" | ||
Write-Error $_ | ||
return | ||
} | ||
|
||
Invoke-Command $PSSession1 -ScriptBlock { | ||
$ScriptsDir = $using:ScriptsDir | ||
$NewComputerName = $using:NewComputerName | ||
|
||
# Enable ICMP Ping | ||
Set-NetFirewallRule -DisplayName 'File and Printer Sharing (Echo Request - ICMPv4-In)' -Enabled True | ||
|
||
# Enable RDP | ||
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' -Value 0 | ||
# Disable RDP via | ||
# ssh -i $SSHPrivateKeyPath $SSHUserAndHost "powershell.exe -ExecutionPolicy Bypass -Command `"Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' -Value 1`"" | ||
|
||
# Set Timezone | ||
Get-TimeZone -Id 'Eastern Standard Time' | Set-TimeZone | ||
|
||
# Rename Computer | ||
Rename-Computer -NewName $NewComputerName -Restart | ||
} | ||
|
||
# Remote Machine is rebooting, so destroy the PSSession | ||
$PSSession1 | Remove-PSSession | ||
|
||
Write-Output "Remote Host is rebooting after renaming it...please wait 2 minutes..." | ||
Start-Sleep -Seconds 90 | ||
|
||
# Create a new PSSession to ensure that the machine has rebooted | ||
try { | ||
$PSSession2 = New-PSSession -HostName $RemoteIPAddress -UserName $RemoteUserName -IdentityFilePath $SSHPrivateKeyPath -ErrorAction Stop | ||
} catch { | ||
Write-Output "Failed to create PSSession2 to $RemoteIPAddress" | ||
Write-Error $_ | ||
return | ||
} | ||
|
||
# Install ZeroTier | ||
$SCPRemoteLocationString = $RemoteUserName + '@' + $RemoteIPAddress + ':' + $ZTScriptPath | ||
scp.exe -i $SSHPrivateKeyPath $ZTScriptPath $SCPRemoteLocationString | ||
|
||
Invoke-Command $PSSession2 -ScriptBlock { | ||
$ScriptsDir = $using:ScriptsDir | ||
$ZTScriptPath = $using:ZTScriptPath | ||
$ZTNetworkID = $using:ZTNetworkID | ||
$ZTToken = $using:ZTToken | ||
|
||
# Install ZeroTier | ||
& $ZTScriptPath -NetworkID $ZTNetworkID -Token $ZTToken | ||
|
||
# Disable Bitlocker and Decrypt on ALL Volumes | ||
Disable-BitLocker -MountPoint (Get-BitLockerVolume) -Confirm:$false | ||
|
||
# Install the latest version of winget | ||
$Owner = "microsoft" | ||
$Repo = "winget-cli" | ||
$ReleaseInfo = Invoke-RestMethod -Uri "https://api.github.com/repos/$Owner/$Repo/releases/latest" | ||
$Asset = $ReleaseInfo.assets | Where-Object {$_.Name -match "msixbundle"} | ||
$AssetUrl = $Asset.browser_download_url | ||
$AssetName = $Asset.Name | ||
$AssetVersion = [version]$($AssetUrl.Split("/")[-2] -replace 'v','') | ||
$DownloadPath = "$env:USERPROFILE\Downloads\$AssetName" | ||
Invoke-WebRequest -Uri $AssetUrl -OutFile $DownloadPath -ErrorAction Stop | ||
$MsixBundleRemotePath = "$ScriptsDir\bin\$AssetName" | ||
|
||
dism.exe /Online /Add-ProvisionedAppxPackage /PackagePath:$MsixBundleRemotePath /SkipLicense | ||
|
||
winget install Google.Chrome | ||
winget install NoMachine.NoMachine | ||
#winget install VMware.WorkstationPlayer | ||
#Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All -NoRestart; Restart-Computer -Force | ||
} | ||
|
||
# Remote Machine might be rebooting, so destroy the PSSession | ||
$PSSession2 | Remove-PSSession | ||
|
||
Write-Output "Remote Host is rebooting after HyperV install...please wait 2 minutes..." | ||
Start-Sleep Seconds 90 | ||
|
||
# Create a new PSSession to ensure that the machine has rebooted | ||
try { | ||
$PSSession3 = New-PSSession -HostName $RemoteIPAddress -UserName $RemoteUserName -IdentityFilePath $SSHPrivateKeyPath -ErrorAction Stop | ||
} catch { | ||
Write-Output "Failed to create PSSession3 to $RemoteIPAddress" | ||
Write-Error $_ | ||
return | ||
} | ||
|
||
Invoke-Command $PSSession3 -ScriptBlock { | ||
# Install Chocolatey | ||
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) | ||
|
||
# Update Machine and User PATH | ||
[Environment]::SetEnvironmentVariable('Path', (([Environment]::GetEnvironmentVariable('Path', 'Machine')).Trim(';') + ';C:\ProgramData\chocolatey\bin;C:\ProgramData\chocolatey\lib'), 'Machine') | ||
[Environment]::SetEnvironmentVariable('Path', (([Environment]::GetEnvironmentVariable('Path', 'User')).Trim(';') + ';C:\ProgramData\chocolatey\bin;C:\ProgramData\chocolatey\lib'), 'User') | ||
|
||
choco install lockhunter -y | ||
choco install vscode -y | ||
choco install nano -y | ||
choco install veeam-agent -y | ||
|
||
# Veeam Agent Requires a reboot | ||
Restart-Computer -Force | ||
} | ||
|
||
Write-Output "Remote Host is rebooting after installing Veeam Agent...please wait 2 minutes..." | ||
Start-Sleep -Seconds 90 | ||
|
||
# OPTIONAL 1: Install WSL | ||
# IMPORTANT NOTE: For some reason the installer fails unless it thinks you're logged into a GUI session | ||
mstsc /v:$RemoteIPAddress | ||
# And within the RDP Session, run the following commands in the below comment block | ||
<# | ||
wsl --install | ||
Restart-Computer -Force | ||
#winget install -e --id Canonical.Ubuntu.2204 | ||
mstsc /v:$RemoteIPAddress | ||
# Just wait for wsl to pop open a window to finish the install | ||
wsl | ||
sudo apt update && sudo apt upgrade -y | ||
sudo apt install openssh-server -y | ||
sudo sed -i -E 's,^#?Port.*$,Port 2222,' /etc/ssh/sshd_config | ||
sudo service ssh restart | ||
sudo sh -c "echo '${USER} ALL=(root) NOPASSWD: /usr/sbin/service ssh start' >/etc/sudoers.d/service-ssh-start" | ||
exit | ||
# Now you should be back in powershell on the remote host within an RDP session | ||
# Allow ssh traffic on port 2222 | ||
New-NetFirewallRule -Name wsl_sshd -DisplayName 'OpenSSH Server (sshd) for WSL' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 2222 | ||
# Now we want to create a scheduled task that will start WSL AND ssh within WSL on boot | ||
$ScriptOutputPath = "C:\Scripts\bin\wsl_sshd.ps1" | ||
$ScriptContentAsString = @' | ||
# Start SSH service in WSL | ||
bash.exe -c "sudo /usr/sbin/service ssh start" | ||
# Remove port proxy rule | ||
netsh.exe interface portproxy delete v4tov4 listenport=2222 listenaddress=0.0.0.0 protocol=tcp | ||
# Get IP address from WSL | ||
$IP = (wsl.exe hostname -I).Trim() | ||
# Add port proxy rule with the obtained IP address | ||
netsh.exe interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=2222 connectaddress=$IP | ||
'@ | ||
$TaskName = "Start WSL SSHD on Boot" | ||
$TaskUser = "ttadmin" | ||
$ScriptContentAsString | Out-File -FilePath $ScriptOutputPath -Encoding ascii -Force | ||
$TenSecondsFromNow = $(Get-Date).Add($(New-TimeSpan -Seconds 10)) | ||
$TaskTrigger = New-ScheduledTaskTrigger -AtStartup | ||
$Options = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -WakeToRun -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit $(New-TimeSpan -Hours 1) | ||
$Passwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((Read-Host -Prompt "Enter password" -AsSecureString))) | ||
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -Command `"& $ScriptOutputPath`"" | ||
Register-ScheduledTask -TaskName $TaskName -Trigger $TaskTrigger -Settings $Options -User $TaskUser -Password $Passwd -Action $Action -ErrorAction Stop | ||
#> | ||
|
||
# OPTIONAL 2: Install Windows Subsystem for Android | ||
# NOTE: If the below $AppxUri doesn't work, you can get the latest version by navigating to https://store.rg-adguard.net/ and in the URL box, input: www.microsoft.com/en-us/p/windows-subsystem-for-android/9p3395vx91nr | ||
<# | ||
$AppxUri = 'http://tlu.dl.delivery.mp.microsoft.com/filestreamingservice/files/862fe146-d265-4305-9951-f8c5983f427d?P1=1689726418&P2=404&P3=2&P4=mHD%2fatoZ49xCdHZMGnivAxQwc0j86RlROyiL2pMGxg0%2fb6ezs9lPQYFHzQTTPcA9XkalD6focoDkR1%2bkSgKp2Q%3d%3d' | ||
$OutFilePath = 'C:\Users\ttadmin\Downloads\windows_subsystem_for_android.msixbundle' | ||
ssh -i $SSHPrivateKeyPath $SSHUserAndHost "pwsh.exe -ExecutionPolicy Bypass -Command `"Invoke-WebRequest -uri '$AppxUri' -OutFile '$OutFilePath'; dism.exe /Online /Add-ProvisionedAppxPackage /PackagePath:$OutFilePath /SkipLicense`"" | ||
#> | ||
|
||
# OPTIONAL 3: Install TTYD | ||
# Install TTYD and create scheduled task to run it as a specific user (i.e. the person who uses the PC most often) | ||
<# | ||
$CreateRemoteSchdTaskParams = @{ | ||
RemoteUserName = $RemoteUserName | ||
RemoteIPAddress = $RemoteIPAddress | ||
ModuleDir = "C:\Scripts\powershell" | ||
SSHPrivateKeyPath = $SSHPrivateKeyPath | ||
NetworkInterfaceAlias = "ZeroTier One [$ZTNetworkID]" | ||
TaskUser = "ttadminbackup" | ||
TTYDWebUser = "ttydadmin" | ||
TTYDWebPassword = "MyP@ssword123!" | ||
} | ||
Create-RemoteTTYDScheduledTask @CreateRemoteSchdTaskParams | ||
#> | ||
|
||
# OPTIONAL 4: Prompt the remote user for a secure string | ||
<# | ||
Import-Module "$ScriptsDir\powershell\MiniServeModule.psm1" | ||
# Make sure you have miniserve.exe on the local workstation | ||
$NetworkInterfaceAlias = "ZeroTier One [$ZTNetworkID]" | ||
Install-MiniServe -NetworkInterfaceAlias $NetworkInterfaceAlias | ||
$PromptSSParams = @{ | ||
RemoteUserName = $RemoteUserName | ||
RemoteIPAddress = $RemoteIPAddress | ||
SSHPrivateKeyPath = $SSHPrivateKeyPath | ||
MiniServeNetworkInterfaceAlias = $NetworkInterfaceAlias | ||
RemovePwdFile = $False | ||
} | ||
$UserString = Prompt-ActiveUserForSecureString @PromptSSParams | ||
#> |