Skip to content

Instantly share code, notes, and snippets.

@Diagonactic
Last active June 30, 2017 20:43
Show Gist options
  • Save Diagonactic/7182862f01f521aa302730b26a787023 to your computer and use it in GitHub Desktop.
Save Diagonactic/7182862f01f521aa302730b26a787023 to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Looks up the first virtual machine and attempts to connect to it with Secure Shell using Public/Private Key Authentication
.DESCRIPTION
Used to make a connection to a Virtual Machine that has a DHCP address which changes often. Works in a non-elevated prompt, however, requires that the user be able to elevate
a session (ie. the user must know an Administrator password or be logged in as an administrative user in a non-elevated prompt). The script will elevate the user for the purpose
of getting the Hyper-V IP address, but will run the Secure Shell session as the non-elevated user if the session isn't elevated.
The script has the following requirements:
1. The SSH.EXE file must be in the $env:PATH or the directory that the script is executed from.
2. A private key with the file name 'id_rsa' that can be used to connect to the host is located either in $env:USERPROFILE\.ssh or within the Bash on Windows "root" user's
~/.ssh folder named 'id_rsa'. Note that the keypair doesn't have to be an RSA keypair, that filename was just used because it was what mine was named.
If the $env:USERPROFILE\.ssh folder isn't there, it'll be created and the script will attempt to copy the .ssh file from the Bash on Windows root folder.
The detected IP address is outputted before connecting since you might need it for something else (or just to verify that it's the right place).
If you have more than one VM and the target VM isn't the first (at $Index 0), you can run (in an elevated Powershell prompt):
(Get-VM | ?{$_.ReplicationMode -ne "Replica"} | Select -ExpandProperty NetworkAdapters | Select IPAddresses).IPAddresses
This will get a list of IP addresses assigned to the Virtual Machines on your computer. The Index will be the line number with the first line being 0.
.PARAMETER UserId
Specifies the user ID to login to the SSH session with
.PARAMETER VmIndex
Specifies the index of the Virtual Machine (default is 0)
#>
param(
[Parameter(Mandatory=$true, Position=0)][string]$UserId,
[int]$VmIndex=0
)
Function Get-ElevantedCommandLine {
[OutputType([string])]
param (
[Parameter(Mandatory = $true, Position=0)][string]$InputFile,
[Parameter(Mandatory = $true, Position=1)][string]$OutputFile,
[Parameter(Mandatory = $true, Position=2)][string]$ErrorFile,
[Parameter(Mandatory = $true, Position=3)][ScriptBlock] $ScriptBlock
)
process {
$Local:subCommand = "Set-Location '$($pwd.Path)'; `$output = Import-CliXml '$inputFile' | & { $($scriptblock.ToString()) } 2>&1 ; Out-File -FilePath '$errorFile' -InputObject `$error;Export-CliXml -Depth 1 -In `$output '$outputFile';"
return "-NonInteractive -NoProfile -EncodedCommand $([Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($Local:subCommand)))"
}
}
Function Invoke-ElevatedCommand {
param (
[Parameter(Mandatory = $true)][ScriptBlock] $Scriptblock,
[Parameter(ValueFromPipeline = $true)]$InputObject
)
begin {
Set-StrictMode -Version Latest
$inputItems = New-Object System.Collections.ArrayList
}
process {
$null = $inputItems.Add($inputObject)
}
end {
$outputFile = [IO.Path]::GetTempFileName()
$inputFile = [IO.Path]::GetTempFileName()
$errorFile = [IO.Path]::GetTempFileName()
$inputItems.ToArray() | Export-CliXml -Depth 1 $inputFile
$Local:commandLine = Get-ElevantedCommandLine $Local:inputfile $Local:outputfile $Local:errorFile -ScriptBlock $scriptBlock
(Start-Process -FilePath (Get-Command powershell).Definition -ArgumentList $Local:commandLine -Passthru -Verb RunAs -WindowStyle "Hidden").WaitForExit()
$errorMessage = $(gc $errorFile | Out-String)
if($errorMessage) { Write-Error -Message $errorMessage } else {
if((Get-Item $outputFile).Length -gt 0) {
Import-CliXml $outputFile
}
}
Remove-Item $outputFile, $inputFile, $errorFile
}
}
function Test-Administrator {
return (New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
function Get-CommandLocalOrPath {
[OutputType([string])]
param (
[Parameter(Mandatory=$true, Position=0)][string]$FileName
)
process {
$Local:retVal = ".\$FileName"
if (Test-Path "$Local:retVal" -PathType Leaf) {
return "$Local:retVal"
}
else {
$Local:retVal = (Get-Command $FileName -ErrorAction SilentlyContinue).Source
if (-not ([string]::IsNullOrWhiteSpace($Local:retVal))) {
if (Test-Path "$Local:retVal" -ErrorAction Ignore -PathType Leaf) {
return "$Local:retVal"
}
}
}
return $null
}
}
$Script:Ssh = Get-CommandLocalOrPath -FileName "ssh.exe"
if ($Script:Ssh -eq $null) {
Write-Host "Secure Shell client is not installed or not in path"
exit
}
$Script:BashOnWindowsKeyFile = [System.IO.Path]::Combine("$env:LOCALAPPDATA", "Lxss\root\.ssh\id_rsa")
$Script:LocalSshPath = [System.IO.Path]::Combine("$env:USERPROFILE", ".ssh")
$Script:LocalSshKeyFile = [System.IO.Path]::Combine("$env:USERPROFILE", ".ssh\id_rsa")
if (-not (Test-Path "$Script:LocalSshPath" -PathType Container)) {
Write-Host "An SSH directory was not found in your home drive - Attempting to create it"
mkdir "$Script:LocalSshPath"
$Local:Success = $?
if (-not ($Local:Success)) {
Write-Host "Couldn't create '$env:LocalSshPath'. Please create this manually and copy your private key to this folder"
exit
}
}
if (-not (Test-Path "$env:HOME\.ssh\id_rsa" -PathType Leaf)) {
if (-not (Test-Path "$Local:BashOnWindowsKeyFile" -PathType Leaf)) {
Write-Host "'id_rsa' file does not exist in Bash on Windows Profile. Copy your private key to '$Script:LocalSshKeyFile'"
exit
}
Copy "$Script:BashOnWindowsKeyFile" "$Script:LocalSshKeyFile"
$Local:Success = $?
if (-not ($Local:Success)) {
Write-Host "Couldn't copy '$Script:BashOnWindowsKeyFile' to '$Script:LocalSshKeyFile'. Please create this manually and copy your private key to this folder"
exit
}
}
$VirtualIP = $null
if (-not (Test-Administrator)) {
$VirtualIP = Invoke-ElevatedCommand {(Get-VM | ?{$_.ReplicationMode -ne "Replica"} | Select -ExpandProperty NetworkAdapters | Select IPAddresses).IPAddresses[$VmIndex]}
}
else {
$VirtualIP = (Get-VM | ?{$_.ReplicationMode -ne "Replica"} | Select -ExpandProperty NetworkAdapters | Select IPAddresses).IPAddresses[$VmIndex]
}
if ($VirtualIP -eq $null) {
Write-Host "Couldn't detect IP address of virtual host"
exit
}
Write-Host "Virtual Machine detected at IP address $VirtualIP"
$Script:OldHomePath = $env:HOME
$env:HOME = $env:USERPROFILE
. "$Script:Ssh" $UserId@$VirtualIP -i $Script:LocalSshKeyFile
$env:HOME = $Script:OldHomePath
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment