I am getting a weird error, and that too sometimes while executing my script. The error is:
Method invocation failed because [System.Object[]] does not contain a method named 'op_Subtraction'.
The line in which I get this error is:
$LineNr = $dbsnap_file | Select-String -Pattern $check | Select-Object -ExpandProperty LineNumber
$del = $dbsnap_file[$LineNr-13] -split ':' | Select-Object -Last 1
$dbsnap_file is gc (some_file). That file contents are like:
AllocatedStorage : 5
AvailabilityZone : us-west-1a
DBInstanceIdentifier : test-multisite
DBSnapshotIdentifier : test-multisite-2015-09-03-04-15
Encrypted : False
Engine : mysql
EngineVersion : 5.6.19a
InstanceCreateTime : 12/19/2014 5:19:26 AM
Iops : 0
KmsKeyId :
LicenseModel : general-public-license
MasterUsername : root
OptionGroupName : default:mysql-5-6
PercentProgress : 100
Port : 3306
SnapshotCreateTime : 9/2/2015 11:15:36 PM
SnapshotType : automated
$check has value like test-multisite-2015-09-03-04-15. So, what I get as $del is the SnapshotCreateTime.
Iam recieving this error intermittently, sometimes its working, sometimes not. Can someone please guide me through what will be the issue.?
Like CB was saying Select-String will return all matches. You were expecting only one and the code was built around that assumption. The error you are getting is fairly explicit.
[System.Object[]] does not contain a method named 'op_Subtraction'
You were trying to subtract 13 from an object instead of an integer. As discussed in chat it turned out the issue was your source file had a double of data.
The solution in this case was to clean your source. If you are comfortable with assumptions you can also address this issue by updating the select
$LineNr = $dbsnap_file | Select-String -Pattern $check | Select-Object -First 1 -ExpandProperty LineNumber
That will ensure only one is returned. Caveat being you are ignoring real data. So verify the source and the contents of the $LineNr are the solutions I would recommend here.
Related
I'm working with a hashtable which I've built using a list of 3.5 million IP addresses stored in CSV format, and I am trying to search through this table using wildcards.
The CSV is MaxMind's list of IPs, which I convert to Hashtable using the following code
[System.IO.File]::ReadLines("C:\temp\iptest.csv") | ForEach-Object { $data= $_.split(','); $ht = #{"geoname_id"="$($data[1])";"registered_country_geoname_id"="$($data[2])"}
$name = $($data[0])
$mainIPHhash.add($name, $ht)}
The code just pulls out the CIDR and it's corresponding City/Country code.
This works well, and builds the table in a little over two minutes, but the issue I am now facing is searching this hashtable for wild card entries.
If I search for a complete CIDR, the search happens in milliseconds
$mainIPHhash.item("1.0.0.0/24")
Measure command reports - TotalSeconds : 0.0001542
But if I need to do a wildcard search, it has to loop through the hashtable looking for my like values, which takes a long time!
$testingIP = "1.0.*"
$mainIPHhash.GetEnumerator() | Where-Object { $_.key -like $testingIP }
Measure command reports - TotalSeconds : 33.3016279
Is there a better way for searching wildcard entries in Hashtables?
Cheers
Edit:
Using a regex search, I can get it down to 19 seconds. But still woefully slow
$findsStr = "^$(($testingIP2).split('.')[0])" +"\."+ "$(($testingIP2).split('.')[1])" +"\."
$mainIPHhash.GetEnumerator() | foreach {if($_.Key -match $findsStr){#Dostuff }}
The above takes the first two octets of the IP address, and uses regex to find them in the hashtable.
Days : 0
Hours : 0
Minutes : 0
Seconds : 19
Milliseconds : 733
Ticks : 197339339
TotalDays : 0.000228402012731481
TotalHours : 0.00548164830555556
TotalMinutes : 0.328898898333333
TotalSeconds : 19.7339339
TotalMilliseconds : 19733.9339
You can take the list of IPs and do either -like or -match for a list. Either should be faster than a Where-Object clause
$mainIPhash.Values -like '1.0.*'
$mainIPhash.Values -match '^1\.0\.'
Other solution may be, use group-object :
$contentcsv=import-csv "C:\temp\iptest.csv" -Header Name, geoname_id, registered_country_geoname_id |Group Name
$contentcsv | where Name -like '1.0.*'
Question
Should the script block of Invoke-Command, when run with a PSSession, always run on the remote computer?
Context
I ran the below powershell against a list of servers:
Clear-Host
$cred = get-credential 'myDomain\myUsername'
$psSessions = New-PSSession -ComputerName #(1..10 | %{'myServer{0:00}' -f $_}) -Credential $cred
Invoke-Command -Session $psSessions -ScriptBlock {
Get-Item -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters'
} | Sort-Object PSComputerName
# $psSessions | Remove-PSSession
This returned:
Hive: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos
Name Property PSComputerName
---- -------- --------------
Parameters MaxPacketSize : 1 myServer01
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer02
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer03
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer04
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer05
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer06
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer07
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer08
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer09
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer10
MaxTokenSize : 65535
All looks good; onlyl I'd not expected to see these values / I was running this as a quick sense check before setting the values on these servers to ensure I didn't overwrite anything.
I had a quick look at one of the servers using regedit; and found that MaxTokenSize and MaxPacketSize did not exist.
I then amended the command to use Get-ItemProperty instead of Get-Item:
Invoke-Command -Session $psSessions -ScriptBlock {
Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters' -Name 'MaxTokenSize'
} | Sort-Object PSComputerName
This time I got 10 errors:
Property MaxTokenSize does not exist at path HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters.
+ CategoryInfo : InvalidArgument: (MaxTokenSize:String) [Get-ItemProperty], PSArgumentException
+ FullyQualifiedErrorId : System.Management.Automation.PSArgumentException,Microsoft.PowerShell.Commands.GetItemPropertyCommand
+ PSComputerName : myServer01
# ... (and the same for the other 9 servers, with only PSComputerName changing)
Regarding where the values that were returned came from... they're from my local machine. Amending my local registry entries and rerunning the original command showed all "servers" as having the new value.
I'm guessing this is a bug; but because I've not played with PSSessions much so far wanted to check here in case it's an issue with my understanding / usage of these commands, or if there are known gotchas to watch out for when using PSSessions.
tl;dr:
The root cause is a bug in the formatting instructions for registry keys (as of Windows PowerShell 5.1.18362.125 and PowerShell Core 7.0.0-preview.2) leading to the unexpected mix of remote and local information - see GitHub issue #10341.
As an aside: Similarly, PowerShell's ETS (extended type system) can introduce problems too, as shown in Mathias' answer here.
The best workaround is to simply use Get-ItemProperty (without a -Name argument) instead of Get-Item.
Mathias R. Jessen has provided the crucial pointer in a comment on the question, and js2010's answer provides a limited workaround and a pointer to the root cause, but it's worth providing more background information:
PowerShell comes with formatting instructions for type Microsoft.Win32.RegistryKey, as output by Get-Item with a registry path.
These formatting instructions define a calculated column named Property for the default (tabular) view, which helpfully shows a summary of the output registry key's values, which involves accessing the registry again, using Get-ItemProperty as shown in js2010's answer.
However, that behind-the-scenes Get-ItemProperty call always accesses the local registry - even when the keys were retrieved from a different machine, via PowerShell remoting, so you'll end up with a spurious mix of remote and local information.
Note that, technically, when Get-Item is run remotely, what you receive locally is an approximation of the original Microsoft.Win32.RegistryKey object, due to the serialization and deserialization involved in remoting. This approximation is a custom object with static copies of the original object's property values, and its (simulated) type name is Deserialized.Microsoft.Win32.RegistryKey - note the prefix.
PowerShell applies formatting instructions based on the full type name of output objects, but in the absence of a specific instructions or a given Deserialized.<originalTypeName> type, PowerShell applies the instructions for <originalTypeName>, which is what causes the problems here.
A - cumbersome, but edition-agnostic[1] - way to see the problematic formatting instruction is to run the following command:
(Get-FormatData Microsoft.Win32.RegistryKey -PowerShellVersion $PSVersionTable.PSVersion).FormatViewDefinition.Control | % {
$colNames = $_.Headers.Label
$colValues = $_.Rows.Columns.DisplayEntry.Value
foreach ($i in 0..($colNames.Count-1)) {
[pscustomobject] #{
ColumnName = $colNames[$i]
ColumnValue = $colValues[$i]
}
}
} | Format-Table -Wrap
This yields the column names and definitions for the table view:
ColumnName ColumnValue
---------- -----------
Name PSChildName
Property
$result = (Get-ItemProperty -LiteralPath $_.PSPath |
Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider |
Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
The workaround suggested in js2010's answer - piping to Format-Table * or Format-List * is effective in the sense that it prevents the inapplicable local information from being displayed: by specifying properties explicitly (even by wildcard pattern *), only those properties are displayed on output - not also the flawed calculated column.
However, while the true Property property of the output objects provides access to the value names in the registry key at hand, it doesn't provide the actual data, the way that the calculated Property column does.
By contrast, using Get-ItemProperty without a -Name argument in lieu of Get-Item as a workaround returns both value names and data (correctly even when remoting) and even does so without restrictions (whereas Get-Item limits output to 5000 chars.)
The output format will be slightly different, but all the information is there.
[1] That is, the command works also in PowerShell Core, where the built-in formatting instructions are no longer maintained as external *.format.ps1xl files and are instead compiled into the executable.
Pipe it to fl * or ft * so it doesn't use the format file to display the registry keys. The format file runs get-itemproperty locally to try to display the properties.
From the bottom of $PSHOME\Registry.format.ps1xml for type Microsoft.Win32.RegistryKey:
<ScriptBlock>
$result = (Get-ItemProperty -LiteralPath $_.PSPath |
Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider |
Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
</ScriptBlock>
I have this Select-String using a regex containing a named group
$m=Select-String -pattern '(?<mylabel>error \d*)' -InputObject 'Some text Error 5 some text'
Select-String does its job:
PS > $m.Matches.groups
Groups : {0, mylabel}
Success : True
Name : 0
Captures : {0}
Index : 10
Length : 7
Value : Error 5
Success : True
Name : mylabel
Captures : {mylabel}
Index : 10
Length : 7
Value : Error 5
I can get the value of the matching named group by using the index of the group, no problem:
PS > $m.Matches.groups[1].Value
Error 5
But I have no success in getting the same result by using the named regex group (mylabel). I found statements like $m.Matches.groups["mylabel"].Value but that doesn't work on my machines (W10/W2012, PS 5.1)
You got one correct answer in the comment above, but here is how to do it without using the 0 match index:
$m.Matches.groups | ? { $_.Name -eq 'mylabel' } | Select-Object -ExpandProperty Value
As you probably understand from the title, I'm new to PowerShell, having a hard time even trying to describe my question. So please forgive my terminology.
Scenario
I am using PowerShell to query the audit log of Office 365. The cmdlet Search-UnifiedAuditLog returns "multiple sets of objects"(?), one of which has an array of other objects(?). The output is JSON if I got this right.
Here is an example of what is returned (I will call it one "Set of Objects"):
RunspaceId : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
RecordType : AzureActiveDirectoryStsLogon
CreationDate : 21/02/2017 12:05:23
UserIds : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Operations : UserLoggedIn
AuditData : {"CreationTime":"2017-02-21T12:05:23","Id":"{"ID":"00000000000000000","Type":3}],"ActorContextId":"xxxxxxxxxxxxxxxxxxxxxxxxx","ActorIpAddress":"xxxxxxxxxxxxx","InterSystemsId":"xxxxxxxxxxxxxxxxx","IntraSystemId":"000000000000-000000-000","Target":[{"ID":"00-0000-0000-c000-000000000000","Type":0}],"TargetContextId":"xxxxxxxxxxxxxxxxxx","ApplicationId":"xxxxxxxxxxxxxxxxxxxxxxxxxx"}
ResultIndex : 1
ResultCount : 16
Identity : xxxxxxxxxxxxxxxxxxxxxxxxxxx
IsValid : True
ObjectState : Unchanged
Now, I want some of the content of the AuditData line exported to a csv (normally containing much more data than copied here). This works fine for one "set of objects" (like the one above). To do this I use:
$LogOutput = Search-UnifiedAuditLog -StartDate 2/20/2017 -EndDate 2/23/2017 -ResultSize 1
$ConvertedOutput = ConvertFrom-Json -InputObject $LogOutput.AuditData
$ConvertedOutput | Select-Object CreationTime,UserId,ClientIP | Export-Csv -Path "C:\users\some.user\desktop\users.csv
ResultSize returns 1 instead of multiple "sets of objects". The ConvertFrom-Json does not work if I remove ResultSize.
So the question is:
Can I loop through all the "set of objects" and convert from json and have this exported line-by-line on a csv? Resulting in something like this:
UserId,Activity,UserIP
this#user.com, loggedIn, 10.10.10.10
that#user.org, accessedFile, 11.11.11.11
A pedagogic answer would be very, very much appreciated. Many thanks!
Instead of -ResultSize, try using Search-UnifiedAuditLog <args> | Select-Object -ExpandProperty AuditData | ConvertFrom-Json
This will make only the AuditData property get forwarded into ConvertFrom-Json and ignore the rest of the object from Search-UnifiedAuditLog
I am using the Invoke-Ping function (found here: https://gallery.technet.microsoft.com/scriptcenter/Invoke-Ping-Test-in-b553242a), which is working great. It creates output that looks like this:
> $Info = Invoke-Ping $ComputerNames -ErrorAction SilentlyContinue
> $Info[0..2]
Address : Machine1
IPV4Address : 10.10.44.213
IPV6Address :
ResponseTime : 0
STATUS : Responding
Address : Machine2
IPV4Address : 10.10.4.46
IPV6Address :
ResponseTime : 0
STATUS : Responding
Address : Machine3
IPV4Address : 10.10.4.58
IPV6Address :
ResponseTime : 0
STATUS : Responding
The problem I'm running into is when I try to do $Info.Address to output machine names. When I type $Info.Address I get
OverloadDefinitions
-------------------
System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Address(int )
I'm sure it's because .Address is already defined but I don't know how to work around this and get to the actual value I need - My object's Address value. I'm sure it's simple but I'm just ignorant... What's the trick to get to my value?
use this command this may help you.
$Info | %{$_.Address}
This is an interesting one. It looks like you've found a bug in the Test-Connection cmdlet. That is what Invoke-Ping uses to ping the computers.
PetSerAl is correct. You can use ForEach to get the correct output. Alternatively you could also manually specify the item in the array that you are looking for. Example:
#Display Address of First item
$info[0].Address
#Display Address of All items
$info | Foreach {$_.Address}
#or
for ($i=0; $i -lt $info.Count; $i++) { $info[$i].Address }
Interesting, haven't run into a case of this, but it appears the root cause is that an array has an Address method.
You can verify like this: Get-Member -InputObject #(), or, with your code, Get-Member -InputObject $Info.
Why is this happening? While it's quite convenient, we're relying on a language feature that will unravel a property from an array, and properties / methods directly on the array will take precedence. This means we need to resort to the various workarounds folks have answered with.
Cheers!