Calling a variable without a space before it in powershell? - powershell

I am trying to use variables from a imported CSV file to substitute variables in a string.
Here's the code.
param( [string] $CSV)
$VMs = Import-Csv $CSV
Foreach ($VM in $VMs) {
psexec \\$VM.VM_Name -h netsh interface ip set address name='"Local Area Connection"' static $VM.IP_Address 255.255.255.0 $VM.Gateway 1
}
this is what it returns:
psexec \#{VM_Name=TESTCSVVM; IP_Address=10.12.81.82; Gateway=10.12.81.1; VLAN=H
Q_VM_81}.VM_Name -h netsh interface ip set address name="Local Area Connection"
static 10.12.81.82 255.255.255.0 10.12.81.1 1
Here's what I want it to look like:
psexec \TESTCSVVM -h netsh interface ip set address name="Local Area Connection"
static 10.12.81.82 255.255.255.0 10.12.81.1 1
Here's the CSV file:
"VM_Name","IP_Address","Gateway","VLAN"
"TESTCSVVM","10.12.81.82","10.12.81.1","HQ_VM_81"
How do I make sure theres no space between the \ and the 1st variable $VM.VM_Name?
Thanks

Try this :
Foreach ($VM in $VMs) {
psexec \\\\$($VM.VM_Name) -h netsh interface ip set address `
name='"Local Area Connection"' `
static $VM.IP_Address 255.255.255.0 $VM.Gateway 1
}

Also, generally wrapping variables in quotes will work too.
So if:
$path = C:\Windows\
$File = MyFile.ext
write-host "$path$File
Would look like:
C:\Windows\myFile.ext

Related

IP Address Input from Jenkins to Variable powershell

I Have Jenkins job that asks for IP Address
$ip = $env:Lan_ip
what the user enter goes to $ip
now $ip is 192.168.10.10 for Example
now I'm trying to insert this variable to FortiGate SSH
Invoke-SshCommand $Firewall -command ‘config system interface
edit port1
set ip $ip 255.255.255.0
end’
but he can not read the $ip I need to make it like INT separate with .
im getting this Error
node_check_object fail! for ip $ip
how can i convert the sting im getting from the user when he enter the ip address in for example --> 192.168.10.10
to usable variable in the code
From what I gather from here is that you need to give the subnet mask as a CIDR-formatted subnet mask like 255.255.255.0/24
To get that CIDR value off a subnet IP address, you can use this function:
function Get-SubnetMaskLength ([string]$SubnetMask) {
# $cidr = Get-SubnetMaskLength "255.255.240.0" --> 20
$result = 0
[IPAddress]$ip = $SubnetMask
foreach ($octet in $ip.GetAddressBytes()) {
while ($octet) {
$octet = ($octet -shl 1) -band [byte]::MaxValue
$result++
}
}
$result
}
So
$subNet = '255.255.255.0'
$cidr = Get-SubnetMaskLength $subNet # --> 24
$subNetAndCidr = '{0}/{1}' -f $subNet, $cidr # --> 255.255.255.0/24
P.S. Always use straight quotes instead of the curly thingies ‘ and ’ in code!

Script to search several servers for connected client IP addresses

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
}

Powershell - Checking IP Address range based on CSV file

