Integer incrementing 2 times in foreach loop - powershell

I'm trying to figure out why my "$count--" is incrementing the integer down by 2 every time it cycles to a new IP. I want this to count down by 1 every time a new IP is being checked.
function scrape {
$PortList = #(443, 4433, 444, 433, 4343, 4444, 4443)
$IPList = $text1.text.split("`r")
$IPList = $text1.text.split()
$count = ($IPList.Count - 1)/2
Write-Host $IPList
Write-Host 'Firewalls with open ports will be listed here as they are discovered. Please wait while the script' `n 'processes the list of IP addresses. There are'$count 'IP addresses to check'
foreach ($IP in $IPList) {
$count--
foreach ($Port in $PortList) {
$url = "https://${IP}:${Port}"
$verb = 'GET'
$SiteData = try{httpget $url $verb}Catch{Continue}
If ($SiteData.Contains("auth1.html")) {
Write-Host ('https://' + $IP + ':' + $Port + " MGMT " + $IP) -ForegroundColor Red
$text2.text += ('https://' + $IP + ':' + $Port + " MGMT " + $IP + "`n")
}
Else {
If ($SiteData.Contains("SSLVPN")) {
Write-Host ('https://' + $IP + ':' + $Port + " SSLVPN " + $IP)
$text2.text += ('https://' + $IP + ':' + $Port + " SSLVPN " + $IP + "`n")
}
Else {
Write-Host ('https://' + $IP + ':' + $Port + " OTHER " + $IP)
$text2.text += ('https://' + $IP + ':' + $Port + " OTHER " + $IP + "`n")
}
}
}
}
}
EDIT/UPDATE: Okay so I figured out that the loop is counting blank space between the IP addresesses as a member of the array, which is causing that double decrement. Now I just have to figure out how to have it only count the addresses.

This looks suspicious:
$count = (15 - 1)/2
Have you tried running your script and outputting the value of $count from within the foreach loop to confirm that the value is what you think it is?
I also think you need to move the incremented statement AFTER doing the work.
Using the example below, the count output will return the following:
count: + 3 count: + 2 count: + 1 count: + 0
function scrape {
$PortList = #(443, 4433, 444, 433, 4343, 4444, 4443)
#$IPList = $text1.text.split("`r")
#$IPList = $text1.text.split()
$IPList = ("IP:A", "IP:B", "IP:C", "IP:D")
$count = ($IPList.Count - 1)
Write-Host $IPList
#Write-Host 'Firewalls with open ports will be listed here as they are discovered. Please wait while the script' `n 'processes the list of IP addresses. There are'$count 'IP addresses to check'
foreach ($IP in $IPList) {
Write-Host "count: " + $count
# go do some stuff
$count--
}
}

