Lab Manager Config IP List and RDP Test

From vWiki
Jump to navigation Jump to search
<#
=============================================================================================
 Lab Manager VM NAT Getter
=============================================================================================
 Simon Strutt  -  November 2010
=============================================================================================

Version 1
 - Initial creation!
 
Version 1.1 - Dec 2010
 - Added bucketName when displaying list of config's to better handle config's with same name in different workspaces
 
Version 1.2 - Jan 2011
 - Added sorting by VM name
 
Version 1.3 - Feb 2011
 - Configuration selection menu...
 -- Now sorted by name
 -- Displays configs in yellow if they're not shared
 
Version 2.0 - Apr 2011
 - Changed from single run to modular, continuous operation
 - Added RDP port test
 
=============================================================================================
#>

$UserFile = "User.fil"
$PassFile = "Pass.fil"                           # Encrypted file to store password in
$LabMgrSvr = "lab-manager"                       # Hostname of your lab Manager server
$smtpSvr = "smtp"                                # Hostname of your SMTP server
$EmailFrom = "me@domain.com"                     # Email address to send from
$EmailDomain = "domain.com"                      # Email domain (used to guess email to send to)
$ScriptName = ($MyInvocation.MyCommand.Name).Replace(".ps1", "")

$host.UI.RawUI.WindowTitle = $ScriptName

# =============================================================================================
# Basic helper functions

function PressAnyKeyToExit {
    Write-Host "Press any key to exit..."
    $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    $labManager.Dispose()
    Exit
}

# =============================================================================================
# Lab Manager SOAP connection functions (adapted from POSH - http://poshcode.org/753)

function New-ObjectFromProxy {
	param($proxy, $proxyAttributeName, $typeName)

	# Locate the assembly for $proxy
	$attribute = $proxy | gm | where { $_.Name -eq $proxyAttributeName }
	$str = "`$assembly = [" + $attribute.TypeName + "].assembly"
	invoke-expression $str

	# Instantiate an AuthenticationHeaderValue object.
	$type = $assembly.getTypes() | where { $_.Name -eq $typeName }
	return $assembly.CreateInstance($type)
}

function Connect-LabManager {
	param($server, $credential)
    
    Write-Host "Connecting to $server..."

	# Log in to Lab Manager's web service.
	$server = "https://" + $server + "/"
	$endpoint = $server + "LabManager/SOAP/LabManager.asmx"
    try {
	   $proxy = new-webserviceproxy -uri $endpoint -cred $credential -ErrorAction:Stop
    } catch {
        Write-Host $_
        if ($_.ErrorDetails.Message -match "401: Unauthorized") {
            Remove-Item $UserFile
            Remove-Item $PassFile
            Write-Host "User/pass credential cache files have been deleted"
        } else {
             Write-Host $_.Exception.Message
             Write-Host $_.Exception.GetType().FullName
        }     
        PressAnyKeyToExit
    }

	# Before continuing we need to add an Authentication Header to $proxy.
	$authHeader = New-ObjectFromProxy -proxy $proxy -proxyAttributeName "AuthenticationHeaderValue" -typeName "AuthenticationHeader"
	$authHeader.username = $credential.GetNetworkCredential().UserName
	$authHeader.password = $credential.GetNetworkCredential().Password
	$proxy.AuthenticationHeaderValue = $authHeader
	return $proxy
}

function Ignore-SslErrors {
	# Create a compilation environment
	$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
	$Compiler=$Provider.CreateCompiler()
	$Params=New-Object System.CodeDom.Compiler.CompilerParameters
	$Params.GenerateExecutable=$False
	$Params.GenerateInMemory=$True
	$Params.IncludeDebugInformation=$False
	$Params.ReferencedAssemblies.Add("System.DLL") > $null
	$TASource=@'
	  namespace Local.ToolkitExtensions.Net.CertificatePolicy {
	    public class TrustAll : System.Net.ICertificatePolicy {
	      public TrustAll() { 
	      }
	      public bool CheckValidationResult(System.Net.ServicePoint sp,
	        System.Security.Cryptography.X509Certificates.X509Certificate cert, 
	        System.Net.WebRequest req, int problem) {
	        return true;
	      }
	    }
	  }
'@ 
	$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
	$TAAssembly=$TAResults.CompiledAssembly

	## We now create an instance of the TrustAll and attach it to the ServicePointManager
	$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
	[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
}

# ===============================================================================================
# TCP Port test function (adapted from POSH - http://poshcode.org/85)
function Test-Port {
    Param([string]$srv,$port=3389,$timeout=3000)
     
    # Does a TCP connection on port 3389 (RDP), port and timeout can be overriden
     
    $ErrorActionPreference = "SilentlyContinue"
     
    # Create TCP Client
    $tcpclient = new-Object system.Net.Sockets.TcpClient
     
    # Tell TCP Client to connect to machine on Port
    $iar = $tcpclient.BeginConnect($srv,$port,$null,$null)
     
    # Set the wait time
    $wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)
     
    # Check to see if the connection is done
    if(!$wait)
    {
        # Close the connection and report timeout
        $tcpclient.Close()
        Write-Host "Connection Timeout" -BackgroundColor DarkRed
        Return
    }
    else
    {
        # Close the connection and report the error if there is one
        $error.Clear()
        $tcpclient.EndConnect($iar) | out-Null
        if (!$?) {
             write-host $error[0] -BackgroundColor DarkRed
             $failed = $true
        }
        $tcpclient.Close()
    }
     
    # Return $true if connection Establish else $False
    if (!$failed) {
        Write-Host "Connection successful" -BackgroundColor DarkGreen
    }
}