I have been developing VM provision script. My question is : I have here-string like below. now , I want to add route based on ip address range. I am using CSV file with BACKUPIP column.
if an BACKUPIP is in range 10.10.104.1 to 10.10.107.254 it will work route add xx.xx.xx.xx mask 255.255.255.0 xx.xx.xx.xx -p
if an BACKUPIP is in range 10.10.180.1 to 10.10.185.254 it will work route add yy.yy.yy.yy mask 255.255.255.0 yy.yy.yy.yy -p
Here is my script:
Import-Csv -Path .\vm.csv -UseCulture -PipelineVariable row |
ForEach-Object -Process {
# Create the VM, store result in $vm
if($($row.IP) -eq '???'){
route add xx.xx.xx.xx mask 255.255.255.0 xx.xx.xx.xx -p
}
else{
route add yy.yy.yy.yy mask 255.255.255.0 yy.yy.yy.yy -p
}
}
LAST UPDATE :
$rangeFrom104 = '10.10.104.1'
$rangeTo107 = '10.10.107.254'
$rangeFrom180 = '10.10.180.1'
$rangeTo185 = '10.10.185.254'
if (([version]$rangeFrom104) -lt ([version]$($row.IP)) -and ([version]$($row.IP)) -lt ([version]$rangeTo107) )
{
route add xx.xx.xx.xx mask 255.255.255.0 xx.xx.xx.xx -p
}
elseif (([version]$rangeFrom180) -lt ([version]$($row.IP)) -and ([version]$($row.IP)) -lt ([version]$rangeTo185) )
{
route add yy.yy.yy.yy mask 255.255.255.0 yy.yy.yy.yy -p
}
Really like the [Version] approach Lee_Dailey suggested.
Here's another approach that converts the IP addresses to their numeric values:
function Convert-IPv4ToDecimal ([string]$IpAddress){
# helper function to return the numeric value (uint32) of a dotted IP
# address string used for testing if an IP address is in range.
$n = [uint32[]]$IpAddress.Split('.')
# or use: $n = [uint32[]]([IpAddress]$IpAddress).GetAddressBytes()
# to get the obsolete property ([IpAddress]$IpAddress).Address
# you need to do the math in reversed order.
# return [uint32] ($n[3] -shl 24) + ($n[2] -shl 16) + ($n[1] -shl 8) + $n[0]
# for comparing different ranges as in this question, do not reverse the byte order
return [uint32] ($n[0] -shl 24) + ($n[1] -shl 16) + ($n[2] -shl 8) + $n[3]
}
$startRange1 = Convert-IPv4ToDecimal '172.25.104.1'
$endRange1 = Convert-IPv4ToDecimal '172.25.107.254'
$startRange2 = Convert-IPv4ToDecimal '172.25.112.1'
$endRange2 = Convert-IPv4ToDecimal '172.25.115.254'
Import-Csv -Path .\vm.csv -UseCulture | ForEach-Object {
# Create the VM, store result in $vm
# convert the .BACKUPIP to numeric value
$backupIp = Convert-IPv4ToDecimal $_.BACKUPIP
# test the IP range
if ($backupIp -ge $startRange1 -and $backupIp -le $endRange1) {
Write-Host "BACKUPIP '$($_.BACKUPIP)' is in Range 1"
route add xx.xx.xx.xx mask 255.255.255.0 xx.xx.xx.xx -p
}
elseif ($backupIp -ge $startRange2 -and $backupIp -le $endRange2) {
Write-Host "BACKUPIP '$($_.BACKUPIP)' is in Range 2"
route add yy.yy.yy.yy mask 255.255.255.0 yy.yy.yy.yy -p
}
else {
Write-Warning "No range defined for IP address '$($_.BACKUPIP)'"
}
}
There exists a IPAddress class in .Net:
$MyIPAddress = [System.Net.IPAddress]'10.10.105.7'
$rangeFrom104 = [System.Net.IPAddress]'10.10.104.1'
$rangeTo107 = [System.Net.IPAddress]'10.10.107.254'
If ($rangeFrom104.Address -lt $MyIPAddress.Address -and $MyIPAddress.Address -lt $rangeTo107.Address) {
# route add xx.xx.xx.xx mask 255.255.255.0 xx.xx.xx.xx -p
}
As #Theo commented, the Address property is obsolete:
This property has been deprecated. It is address family dependent.
Please use IPAddress.Equals method to perform comparisons.
I guess this is due to compliance with IPv6 (but I presume that the property won't easily cease to exist as that would probably break some legacy programs). Anyways, that doesn't mean that the whole [System.Net.IPAddress] class is deprecated. Meaning that you might also use the GetAddressBytes method which I think better than a custom function or relying on a (smart! [version]) type but also are both limited to IPv4 (~4 bytes).
With using the GetAddressBytes method, you might simple convert the bytes to a hexadecimal string, which format is comparable (e.g. '10' -gt '0A') as long as the byte arrays are of the same size (e.g. both IPv4):
function Convert-IPAddressToHexadecimal ([Net.IPAddress]$IPAddress, [Switch]$IPv6) {
If ($IPv6) {$IPAddress = $IPAddress.MapToIPv6()}
[BitConverter]::ToString($IPAddress.GetAddressBytes())
}; Set-Alias IP2Hex Convert-IPAddressToHexadecimal
$MyIPAddress = IP2Hex '10.10.105.7' # 0A-0A-69-07
$rangeFrom104 = IP2Hex '10.10.104.1' # 0A-0A-68-01
$rangeTo107 = IP2Hex '10.10.107.254' # 0A-0A-6B-FE
If ($rangeFrom104 -lt $MyIPAddress -and $MyIPAddress -lt $rangeTo107) {
# route add xx.xx.xx.xx mask 255.255.255.0 xx.xx.xx.xx -p
}
If you do need to make your script IPv6 compliant and comparing IP addresses to both IPv4 ranges and IPv6 ranges, you might consider to map all IP addresses to an IPv6 address: using: $MyIPAddress.MapToIPv6().GetAddressBytes() (the -IPv6 switch):
IP2Hex -IPv6 '10.10.105.7' # 00-00-00-00-00-00-00-00-00-00-FF-FF-0A-0A-69-07
Update 2020-09-06:
It is unclear whether the Address property is really obsolete. See: Avoid byte[] allocations once IPAddress.Address is back #18966.
Anyhow, there is a pitfall in using the Address property for comparison as it appears that the address is stored as Big-Endian read from memory as Little-Endian format, see: System.Net.IPAddress returning weird addresses, causing the last byte in 10.10.104.1 (1) to become most significant.
This means that comparing the Address property might give an incorrect result if there are differences between multiple bytes in the concerned IP Addressed:
([IPAddress]'0.0.0.1').Address -lt ([IPAddress]'0.0.1.0').Address
False

read section of lines from Cisco IOS configuration loaded as text file in powershell

I'm not expert in powershell but looking to write function in powershell to read section of lines from Cisco IOS configuration loaded in as text file in powershell. there will be multiple sections with different names, each section have parent line with child section as below in configuration. "interface" section have names, "object" section have names and "object-group" section have names to filter them or search. so how to write function to get each section of lines and than parse further to get IPs from that section.
IOS Configuration Example:
interface GigabitEthernet0/0
description XXX
speed 1000
duplex full
nameif XXX
security-level 100
ip address 1.1.1.1 255.255.255.0
!
interface GigabitEthernet0/1
description YYY
speed 1000
duplex full
nameif YYY
security-level 100
ip address 2.2.2.2 255.255.255.0
!
...
object network APP_NETWORK
subnet 10.10.10.1 255.255.255.0
object network WEB_NETWORK
host 10.10.10.2
object network DB_NETWORK
range 10.10.10.3 10.10.10.5
...
object-group network APP_GROUP
network-object host 10.10.20.1
network-object host 10.10.20.2
network-object host 10.10.20.3
object-group network WEB_GROUP
network-object host 10.10.30.1
network-object host 10.10.30.2
network-object host 10.10.30.3
...
For Example I tried following to read all "object-group network" parent sections:
$config = Get-Content $runconfig -ErrorAction stop
$config | where { $_.Contains("object-group network") }
But not able to get its child section along with. how can write function to get parent and child section both.
Example1
Get-Section(object-group network APP_GROUP)
should return following
object-group network APP_GROUP
network-object host 10.10.20.1
network-object host 10.10.20.2
network-object host 10.10.20.3
Example2
Get-Section(nameif XXX) OR Get-Section(interface GigabitEthernet0/0)
should return something like this
interface GigabitEthernet0/0
description XXX
speed 1000
duplex full
nameif XXX
security-level 100
ip address 1.1.1.1 255.255.255.0
!
I have searched many blogs, your help or hints will be really appreciated! Thank you!
One way to do this is to use a state variable and an appropriate regular expression. Here's an example function:
function Get-Section {
param(
[String[]] $configData,
[String] $sectionName
)
$pattern = '(?:^(!)\s*$)|(?:^[\s]+(.+)$)'
$inSection = $false
foreach ( $line in $configData ) {
# Skip empty lines
if ( $line -match '^\s*$' ) {
continue
}
if ( $line -eq $sectionName ) {
$inSection = $true
continue
}
if ( $inSection ) {
if ( $line -match $pattern ) {
[Regex]::Matches($line, $pattern) | ForEach-Object {
if ( $_.Groups[1].Success ) {
$_.Groups[1].Value
}
else {
$_.Groups[2].Value
}
}
}
else {
$inSection = $false
}
if ( -not $inSection ) {
break
}
}
}
}
If your example data is in a text file (e.g., config.txt), you could extract the interface GigabitEthernet0/1 section as follows:
$configData = Get-Content "config.txt"
Get-Section $configData 'interface GigabitEthernet0/1'
The output would be:
description YYY
speed 1000
duplex full
nameif YYY
security-level 100
ip address 2.2.2.2 255.255.255.0
!
The function doesn't output the section's name because you already know it (you passed it to the function).
If you know the number of lines following the APP_GROUP, which in this case is 3, you can use that value for the -Context switch on Select-String:
$config | Select-String -Pattern '(object-group network APP_GROUP)' -Context 0,3
Which would give:
object-group network APP_GROUP
network-object host 10.10.20.1
network-object host 10.10.20.2
network-object host 10.10.20.3
Edit: Alternative regex solution below since no of lines is dynamic
$section = 'APP_GROUP'
$regex = "(?:object-group\snetwork\s$section\n)(\snetwork-object\shost\s.*\n)+(?=object-group)"
$oneline = Get-Content C:\temp\cisco.txt | Out-String
$oneline -match $regex
$matches[0]
network-object host 10.10.20.1
network-object host 10.10.20.2
network-object host 10.10.20.3

Capture specific text from output into a variable

I'm trying to write a script that will detect what COM port a device is plugged into, then map it to a new port.
Here is the output from the "change port" command:
PS C:\> change port
COM11 = \Device\19H2KP0
COM2 = \Device\Serial1
COM5 = \Device\Serial2
KEYSPAN#*USA19HMAP#00_00#{4d36e978-e325-11ce-bfc1-08002be10318} = \Device\ComUs19H-00
KEYSPAN#*USA19HMAP#00_00#{86e0d1e0-8089-11d0-9ce4-08003e301f73} = \Device\ComUs19H-00
USA19HP0 = \Device\19H2KP0
I need to capture the COM number prior to "\Device\19H2KP0". So in this example output, I would capture COM11 into a variable.
Next I need to run the "change port" command with that variable. i.e.:
change port COM1=$CapturedText
Thank you in advance for any assistance!
Do you already know what the 19H2KP0 bit will be? If so, you could use a regular expression to detect the unique ID using a look-ahead.
Here's a fully working example, using your example text:
$Output = #"
COM11 = \Device\19H2KP0
COM2 = \Device\Serial1
COM5 = \Device\Serial2
KEYSPAN#*USA19HMAP#00_00#{4d36e978-e325-11ce-bfc1-08002be10318} = \Device\ComUs19H-00
KEYSPAN#*USA19HMAP#00_00#{86e0d1e0-8089-11d0-9ce4-08003e301f73} = \Device\ComUs19H-00
USA19HP0 = \Device\19H2KP0
"#
$Output -match "COM[0-9]{1,2}(?=.*$Id)";
Write-Host -Object ('COM port is: {0}' -f $matches[0]);
And now here is the example, using the in-line command:
$Id = '19H2KP0';
$Output = change port;
$Output -match "COM[0-9]{1,2}(?=.*$Id)";
Write-Host -Object ('COM port is: {0}' -f $matches[0]);
Result
COM port is: COM11
Longer, but perhaps more intuitive, you can also use chained -match and -replace operators with simpler regexes:
$CapturedText = (change port) -match 'COM.+19h2kp0' -replace '^(COM\d+).+','$1'
$CapturedText