As you've since noticed, your problem is that your $IPList array contains empty elements between the IP addresses to examine.
The reason is that neither of your attempts to split a multi-line string into individual lines works correctly:
$text1.text.split("`r") # !! retains the `n after `r -> elements start with `n
$text1.text.split() # !! splits by *both* `r and `n -> empty extra elements
The simplest solution is to use the unary form of the -split operator, which conveniently splits a string into tokens by any runs of whitespace, including newlines (irrespective of whether they are Windows-style CRLF newlines (`r`n) or Unix-style LF-only newlines (`n)):
-split $text1.text # split by runs of whitespace, including newlines
Note, however, that if the individual lines contain intra-line whitespace (spaces, tabs), they will be broken into tokens as well.
If you truly only want to split by newlines (line breaks), use the binary form of the -split operator:
$text1.text -split '\r?\n' # split by CRLF or LF
Note that -split's RHS is a regular expression; \r?\n is a pattern that matches both CRLF and LF-only newlines.
If you know that the newlines are only ever the platform-appropriate ones, you can use $text1.text -split [Environment]::NewLine.

Related

powershell (Get-WmiObject win32_physicalmedia).serialnumber output hex

When I used (Get-WmiObject win32_physicalmedia).serialnumber the output was in hex. Example: 31323334353637383930. Then then I used the code below
$pass=""
$t=(Get-WmiObject win32_physicalmedia).serialnumber
$t -split '(.{2})' |%{ if ($_ -ne "") { $pass+=[CHAR]([CONVERT]::toint16("$_",16)) }}
write host $pass
The output was: 1234567890. The problem is that 1234567890 is not the serial number -- the real serial number is 2143658709. I need a script to swap the number $input "1234567890" to $output "214365768709".
this presumes your SN string is an even number of characters, and that the real number simply reverses the character pairs.
$InString = '1234567890'
$OutString = ''
foreach ($Index in 0..($InString.Length / 2))
{
$CurPos = $Index * 2
$OutString += $InString[$CurPos + 1] + $InString[$CurPos]
}
$OutString
output = 2143658709
I think this is called "middle endian" format, where every two bytes are reversed: middle-endian
Coming from a post here: WMI Win32_PhysicalMedia SMART ID in Vista and 7 Permissions

Get string length that is created dynamically

Lets say I want to do something like this:
$string = 'arbitrary string' # << is random
(Do-Something -Name ($string + (Get-Random)).Substring(0, [math]::Min(20, ??)
How do I refer to current length of the result of the expression inside ($string + (Get-Random))?
Fails when using Substring(0, 20) when string shorter than 20 chars
Exception calling "Substring" with "2" argument(s): "Index and length
must refer to a location within the string. Parameter name: length"
You'll have to assign the new string to a variable:
Do-Something -Name ($s = $string + (Get-Random)).Substring(0, [math]::Min(20,$s.Length))
A less terse version of the above:
$Name = $string + (Get-Random)
if($Name.Length -gt 20){
$Name = $Name.Substring(0, 20)
}
Do-Something -Name $Name
As mentioned in the comments, you could also select the first 20 chars by index from the string and combine again with the -join operator (v3.0 and newer):
$Name = "$string$(Get-Random)"[0..19] -join ''
Feeling frisky? Use regex (as suggested by wOxxOm):
$Name = "$string$(Get-Random)" -replace '^(.{20}).+','$1'
If the concatenated string is less than 20 characters nothing will be replaced, otherwise the entire string will match and get replaced by the first 20 characters
Another approach would be to generate a random number of X digits where X is 20 - $string.Length (only works if $string is guaranteed to be between 2 and 19 characters long):
$Name = "$string$(Get-Random -Min ('1' * (20 - $string.Length)) -Max ('9' * (20 - $string.Length)))"

Is there a better way to convert all control characters to entities in PowerShell 5?

Context: Azure, Windows Server 2012, PowerShell 5
I've got the following code to convert all control characters (ascii and unicode whitespace other than \x20 itself) to their ampersand-hash equivalents.
function ConvertTo-AmpersandHash {
param ([Parameter(Mandatory)][String]$Value)
# there's got to be a better way of doing this.
$AMPERHASH = '&#'
$SEMICOLON = ';'
for ($i = 0x0; $i -lt 0x20; $i++) {
$value = $value -replace [char]$i,($AMPERHASH + $i + $SEMICOLON)
}
for ($i = 0x7f; $i -le 0xa0; $i++) {
$value = $value -replace [char]$i,($AMPERHASH + $i + $SEMICOLON)
}
return $Value
}
As can be seen by the embedded comment, I'm sure there's a better way to do this. As it stands, one does some 65 iterations for each incoming string. Would regular expressions work better/faster?
LATER
-replace '([\x00-\x1f\x7f-\xa0])',('&#' + [byte][char]$1 + ';')
looks promising but the $1 is evaluating to zero all the time, giving me  all the time.
LATER STILL
Thinking that -replace couldn't internally iterate, I came up with
$t = [char]0 + [char]1 + [char]2 + [char]3 + [char]4 + [char]5 + [char]6
$r = '([\x00-\x1f\x7f-\xa0])'
while ($t -match [regex]$r) {
$t = $t -replace [regex]$r, ('&#' + [byte][char]$1 + ';')
}
echo $t
However out of that I still get

FINALLY
function ConvertTo-AmpersandHash {
param ([Parameter(Mandatory)][String]$Value)
$AMPERHASH = '&#'
$SEMICOLON = ';'
$patt = '([\x00-\x1f\x7f-\xa0]{1})'
while ($Value -match [regex]$patt) {
$Value = $Value -replace $Matches[0], ($AMPERHASH + [byte][char]$Matches[0] + $SEMICOLON)
}
return $Value
}
That works better. Faster too. Any advances on that?
Kory Gill's answer with the library call is surely a better approach, but to address your regex question, you can't evaluate code in the replacement with the -replace operator.
To do that, you need to use the .Net regex replace method, and pass it a scriptblock to evaluate the replacement, which takes a parameter of the match. e.g.
PS C:\> [regex]::Replace([string][char]2,
'([\x00-\x20\x7f-\xa0])',
{param([string]$m) '&#' + [byte][char]$m + ';'})

Your question is a little unclear to me, and could be a duplicate of What is the best way to escape HTML-specific characters in a string (PowerShell)?.
It would be nice if you explicitly stated the exact string you have and what you want it to converted to. One has to read the code to try to guess.
I am guessing one or more of these functions will do what you want:
$a = "http://foo.org/bar?baz & also <value> conversion"
"a"
$a
$b = [uri]::EscapeDataString($a)
"b"
$b
$c = [uri]::UnescapeDataString($b)
"c"
$c
Add-Type -AssemblyName System.Web
$d = [System.Web.HttpUtility]::HtmlEncode($a)
"d"
$d
$e = [System.Web.HttpUtility]::HtmlDecode($d)
"e"
$e
Gives:
a
http://foo.org/bar?baz & also <value> conversion
b
http%3A%2F%2Ffoo.org%2Fbar%3Fbaz%20%26%20also%20%3Cvalue%3E%20conversion
c
http://foo.org/bar?baz & also <value> conversion
d
http://foo.org/bar?baz & also <value> conversion
e
http://foo.org/bar?baz & also <value> conversion
I have one small function which helps me replacing as per my requirement:
$SpecChars are all the characters that are going to be replaced with nothing
Function Convert-ToFriendlyName
{param ($Text)
# Unwanted characters (includes spaces and '-') converted to a regex:
$SpecChars = '\', ' ','\\'
$remspecchars = [string]::join('|', ($SpecChars | % {[regex]::escape($_)}))
# Convert the text given to correct naming format (Uppercase)
$name = (Get-Culture).textinfo.totitlecase(“$Text”.tolower())
# Remove unwanted characters
$name = $name -replace $remspecchars, ""
$name
}
Example: Convert-ToFriendlyName "My\Name\isRana\Dip " will result me "MyNameIsranaDip".
Hope it helps you.

$line.StartsWith on multiple options

I want to be able to scan for two separate values in a line how do I do this?
$file = Get-Content "D:\path\tmp\certlist.txt"
foreach ($line in $file)
{
if ($line.StartsWith("snl") or $line.StartsWith("HSM1"))
{
$baseKey = "app.swift.snl."
$profileName = $line.Substring(0,13).TrimEnd()
$certType = $line.Substring(29,10).TrimEnd()
$renewalDate = Get_Unixtime $line.Substring(42,11).TrimEnd()
$expiryDate = Get_Unixtime $line.Substring(58,11).TrimEnd()
Send_Zabbix ($baseKey + $profileName + "." + $certType + ".renewaldate") $renewalDate
Send_Zabbix ($baseKey + $profileName + "." + $certType + ".expirydate") $expiryDate
}
}
The above doesn't work. It gives me an unexpected token.
For checking if a variable starts with one of several values a regular expression might be a better approach than daisy-chained StartsWith() calls.
if ($line -cmatch '^(snl|HSM1)')
{
...
}
^ matches the beginning of a string, (snl|HSM1) matches either snl or HSM1. -cmatch does a case-sensitive match. If you don't need case-sensitivity use -imatch or just -match.
Apparently all I needed was a '-'
if ($line.StartsWith("snl") -or $line.StartsWith("HSM1"))

Advanced Powershell Error Handling for Microsoft Modules (eg Lync)

I have a fairly complex script that includes Lync functionality. For this I import the PSSession.
When one of these commands errors, my error handling returns the line and command within the Lync module it fails on (eg line 2055 $steppablePipeline.End() ) instead of the line and command in my code (eg line 18 Enable-CSUser)
Is there some way to capture my calling command and line?
The following is a simplified example of the script
Function Main
{
Try
{
LyncTest
MailboxTest
}
Catch {$rtn = ReturnErrorCatch $_ ; Return $rtn} #Return Failure
}
Function MailboxTest
{
$script:callline = (Get-PSCallStack)[1].ScriptLineNumber
Get-Mailbox "kfsjlxghkjsdh" -ErrorAction:Stop
}
Function LyncTest
{
$script:callline = (Get-PSCallStack)[1].ScriptLineNumber
Enable-CSUser "kfsjlxghkjsdh" -ErrorAction:Stop
}
Function ReturnErrorCatch ($catch)
{
If ($catch.InvocationInfo.InvocationName -eq "throw"){$errorreturn = $catch.Exception.Message.ToString()}
Else
{
$line = "Line: " + $catch.InvocationInfo.ScriptLineNumber.ToString()
$exception = $catch.Exception.Message.ToString() -replace "`n"," "
$command = "Command: " + ($catch.InvocationInfo.Line.ToString() -replace "`t","").Trim()
$activity = " (Activity: " + $catch.CategoryInfo.Activity +")"
$line = $line + "(" + $callline.ToString() + ")"
$errorreturn = $line + " " + $exception + "`r`n" + $command + $activity
}
$Output = New-Object PSObject -Property #{Result=1;ResultReason=$errorreturn}
Return $Output
}
$result = Main
$result | fl
This returns:
Result : 1
ResultReason : Line: 2055(5) If SIP address type (SipAddressType) is set to None or is not set, you must specify the SIP address (SipAddress).
Command: $steppablePipeline.End() (Activity: Enable-CsUser)
Within my script, I am able to get the calling line by putting the $script:callline line within every subfunction (better ways to do this?), so if I comment out the LyncTest, and run the mailboxtest, I get the expected error return with erroring command, the line it failed on, and the line the function was called from.
As this is for Exchange 2010, I cannot use any Powershell V3 functionality, it must be V2.