# Bussiness functions ==========================================================================

function Select-Configuration {
    # Get list of configurations, query user which to do, and return selected config ID
    
    $ConfIDs = @()
    $confID = -99

    # Get list of config's and display deployed only
    Write-Host ("Deployed configurations...`n")
    $Configs = $labManager.ListConfigurations(1) 
    $Configs = $Configs | Sort name
    foreach ($config in $Configs) {
        if ($config.isDeployed) {
            if ($FontColour -eq "Gray") {
                $FontColour = "White"
            } else {
                $FontColour = "Gray"
            }
            if ($config.isPublic) {
                Write-host -ForegroundColor $FontColour ("ID " + $config.id + " - " + $config.name + "  [" + $config.bucketName + "]")
                
            } else {
                Write-Host -ForegroundColor Yellow ("ID " + $config.id + " - " + $config.name + "  [" + $config.bucketName + "] - Not Shared !!")
            }
            $ConfIDs += $config.ID                           # List of config ID's (with which to validate user input)
        }
    }

    # Get config ID from user
    While (!($ConfIDs -contains $ConfID)) {   # Don't leave loop until user selected config ID is valid!
        $ConfID = Read-Host "`nSelect a configuration ID [0 to quit]"
        if (!$ConfID -or ($ConfID -eq 0)) {
            # Dump the Lab Manager connection
            $labManager.Dispose()
            exit
        } elseif (!($ConfIDs -contains $ConfID)) {
            Write-Host "Invalid, should be one of" ([string]::join(", ", $ConfIDs))
        }
    }
    Return $labManager.GetConfiguration($ConfID)
}


function Get-ConfigVMs {
    Param($objConfig)
    
    $VMs = @()
    
    # Get VM's for selected config
    $labManager.ListMachines($objConfig.id) | foreach {
        $vm = "" | Select VM_Name, VM_IntIP, VM_ExtIP, VM_State
        $vm.VM_Name = $_.name
        $vm.VM_IntIP = $_.internalIP
        $vm.VM_ExtIP = $_.externalIP
        $vm.VM_State = $_.status        # 1=Off, 2=On, 3=Suspended, 4=Stuck, 128=Invalid
        $VMs = $VMs + $vm
    }
    $VMs = $VMs | Sort VM_Name
    #$VMs | Select VM_Name, VM_IntIP, VM_ExtIP | Format-Table
    Return $VMs
}

