Script to search several servers for connected client IP addresses - powershell

I'm trying to make a Powershell script to search 5 servers for connected client's IP addresses. There are 5 servers and clients are connected via a user tunnel and an asset one. I'm trying to make a looping script that asks for the asset number and username then searches all 5 servers then reports back the tunnel IPs.
My Powershell skills are very rudimentary. I've managed to make a script that mostly works, the trouble I seem to be having is getting the script to report negative results properly. Here's where I am so far:
Clear-Host
$continue = $true
while ($continue){
Write-Host "Tunnel IP finder" -ForegroundColor White
$Asset = Read-Host "Enter asset number"
$AssetAddress = "$asset.corporate.domain.com"
$User = Read-Host "Enter Username"
$Username = "$User#domain.com"
$servers = "RRAS_01","RRAS_02","RRAS_03","RRAS_04","RRAS_05"
Write-Host ""
$data1 = Foreach ($Server1 in $Servers)
{
Get-RemoteAccessConnectionStatistics -computername $Server1 | Where {$_.UserName -eq $AssetAddress} | Select ClientIPAddress | findstr /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"
}
foreach($item1 in $data1){
if($item1.length -gt 1){
Write-Host "Asset tunnel IP is $item1"-ForegroundColor Green}
}
if($item1.length -LT 1){
Write-Host "Unable to locate asset on RRAS servers"-ForegroundColor yellow
}
$data2 = Foreach ($Server2 in $Servers)
{
Get-RemoteAccessConnectionStatistics -computername $Server2 | Where {$_.UserName -eq $Username} | Select ClientIPAddress | findstr /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"
}
foreach($item2 in $data2){
if($item2.length -gt 1){
Write-Host "User tunnel IP is $item2"-ForegroundColor Green}
}
if($item2.length -lt 1){
Write-Host "Unable to locate user on RRAS servers"-ForegroundColor yellow
}
Write-Host ""
}
When I search for an asset number and username of someone who is connected it is reporting the results back like this:
Tunnel IP finder
Enter asset number: N02312
Enter Username: SmithJ
Asset tunnel IP is 10.120.xxx.xxx
User tunnel IP is 10.120.xxx.xxx
AOVPN Tunnel IP finder
Enter asset number:
Which is what I was looking to achieve, it displays the IPs and loops to enter more if needed, however when I input details that is not currently connected I get:
Tunnel IP finder
Enter asset number:
Enter Username:
AOVPN Tunnel IP finder
Enter asset number:
It's not reporting the negative results. When I take the scripting off and just have it dump what it thinks $item1/2 is supposed to be it prints nothing, so as far as I can tell $item1/2.length -LT 1 should be doing it.
I've tried experimenting with Else and Elseif, but I can't seem to make those work. There are probably better ways of doing this, but my Powershell is still very basic.
Any help would be greatly appreciated.
Thanks in advance.

It seems if you loop through a collection that has $null value the $item.length will be equal to 1, not to 0 and its value will not be equal to $null. Maybe that´s what happening to you.
PS C:\Windows\System32\WindowsPowerShell\v1.0> $x = $null
PS C:\Windows\System32\WindowsPowerShell\v1.0> $x.length
0
PS C:\Windows\System32\WindowsPowerShell\v1.0> $arr = {$null}
PS C:\Windows\System32\WindowsPowerShell\v1.0> $arr.length
1
PS C:\Windows\System32\WindowsPowerShell\v1.0> $arr | % {$_.length}
1
PS C:\Windows\System32\WindowsPowerShell\v1.0> $arr | % {$_ -eq $null}
False
In your code you only check for the length to be either less or greater than 1 so in the case of the length being equal to 1 it would not print anything. Since the $null check doesnt work I think the best option would be to change the second comparison operator to less or equal instead of less than:
if($item1.length -LE 1){
Write-Host "Unable to locate asset on RRAS servers"-ForegroundColor yellow
}

Related

Foreach loop in powershell with hasharray

