I am looking for a more efficient way to obtain an IP address from a user, splitting the IP address into an array, knocking off the last octet, replacing with a specific number, then joining the array back into a new IP address.
For example: User inputs IP: 10.1.1.50. From this input, I take the first 3 octets, then generate 10.1.1.1, 10.1.1.10 and 10.1.1.11. I can procedurally do this, but seems Im doing the same operations over for 3 iterations, a function could possibly be beneficial, but my attempts are failing.
$ip= $(read-host "Enter any IP Address")
$gateway = $ip.split('.')
$gateway[-1] = 1
$gateway = $ipArray -join '.'
$dns1 = $ip.split('.')
$dns1[-1] = 10
$dns1 = $dns1 -join '.'
$dns2 = $ip.split('.')
$dns2[-1] = 11
$dns2 = $dns2 -join '.'
I've separated out the first 3 octets with $threeOctets = $ip | Select-Object -First 3, which works fine, what any time i operate on it, i just seem to ADD on the 1, then the 10, then the 11 instead of adding on the 1, then going back to the first 3 octets and adding a 10, then going back and adding an 11.
Why are you making the user type in the last octet, only to cut it off and throw it away? And are you sure this is a good design, given that networks aren't all /24?
Anyway, no real need to split and join, you can just:
$ip = (Read-Host "Enter an IP") -replace '\d+$'
$gateway, $dns1, $dns2 = ('1','10','11' | foreach {"$ip$_"})
The replace gets rid of the last digits. The loop puts a string into the pipeline for each of the three numbers in the array. The multiple assignment takes the three values coming out of the pipeline into the three variables.
(PowerShell does have rudimentary IP address types, with [ipaddress]"192.168.0.1", but they're too basic to be of much help here).
I agree with was TessellatingHeckler's answer says. But I wanted to show another way that has some basic error correction as well using [ipaddress].
do{
$ip = $(read-host "Enter any IP Address")
} while(!($ip -as [ipaddress]))
$octets = ([ipaddress]$ip).Getaddressbytes()
$gateway, $dns1, $dns2 = '1','10','11' | ForEach-Object{$octets[3] = $_; $octets -join "."}
This is by no means a better approach. Merely an alternate one. The error correction is not perfect and can break but its better than a kick in the pants in some cases.
Get the ip. Convert it into its octets as an array. replace the last element of the array with the pipeline value. Join again with a period to make it an valid ip again.
It is a shame that the [Ipaddress] class wont let you edit the octets in place. [version] works like this too, which is unfortunate.
Related
I am working on a code where I need to get the device id from below string
key-vim.host.PlugStoreTopology.Device-0200eb00006000097000029790063053303032344353594d4d4554
device id in this is 60000970000297900630533030323443
Now I wrote the below code where I am getting the index of 6000 and the taking the next 32 chars from there
$strPhrase = $plugs.Device
[int]$check_index = $strPhrase.IndexOf("6000")
$device = "naa."+$strPhrase.Substring($check_index,32)
Most of the cases the index value is 48, so I am able to fetch the proper data. but in few cases(e.g. key-vim.host.PlugStoreTopology.Device-0200e600006000097000029790063053303031343253594d4d4554) the index value is not same, so I am not getting the correct ID.
Please let me know how to get these values
If the string always looks like this (with Device- and then random 10 characters before the actual device id comes), you could do it using the following regex:
$deviceString = 'key-vim.host.PlugStoreTopology.Device-0200eb00006000097000029790063053303032344353594d4d4554'
$deviceString -replace '.*Device-.{10}(.{32}).*', '$1'
You can also get the value going from right to left if the value you're looking for is always the 32 characters long and 12 characters away from the right:
$str = 'key-vim.host.PlugStoreTopology.Device-0200eb00006000097000029790063053303032344353594d4d4554'
$str -replace '^.+(.{32}).{12}$', '$1'
yields 60000970000297900630533030323443
$str = 'key-vim.host.PlugStoreTopology.Device-0200e600006000097000029790063053303031343253594d4d4554'
$str -replace '^.+(.{32}).{12}$', '$1'
yields 60000970000297900630533030313432
or use the string .Substring() method
$str = 'key-vim.host.PlugStoreTopology.Device-0200eb00006000097000029790063053303032344353594d4d4554'
$str.Substring($str.Length - 44).Substring(0,32)
yields 60000970000297900630533030323443
I have 2 arrays here one contains the servername and other contains the IP.
I need to loop through them and create a key value pair like below for each server
server1:ip1
server2:ip2
I have written below code, but the problem is if i debug the code using F11, it is working fine, but i don't it gives some error which is different every time.
so feeling like it is not that reliable piece to continue.
$NewDNSEntryName = $DNSEntryName.Split(",")
$DNSIPs = $DNSIP.Split(",")
if($DNSEntryName -match "," -or $DNSIP -match ",")
{
0..($NewDNSEntryName.Count - 1) | ForEach-Object {
$fullName=""
$fullName += #("$($NewDNSEntryName[$_]):$($DNSIPs[$_])")
This is the line where i am facing trouble
0..($NewDNSEntryName.Count - 1) | ForEach-Object
Please let me know why this code is behaving like this else any alternate idea is appreciated
Assuming each item in each list corresponds with each other exactly, you can use a for loop and loop through the array indexes.
$NewDNSEntryName = $DNSEntryName.Split(",")
$DNSIPs = $DNSIP.Split(",")
for ($i = 0; $i -lt $DNSIPs.count; $i++) {
"{0}:{1}" -f $NewDNSEntryName[$i],$DNSIPs[$i]
}
For the code above to work, $DNSEntryName and $DNSIP must be single strings with commas between names and IPs. If $DNSEntryName and $DNSIP are already lists or arrays, something else will need to be done.
In your attempt, technically, your logic should work given everything written above is true. However, $fullName is emptied at every single iteration, which may produce undesirable results.
I have been writing a script that gathers IPAM info given a provided IP address.
It is working, but my current implementation is highly inefficient.
I am using this script to run checkSubnet, which determines if an IP is in a subnet.
First, I query IPAM to gather this $allSubnets object:
Address CIDR Description VLAN
------- ---- ----------- ----
10.15.10.0 24 DMZ 3000
10.15.11.0 24 Voice 3010
10.15.12.0 24 Wireless 3020
10.15.13.0 28 Management 3030
... ... ... ...
Which is then searched like so:
$testCon = Test-Connection hostname -Count 1
$allSubnets | ForEach-Object {
if((checkSubnet -addr1 ('{0}/{1}' -f $_.Address, $_.CIDR) -addr2 $testCon.IPV4Address.IPAddressToString).Condition -eq $true)
{
[pscustomobject]#{
subnet = ('{0}/{1}' -f $_.Address, $_.CIDR)
desc = $_.Description
}
}
}
This works perfectly for smaller queries.
However, it can take an extremely long time to run against all the items in $allSubnets! Say I want to test 20 IP addresses against a full 2000 subnets, suddenly this query will take a full 2 minutes to complete.
Does anyone have any ideas of how to improve the efficiency of this?
I don't have access to the full list of subnets, but the tests against the ones posted have shown that this is a bit faster:
first a "simpler" function to see if an IP is in a certain range:
Function Find-Subnet ([IPAddress]$SubnetAddress,[byte]$CIDR,[IPAddress]$MatchIP){
[IPAddress]$Mask = [System.Convert]::ToUInt64(('1'*$CIDR).PadRight(32,'0'),2)
return (($SubnetAddress.Address -band $Mask.Address) -eq ($MatchIP.Address -band $Mask.Address))
}
this takes advantage of bit-shifting, the [System.IPAddress] object can be a bit of a performance hit, but it still seems a lot faster than the linked function, and is much more concise, you could always rewrite the [IPAddress] casting as another binary function if you really need the extra performance, as we only use the numerical representation of the Address from it in the end.
then I look to limit the number of searches, when searching against 2000+ subnets this should be a massive performance increase, searching against only the few in the question it was only a slight negative though.
foreach ($Reg in ('(.*\.).*','(.*\.).*\..*','(.*\.).*\..*\..*')){
$Prefix = $ToMatch -replace $Reg,'$1'
Write-Host "Searching subnets beginning with '$($Prefix)'..." -Fore Yellow
$AllSubnets | ? {$_.Address.StartsWith($Prefix)} | ForEach-Object {
if (Find-Subnet -SubnetAddress $_.Address -CIDR $_.CIDR -MatchIP $ToMatch){
$_ ; break
}
}
}
this loops through the regex snippets '(.*\.).*','(.*\.).*\..*' and '(.*\.).*\..*\..*' which when run through the $Prefix = $ToMatch -replace $Reg,'$1' will result in something like this:
'10.11.12.13' -> '(.*\.).*' -> '10.11.12.'
'10.11.12.13' -> '(.*\.).*\..*' -> '10.11.'
'10.11.12.13' -> '(.*\.).*\..*\..*' -> '10.'
we then go through the whole list and pull out subnets with addresses starting with the prefix, for any class A subnet this will be slower than just not including it, for B and C it will be roughly the same performance or faster, with B being more likely to be slower, and C being significantly more likely to be much much quicker.
your current function also does not stop searching after it finds the IP. by including the ;break after returning the matching IP object, we immediately return it and stop searching, if you'd like to store the subnet in a variable you can do $Result = #(foreach ($Reg in ...) { ... }) to end up with it in $Result.
I try to update users AD accounts properties with values imported from csv file.
The problem is that some of the properties like department allow strings of length of max length 64 that is less than provided in the file which can be up to 110.
I have found and adopted solution provided by TroyBramley in this thread - How to replace multiple strings in a file using PowerShell (thank You Troy).
It works fine but... Well. After all replaces have place the text is less meaningful than originally.
For example, original text First Department of something1 something2 something3 something4 would result in 1st Dept of sth1 sth2 sth3 sth4
I'd like to have control over the process so I can stop it when the length of the string drops just under the limit alowed by AD property.
By the way. I'd like to have a choice which replacement takes first, second and so on, too.
I put elements in a hashtable alphabetically but it seems that they are not processed this way. I can't figure out the pattern.
I can see the resolution by replacing strings one by one, controlling length after each replacement. But with almost 70 strings it leds to huge portion of code. Maybe there is simpler way?
You can iterate the replacement list until the string reaches the MaxLength defined.
## Q:\Test\2018\06\26\SO_51042611.ps1
$Original = "First Department of something1 something2 something3 something4"
$list = New-Object System.Collections.Specialized.OrderedDictionary
$list.Add("First","1st")
$list.Add("Department","Dept")
$list.Add("something1","sth1")
$list.Add("something2","sth2")
$list.Add("something3","sth3")
$list.Add("something4","sth4")
$MaxLength = 40
ForEach ($Item in $list.GetEnumerator()){
$Original = $Original -Replace $Item.Key,$Item.Value
If ($Original.Length -le $MaxLength){Break}
}
"{0}: {1}" -f $Original.Length,$Original
Sample output with $MaxLength set to 40
37: 1st Dept of sth1 sth2 sth3 something4
So basically what I'm trying to achieve is to get a MAC address from a text file and increment the value by one.
Been bashing my head against the Google/StackOverflow wall for a couple of hours, think there's a concept I'm just not getting.
PowerShell:
$Last_MAC_Address = (Get-Content -LiteralPath "\\UNC\Path\Last MAC Address.txt")
Write-Host ($Last_MAC_Address)
# Output: 00155DE10B73
$Next_MAC_Address = (($Last_MAC_Address | Format-Hex) + 1)
This is a 3 step process, and although PetSerAl answered it in the comments as a one liner, I'll break it down slightly for posterity (and use a different class).
The first step is to get the Hex number as a decimal (mathematical base 10, not type).
The Second step is the incrementation of the decimal.
And the final step is converting it back to hexadecimal.
broken down and not a one liner this will accomplish the task at hand:
$asDecimal = [System.Convert]::ToInt64("00155DE10B73", 16)
$asDecimal++
$asHex = [System.Convert]::ToString($asDecimal, 16)
Another option is to prefix the value with 0x and cast it to an int64:
$Next_MAC_Address = ([int64]"0x$Last_MAC_Address"+1).ToString('X12')
You could also use the format operator (-f) instead of the ToString() method:
$Next_MAC_Address = '{0:X12}' -f ([int64]"0x$Last_MAC_Address"+1)
There is, however, one thing that may be worth noting. MAC addresses aren't just random 6-byte numbers without any inner structure. They actually consist of two parts. The first 3 bytes form the Organizationally Unique Identifier (OUI), a vendor-specific prefix (00-15-5D is one of the OUIs belonging to Microsoft). Only the last 3 bytes are a random number, a unique identifier for each card from the vendor identified by the OUI.
Taking that into consideration you may want to split the MAC address accordingly, e.g. like this:
$oui, $nid = $Last_MAC_Address -split '(?<=^[0-9a-f]{6})(?=[0-9a-f]{6}$)'
or like this:
$oui = $Last_MAC_Address.Substring(0, 6)
$nid = $Last_MAC_Address.Substring(6, 6)
and increment only the NIC identifier, and only if it wouldn't overflow:
if ($nid -ne 'ffffff') {
$Next_MAC_Address = "{0}{1:X6}" -f $oui, ([int64]"0x$nid"+1)
} else {
Write-Error 'MAC address overflow.'
}