function Send-ConfigIPs {
    Param($objConfig, $objConfVMs)
    
    # Include VM Power State
    $tmp = Read-Host "Include VM power state (on/off) [N]"
    if ($tmp -and ($tmp -match "y")) {
        $IncPowerState = 1
    } else { 
        $IncPowerState = 0
    }

    # Email results to user
    $EmailTo = $env:username + "@" + $EmailDomain
    $tmp = Read-Host "Email to [$EmailTo]"
    if ($tmp) {$EmailTo = $tmp}

    Write-Host "Emailing $EmailTo..." -nonewline

    # Create HTML for email
    [string]$msgHTML = "<!-- Genenerated by $ScriptName at " + (Get-Date).ToString() + " hrs -->`n"
    $msgHTML += "<html xmlns='http://www.w3.org/TR/REC-html40'>`n`n<head>`n<meta http-equiv=Content-Type content='text/html; charset=uk'`n>"
    $msgHTML += "<meta name=Generator content='Lab Manager NATs script'>`n<meta name=Author content='Simon Strutt'>`n</head>`n`n"
    $msgHTML += "<body style='font-family: arial; font-size: 10;'><table style='font-family: arial; font-size: 11';><tr><td>`nConfiguration:<td><b>" + $objConfig.name + "</b><tr><td>`nWorkspace:<td>" + $objConfig.bucketName
    $msgHTML += "<tr><td>`nDescription:<td>" + $objConfig.description + "<tr><td>Info correct at:<td>" + (Get-Date).ToString() + " hrs</table>`n"
    $msgHTML += "<br><br>`n`n"
    $msgHTML += "<table border=1 style='border-width: 1px; border-spacing: 0; border-color: black; background-color: #faf0e6; font-family: arial; font-size: 11; font-weight: bold'>`n"
    $msgHTML += "<tr style='border-width: 2px; background-color: #fcf7f8;'><th>VM Name"
    if ($IncPowerState) {
        $msgHTML += "<th>Power"
    }
    $msgHTML += "<th>Internal IP<th>External IP`n"
    foreach ($vm in $objConfVMs) {
        $msgHTML += "<tr><td>" + $vm.VM_Name
        if ($IncPowerState) {
            Switch ($vm.VM_State) {
                1   {$msgHTML += "<td style='font-weight: normal; text-align: center'>Off"}
                2   {$msgHTML += "<td style='text-align: center'>On"}
                3   {$msgHTML += "<td style='font-weight: normal; text-align: center'>Suspended"}
                4   {$msgHTML += "<td style='font-weight: normal; text-align: center'>Stuck"}
                128 {$msgHTML += "<td style='font-weight: normal; text-align: center'>Invalid"}
                default {$msgHTML += "<td style='font-weight: normal; text-align: center'>Unknown"}
            }
        }
        $msgHTML += "<td style='font-weight: normal'>" + $vm.VM_IntIP + "<td>" + $vm.VM_ExtIP
    }
    $msgHTML += "</table>`n`n<br><br>`n<table style='font-family: arial; font-size: 10; color: #888888'>`n<tr><td>Generated by script: <td>$ScriptName"
    $msgHTML += "`n</table>`n`n</body></html>"

    #Send via email
    $smtp = New-Object Net.Mail.SmtpClient -arg $smtpSvr
    $msg = New-Object Net.Mail.MailMessage
        
    $msg.From = $EmailFrom
    $msg.To.Add($EmailTo)
    $msg.Subject = "Lab Manager configuration: " + $objConfig.name 
    $msg.IsBodyHTML = $true
    $msg.Body = $msgHTML
        
    try {
        $smtp.Send($msg)
    } catch {
        Write-Host "`n" $_
        Write-Host "`nCopy text above if you want to retain the info."
        PressAnyKeyToExit
    }
    Write-Host "sent !`n"
}

function Test-Access {
    Param($objConfVMs)
    
    # Check RDP access to powered-on VM's
    Write-Host -ForegroundColor white "`nTesting RDP (TCP port) access to powered on servers..."
    foreach ($vm in $objConfVMs) {
        if ($vm.VM_State -eq 2) {
            Write-Host ($vm.VM_Name).PadRight(16) ($vm.VM_ExtIP).PadRight(15) -nonewline
            Test-Port $vm.VM_ExtIP
        }
    }
}

# ===============================================================================================
# Faff around with credentials and connect to Lab Manager server

# Check for credential files, create if required
if (!(Test-Path $UserFile) -or !(Test-Path $PassFile)) {
    Write-Host "Credential files not found"
    $cred = Get-Credential -Credential ($env:userdomain + "\" + $env:username)
    $cred.UserName | Set-Content $UserFile -Force
    $cred.Password | ConvertFrom-SecureString | Set-Content $PassFile -Force
    Write-Host "Credentials saved"
}
    
# Load password credential from encrypted file
$pass = Get-Content $PassFile | ConvertTo-SecureString
$user = Get-Content $UserFile
$cred = New-Object System.Management.Automation.PsCredential($user, $pass)

# Connect to Lab Mgr server
Ignore-SslErrors
$labManager = Connect-LabManager -server $LabMgrSvr -credential $cred
Write-Host "Sucessfully connected to Lab Manager web service!"

# ==============================================================================================
# Get list of configurations, query user which to do, then get list of VM's and IP's

$option = 1

$Config = Select-Configuration
$host.UI.RawUI.WindowTitle = $ScriptName + " - " + $Config.name

While (1) {
    Write-Host -ForegroundColor white ("`n" + $Config.name + " (" + $Config.description + ")`n")
    Write-Host "1 - Email IP addresses"
    Write-Host "2 - Check RDP access"
    Write-Host "3 - Change config"
    Write-Host "0 - Exit"
    $tmp = Read-Host "`nSelect an option [$option]"
    if ($tmp) {$option = $tmp}
    
    if ($option -notmatch "0|1|2|3") {
        Write-Host "$option is invalid"
        $option = 0
        
    } elseif ($option -eq 0) {
        # Dump the Lab Manager connection
        $labManager.Dispose()
        exit
    } elseif ($option -eq 1) {
        $ConfVMs = Get-ConfigVMs $Config
        Send-ConfigIPs $Config $ConfVMs
    
    } elseif ($option -eq 2) {
        $ConfVMs = Get-ConfigVMs $Config
        Test-Access $ConfVMs
    
    } elseif ($option -eq 3) {
        $Config = Select-Configuration
        $host.UI.RawUI.WindowTitle = $ScriptName + " - " + $Config.name
        
    } else {
        Write-Host "Unexpected error...!"
    }
}     
    
PressAnyKeyToExit