I'm writing a powershell script to ping all the servers and check which are offline. but i have a bug. By name it works perfectly. But when i do test-connection with an IP it seems to work BUT i cant output the name of the IP in the hashlist. Could someone help me figure this out? Thanks!!
System.Collections.Hashtable.keys Is online/available, This is what it outputs. But i want it to say "Servername is online/available"
#Creating IP Array list
$ip_array = #{
Server = [ipaddress] "192.168.1.1"
sws = [ipaddress] "192.168.1.1"
}
Foreach ($ip in $ip_array)
{
if((Test-Connection -IPAddress $ip.values.ipaddresstostring -quiet -count 1 ) -eq $false)
{
write-output("$ip.keys Is offline/unavailable, please troubleshoot connection, script is terminating") | Red
}
else
{
$ping = $true
write-output("$ip.keys Is online/available") | Green
}
}
PowerShell's default pipeline semantics (any collection that can be enumerated and unraveled will be) makes dictionaries a pain to work with - piping them anywhere would result in a list of disjoint key-value-pairs, dictionary itself lost.
For this reason, PowerShell refuses to automatically enumerate dictionaries, and you must manually obtain an enumerator in order to loop over the entries in it:
foreach($entry in $ip_hash.GetEnumerator()){
# reference `$entry.Key` or `$entry.Name` for the key (eg. Server)
# reference `$entry.Value` for the value (eg. 192.168.1.1)
}
If you really intend to use a Hashtable for this, combining IP addresses with computernames, change to something like this:
# creating IP Hashtable
$ip_hash = #{
'192.168.1.1' = 'Server1'
'192.168.1.2' = 'Server2'
# etcetera
}
# loop through the hash, key-by-key
foreach ($ip in $ip_hash.Keys) {
$ping = Test-Connection -ComputerName $ip -Quiet -Count 1 -ErrorAction SilentlyContinue
if(!$ping) {
Write-Host "Server $($ip_hash[$ip]) is offline/unavailable, please troubleshoot connection, script is terminating" -ForegroundColor Red
}
else {
Write-Host "Server $($ip_hash[$ip]) is online/available" -ForegroundColor Green
}
}
Output would look like:
The Keys in the hash must all have unique values

Running a For Loop through a Multiline Powershell Variable

I have section of a script where the user enters a list of server names which then checks to make sure there they exist in AD and then runs a command on each of the ones that do exist. This is what I have:
$machineNames = (read-host "Enter Server Name(s) (space separated)").Trim()
foreach ($machineName in (($machineNames).Split(' '))) {
Try {
Get-ADComputer $machineName | Out-Null
$veriMachineNames += "$machineName`n"
}
Catch {
Write-Host "*****Invalid Machine Name: $machineName" -Foreground Yellow
Write-Host "*****Skipping $machineName`n" -Foreground Yellow
}
}
foreach ($machine in $sepMachineNames) {
Write-Host "Server: $machine `n"
}
When I run this part of the script I get the following output (error intentionally included):
PS C:\> C:\script.ps1
Enter Machine Name(s) (space separated): server1 server2 server3
*****Invalid Machine Name: server3
*****Skipping server3
Server: server1
server2
As you can see it appears that the second foreach is seeing server1 and server2 as being on one line and not two lines. Is there a way to do this with just a variable? I'd rather not output the list to a file.
Instead of concatenating each name onto a big multi-line string manually, just output the individual names 1 by 1 and store them in a variable:
$veriMachines = foreach ($machineName in (($machineNames).Split(' '))) {
Try {
Get-ADComputer $machineName | Out-Null
# no piping to `|Out-Null`, this will "bubble up" to the variable
$machineName
}
Catch {
Write-Host "*****Invalid Machine Name: $machineName" -Foreground Yellow
Write-Host "*****Skipping $machineName`n" -Foreground Yellow
}
}
Now $veriMachines is an array of string (the machine names we just verified) that you can iterate over:
foreach($machine in $veriMachines){
Write-Host "Server: $machine"
}

IF Statement to Verify VLAN Exists in PowerCLI Script

I am writing a PowerCLI script to automate the creation of VMs based on the data within a CSV file and I would like to know how to format an IF statement to check if the VLANs specified already exist to avoid cluttering up the screen with errors.
The section of the script dealing with the VLAN creation in its current format:
New-VM -Name $_.Name -VMHost ($esx | Get-Random) -NumCPU $_.NumCPU -Location $Folder
$list = Get-Cluster $_.Cluster | Get-VMHost
foreach ($esxhost in $list)
{ Get-VirtualSwitch -Name $switch -VMHost $esxhost |
New-VirtualPortgroup -Name "VLAN $($_.VLAN)" -VLANID $($_.VLAN)
}
Write-Host "Wait - propagating VLAN $($_.VLAN) to all hosts" -foreground yellow
Start-Sleep 10
I would like to determine a way to have the script do something like:
IF $_.VLAN exists
Write-host "$_.VLAN already present, proceeding to next step"
ELSE DO{ Get-VirtualSwitch -Name $switch -VMHost $esxhost |
New-VirtualPortgroup -Name "VLAN $($_.VLAN)" -VLANID $($_.VLAN)
}
I don't have much experience in writing these so I was hoping for some assistance on how to
Check whether the VLAN already exists in vSphere on the switch
How to format the IF/ELSE statement properly to avoid cluttering up the PowerCLI window with errors when the script is run
Thank you for any assistance you may provide
EDIT to work for vlan rather than vswitch
You could use get-virtualportgroup for this and check if the names returned contain your vlanid. This won't work for distributed switches as that's a different set of cmdlets.
$host = 'YourHost'
$vlanid = 'YourVlanId'
if ((Get-VirtualPortGroup -host $host).VLanId -contains $vlanid )
{
Write-Output 'vlan present'
}
else
{
Write-Output 'vlan missing'
#your code to create vlan here
}

Powershell Function returning blank data when setting a variable

First off, I appoligize for my terminalogy. I self taught myself powershell, so my lingo may be a bit off.
I have a script that I am running in WinPE which brings up a menu system for windows imaging (selecting image file, running a diskpart file, ect.). Part of this script looks up which volumes are visible to windows PE and prompts the user to select which volume they would like to install the image to. I separated this portion of the code into it's own function. The issue I'm having is that the WMI call to lookup the logical disks returns blank results when the function is setting a variable (see example below). But when I dot-source and run the function by itself it works fine. Here is the code i'm using
$Vol = Select-VolumeLetter
---- In a seperate file which has been dot-sourced -----
Function Select-VolumeLetter {
$InstallVolumes = (gwmi Win32_LogicalDisk -namespace "root\CIMV2")
$i = 1
Write-Host "`nPlease Select a volume to install the OS on:`n"
ForEach ($V in $InstallVolumes) {
Write-Host "[$i]"
$V | Select Name, Size, FileSystem, VolumeName | fl
$i++
}
Do{
$Range = $i - 1
$Input = Read-Host "Choose 1-$Range"
$Result = ""
if(![int32]::TryParse( $Input , [ref]$Result )) {Write-Host "`nInvalid selection. Please try again.`n"}
} until ($Result -gt 0 -and $Result -lt ($i + 1))
$InstallVolumes[($Input -1)].Name
}
The result looks like:
[1]
[2]
[3] ect.
But when run just as a function I get the full WMI information requested in the correct place. Any help would be much appreciated.
So I think you have some terminology wrong. What you show is not the results, that it what appears on your screen due to the Write-Host calls. You are not seeing the WMI information because you are not specifying that it be written to the screen and it is therefore being passed back as a result of the function. In your case $Vol will contain that WMI information. I think what would fix your situation would be something like this:
$InstallVolumes = (gwmi Win32_LogicalDisk -namespace "root\CIMV2")
$i = 1
Write-Host "`nPlease Select a volume to install the OS on:"
ForEach ($V in $InstallVolumes) {
Write-Host "`n[$i]"
Write-Host ($V | Select Name, Size, FileSystem, VolumeName | fl|out-string).trim()
$i++
}

Powershell foreach math issue. Sum is cumulative, not foreach server polled

Trying to script a daily "check" on DHCP usage. Pulling info from DHCP servers using netsh, looking at IP addresses in use and available, removing letters and delimiting characters so all I have left is the raw number of addresses. Then add them up to get total address pool size and finish with % in use. I've been futzing with this for a few days now, and finally got something I liked, but my math doesn't work right. Some remote sites have multiple scopes/subnets, and when trying to add those numbers into a per-server total, and instead it just adds addresses cumulatively. I think my problem is the final "foreach" in $u and $f, but I'm not sure how to fix it and get what I want. If I remove the final foreach, it works fine for single subnet sites but can't handle more on any one server. I hope that made sense.
function dhcp {
$servers = "contoso","contoso2","contoso3"
Foreach ($server in $servers)
{
netsh dhcp server \\$server show server
"DHCP Server: $server"
#netsh dhcp server \\$server show mibinfo | select-string -pattern "scope", "subnet", "No. of Addresses in use = ", "No. of free Addresses = "
$u = (netsh dhcp server \\$server show mibinfo |
where-object {$_-match "No. of Addresses in use = "} |
foreach-object{$_.Split(“=")[-1].Trim( ).Trim(".").Trim(" ")} |
foreach-object{($u2+=[int]$_)})
$f = (netsh dhcp server \\$server show mibinfo |
where-object {$_-match "No. of free Addresses = "} |
foreach-object{$_.Split(“=")[-1].Trim().Trim(".").Trim(" ")} |
foreach-object{($f2+=[int]$_)})
$total2 = ([int]$u2+[int]$f2)
$percent = [Math]::Round([int]$u2/[int]$total2*100,2)
"No. of Addresses in use = $u2"
"No. of free Addresses = $f2"
"Total Addresses: $total2"
"Percent in use: $percent%"
" ======"
"=================================================="
}
}
dhcp | out-file "C:\Users\crabbypatty\Desktop\Powershell\dhcplog $(get-date -f MM-dd-HHmm).txt"
I'd simplify this significantly by using capture groups in your regex and also using the switch condition's ability to process regular expressions.
Oh and I'd use a dictionary to ensure that each server is tracked individually while allowing you to access the data after the capture is complete. This would also give you the ability to do some historical tracking and comparison if you saved the data... but that's outside of this scope ;)
e.g.
$addressesInUse = #{};
$addressesFree = #{};
foreach ($server in $serverList) {
$mibinfo = netsh dhcp server \\$server show mibinfo; #| where-object {$_-match "No. of Addresses in use = "} |
foreach ($info in $mibinfo) {
switch -regex ($info) {
"No. of Addresses in use = (?<count>\d+)" {
$val = $Matches["count"];
if ($addressesInUse.ContainsKey($server)) {
$addressesInUse.Add($server,$val);
} else {
$scopeCount.$server += $val;
}
break;
}
"No. of free Addresses = (?<count>\d+)" {
$val = [int]::Parse($Matches["count"]);
if ($addressesFree.ContainsKey($server)) {
$addressesFree.Add($server,$val);
} else {
$addressesFree.$server += $val;
}
break
}
}
}
}
foreach ($key in $addressesFree.keys) {
$total = $addressesInUse.$key + $addressesFree.$key;
$percent = [Math]::Round($addressesInUse.$server / $total*100,2)
Write-Host ("DHCP availablility for server {0}" -f $key);
Write-Host ("No. of Addresses in use = {0}" -f $addressesInUse.$key);
Write-Host ("No. of free Addresses = {0}" -f $addressesFree.$key);
Write-Host ("Total Addresses: {0}" -f $total);
Write-Host ("Percent in use: {0}%" - $percent);
}
Might this be as simple as setting your variables to zero at the top of the first loop? Meaning right after your "netsh dhcp server \$server show server", set u2 and f2 both back to zero?
Foreach ($server in $servers) {
netsh dhcp server \\$server show server
"DHCP Server: $server"
At this point, reset the variables:
$f2 = 0
$u2 = 0