Powershell, WMI Methods - powershell

using WMI with PowerShell I found something I don't understand:
# Syntax 1
gwmi -Class Win32_Share | gm -MemberType Method
Output: Delete, GetAccessMask, SetShareInfo
# Syntax 2
$a = New-Object "System.Management.ManagementClass" "Win32_Share" :
$a | gm -MemberType Method
Output: Create.....
So: why I don't get the "Create" Method using the syntax "1"?

Because they return two different types of objects.
(gwmi -Class Win32_Share).GetType()
returns a System.Array instance while
(New-Object "System.Management.ManagementClass" "Win32_Share").GetType()
returns a System.Management.ManagementObject instance
Note that it doesn't make sense to call Create on an already instantiated object anyway, or in other words: why do you think you need it?
Edit
Your comment actually made me rethink (finally) and your conondrum is that you should use -query instead of -class. I have yet to figure out what the actual difference between both methods of calling is but I assume it's the same class/instance distinction.
Get-WmiObject -query "SELECT * FROM meta_class WHERE __class = 'Win32_Share'"

Related

Powershell - how to replace OS Version number with String

I am querying remote servers for their operating system. I know that I can return the Version, but I want to replace these values with the friendly name. The code I have so far is:
$Computer = (gc c:\servers.txt)
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $Computer -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
If ({$BuildVersion.Version -match "5.2.3790"})
{$Build="2003"}
Elseif ({$BuildVersion.Version -match "6.1.7601"})
{$Build="2008"}
Elseif ({$BuildVersion.Version -like "6.3.9600"})
{$Build="2012"}
But this doesn't seem to work and only returns "2003" regardless. Please help, I'm fairly new to PS and coding.
thanks
The problem is your if statements. Putting the Boolean expression inside squiggly brackets makes it a script block, and that's going to get cast as a string before being cast as a Boolean. Strings cast to Booleans always evaluate to true unless they're empty.
PS C:\> {$BuildVersion.Version -match "5.2.3790"}
$BuildVersion.Version -match "5.2.3790"
PS C:\> ({$BuildVersion.Version -match "5.2.3790"}) -as [bool]
True
PS C:\> $BuildVersion.Version -match "5.2.3790"
False
PS C:\> ($BuildVersion.Version -match "5.2.3790") -as [bool]
False
So what you're running is essentially:
if ([bool]'$BuildVersion.Version -match "5.2.3790"') [...]
And that's always going to be true.
Try:
$Computer = (gc c:\servers.txt)
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $Computer -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
If ($BuildVersion.Version -match "5.2.3790")
{
$Build = "2003"
}
Elseif ($BuildVersion.Version -match "6.1.7601")
{
$Build = "2008"
}
Elseif ($BuildVersion.Version -like "6.3.9600")
{
$Build = "2012"
}
Bottom line is that squiggly brackets are not parentheses and you can't use them like they are.
However, there's also a major logic error here. You're potentially fetching an array for $BuildVersion because you're reading from a file, but then you treat it like a single value. You never loop through $BuildVersion. However, I do not have enough information about what you're actually trying to do with your script (like what you do with $Build) to be able to fix that.
I originally said this, but I've since changed my mind
The reason this is only returning 2003 is that you're only running your If code on a single entry in the list.
Wrong
As TessellatingHeckler says, the reason your if wasn't working is that you had too many curly braces, so PowerShell wasn't actually evaluating your logic.
However, you still need to step through each of the computers to do what you're trying to do. We'll do that by adding in a ForEach loop. I also went ahead and replaced your If {} logic with a Switch statement, which I think is easier to understand for a scenario like this with multiple clauses. If's just get way too verbose.
Finally, I'm assuming you want to output the results too, so I added a custom object here, which is just a way of choosing which properties we want to display.
$Computer = (gc c:\servers.txt)
ForEach ($system in $computer){
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $system -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
switch ($build){
"5.2.3790" {$Build="2003"}
"6.1.7601" {$Build="2008"}
"6.3.9600" {$Build="2012"}
}
#output results
[pscustomobject]#{Server=$system;OSVersion=$build;CSName=$buildVersion.CSname}
}#EndOfForEach
Output
>Server OSVersion CSName
------ --------- ------
dc2012 2012 DC2012
sccm1511 2012 SCCM1511
You can use this:
Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption
Additionally you can see everything this WMI object holds like this:
Get-WmiObject -Class Win32_OperatingSystem | fl *
Edit: if you want to remove some text from the string, you can use -replace:
(Get-WmiObject -Class Win32_OperatingSystem |
Select-Object -ExpandProperty Caption) -replace "Microsoft Windows Server ",""

Pass array of CimInstance to CimMethod

I am working with WMI API through Cim cmdlets. The problem is that I can't figure out how to pass wmi object to wmi method that accepts array of wmi objects.
Here are method parameters definition:
Name CimType Qualifiers
---- ------- ----------
Path String {ID, in}
Permissions InstanceArray {EmbeddedInstance, ID, in}
ResetChildren Boolean {ID, in}
Path and ResetChildren are simple parameters. They accepts simple values like "/path" and $true respectively. But I have troubles with Permissions parameter.
Here is my code
#Acquiring object that I want to pass to method
$group = Get-CimInstance -Namespace "root\VisualSVN" -ClassName VisualSVN_Group -Filter "Name='Readers'"
#Acquiring object which method will be called
$repositories = Get-CimInstance -Namespace "root\VisualSVN" -ClassName VisualSVN_Repository
#Preparing method arguments
$args = #{
Path = "/";
Permissions = #($group[0]); #Trouble here
ResetChildren = $true
}
#Invoking method with arguments
Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Arguments $args
Execution of this code will result in error:
Invoke-CimMethod : Unable to cast object of type 'Microsoft.Management.Infrastructure.CimInstance' to type 'M
icrosoft.Management.Infrastructure.Native.InstanceHandle'.
Parameter name: value
At C:\somepath\script1.ps1:11 char:1
+ Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Argume ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Invoke-CimMethod], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.Management.Infrastructure.CimCmdlets.Invoke
CimMethodCommand
If you change code
Permissions = #($group[0]); #Trouble here
To code
Permissions = $group; #Trouble here
Then error message will also change:
Invoke-CimMethod : Unable to cast object of type 'Microsoft.Management.Infrastructure.Native.InstanceHandle'
to type 'System.Collections.IList'.
Parameter name: value
At C:\somepath\script1.ps1:11 char:1
+ Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Argume ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Invoke-CimMethod], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.Management.Infrastructure.CimCmdlets.Invoke
CimMethodCommand
Any ideas how to pass $group to method properly?
I had exactly same problem with VisualSVN_Repository::SetSecurity method.
When working with CIM method arguments, you must cast ANY array arguments to [CimInstance[]].
For example, this worked for me:
$Everyone = Get-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_Everyone
# Grant Everyone a Read/Write access:
$AccessRule = New-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_PermissionEntry -ClientOnly -Property #{ Account = $Everyone; AccessLevel = [UInt32]2 }
$SvnRepo = Get-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_Repository -Filter "Name='MY_REPOSITORY_NAME'"
Invoke-CimMethod -InputObject $SvnRepo -MethodName SetSecurity -Arguments #{
Path = '/';
Permissions = [CimInstance[]]$AccessRule;
ResetChildren = $true
} | Out-Null
You must cast array argument to [CimInstance[]] even when it is just a single item.
P.S.: Be careful with Ref array arguments too: you must first cast it to [CimInstance[]] and then to [ref[]]. For example, when calling VisualSVN_Group::Create method:
[CimInstance[]] $SvnUsers = [CimInstance[]]($ArrayOf_VisualSVN_User_Objects)
Invoke-CimMethod -ClassName VisualSVN_Group -Namespace root/VisualSVN -MethodName Create -Arguments #{
Members = [ref[]]$SvnUsers;
Name = 'MY_NEW_GROUP_NAME'
} | Out-Null
See also: Tip#5: Passing ref and embedded instances on PowerShell Blog.
If you had a repro that did not rely on software I do not have installed, I could answer definitively, so I will make my best attempt based on what I can infer from your question.
To make what you are assigning to Permissions an array, and you are only assigning one value, use a comma in front.
Permissions = ,$group
Here is some sample script and the output to demonstrate.
Script
$var1 = "any value"
$var2 = ,$var1
$var1.GetType()
$var2.GetType()
$var2.Count
$var2[0]
Output
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
True True Object[] System.Array
1
any value
A possible solution (it worked for me with a similar issue that led me here) based on the 'hint' that CIM are inert objects. In my case it was installing SCCM updates in a scripted manner while allowing for reboots if the updates required them, and stopping if the entire process took more than a certain amount of time (to not go past the available maintenance window).
I was passing a CIMUpdate object through to a powershell function to perform the update to allow for rebooting if necessary between updates (Perform a single update at a time, verify if reboot is needed, then try the next update).
The CIM object was giving a Type Mismatch error when forcing it to cast as [CIMInstance[]], and if not casting at all I would get the same Unable to cast object of type 'Microsoft.Management.Infrastructure.CimInstance' to type 'Microsoft.Management.Infrastructure.Native.InstanceHandle' error message.
Solution was to use that CIMUpdate and re-query to get a live version of the object within the function and then use that new object as the argument to the CIMMethod.
as of now, it works for me , the case I am working on is passing New-ScheduledTaskTrigger instance to a function.
I am passing a CimInstance to a function and receiving it as PSCustomObject type.
Send CimInstance receive as [PSCustomObject] -> at target it remains CimInstance object type
Send CimInstance receive as [CimInstance] -> at target it remains CimInstance object type
Send CimInstance receive as [CimInstance[]] -> at target it turn to CimInstance array type

WMI Object with another object as property ( how to pass an object as input parameter)

Hi
I have 2 Wmi object with one of them being a property of the other.
here is my code.
$connections = get-wmiobject -class 'HNet_Connection' -Namespace 'root\Microsoft\HomeNet' ;
$connectioName = 'ethernet';
#Write-Host $connections;
foreach ($connection in $connections)
{
if ( $connection.Name -eq $connectioName)
{
$connectionx = $connection;
Write-Host $connectionx;
}
}
$connectionproperties = get-wmiobject -class 'HNet_Connectionproperties' -Namespace 'root\Microsoft\HomeNet' ;
}
So I have the connection object now I want to use it as input parameter to find the associated connection properties and I have no Idea how to do it. I don't want to parse anything; I can do some parsing on the string but for sure there should be a way to use this object as is.
Any input on how to use object in the where ( simple WQL query instead of power shell)
select * from something where something.otherobject = otherobject;
something like above.
Any ideas.
Thanks.
So the quick answer is yes, or at least as far as I understand your question the answer is yes.
Get-WmiObject has a -filter parameter that you can use to input a WQL query. As an Example:
$service = Get-WmiObject Win32_service -Filter "Name = 'Bits'"
Get-WmiObject Win32_process -Filter "ProcessID = $($service.ProcessId)"
The $() symbol indicates a subexpression I use it in this case to expand out the ProcessID property on the Service variable.

How to get WMI object from a WMI object reference?

Similar to this question except that no answer was given with regards to the main question of getting an object from reference.
For example:
PS C:\Users\admin> Get-WmiObject -Namespace $namespace -Class $class
...
IsActive : 1
oA: \\.\ROOT\abc\abc\ABC:abc.xyz="tst2"
oB : \\.\ROOT\abc\abc\ABC:abc.xyz="tst3"
PSComputerName : admin-test2
oA and oB are references and therefore come up as strings in powershell. Is there a way I can get the object they represent using WMI query in powershell?
Assuming that oA and oB actually are strings you should be able to resolve these WMI paths to WMI objects like this:
Get-WmiObject -Namespace $namespace -Class $class | ForEach-Object {
$oA = [wmi]$_.oA
$oB = [wmi]$_.oB
}
Example:
PS C:\> $namespace = 'root/cimv2'
PS C:\> $class = 'Win32_OperatingSystem'
PS C:\> $obj1 = Get-WmiObject -Namespace $namespace -Class $class
PS C:\> $obj1
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 7601
RegisteredUser : foo
SerialNumber : 00371-OEM-8310595-XXXXX
Version : 6.1.7601
PS C:\> $obj1.GetType().FullName
System.Management.ManagementObject
PS C:\> $obj1.Path.Path
\\FOO\root\cimv2:Win32_OperatingSystem=#
PS C:\> ($obj1.Path.Path).GetType().FullName
System.String
PS C:\> $obj2 = [wmi]$obj1.Path.Path
PS C:\> $obj2
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 7601
RegisteredUser : foo
SerialNumber : 00371-OEM-8310595-XXXXX
Version : 6.1.7601
PS C:\> $obj2.GetType().FullName
System.Management.ManagementObject
Your question is rather vague, though, so I'm not sure if this answer actually covers what you've been asking.
As OP mentioned that all he wants is a generic answer (which is again tough given the nature of Object Paths and dependency on key), I am giving another example of using Associators Of WMI query.
$query = "ASSOCIATORS OF {Win32_Account.Name='DemoGroup2',Domain='DomainName'} WHERE Role=GroupComponent ResultClass=Win32_Account"
Get-WMIObject -Query $query | Select Name
If you need to use the example above, you need to first find out what is the key property and use that in the object path.
-----Original answer -----
What namespace? What class? You need to use associations and/or references to retrieve that. It is hard to give a generic answer unless we know the exact object path. For example,
$query = "REFERENCES OF {Win32_Service.Name='Netlogon'} WHERE ClassDefsOnly"
Get-WMIObject -Query $query
The above query will give all references of Win32_Service with an object path ServiceName='NetLogon'

(Powershell) new-object cmdlet doesn't return anything using [CodeDom.Compiler.TempFileCollection]

I'm writing a Powershell script in which I have to create several temporary files. I stumbled upon a .net class that sounds very useful to manage this task:[System.CodeDom.Compiler.TempFileCollection].
However for some strange reason I'm not able to create an object.
Here's what I've tried to do:
$a = new-object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\"
$a is empty after this call.
However if I do this:
(new-object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\").tempdir
It returns:
C:\folder1\
This means an object has been created. I just seem to be unable to save it into a variable! Does anybody have any idea why that is?
Another strange thing:
new-object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\" | gm
Returns:
Name MemberType Definition
---- ---------- ----------
AddExtension Method string AddExtension(string fileExtension), string
...
However this returns an exception (Get-Member : No object has been specified to the get-member cmdlet.) :
(new-object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\") | gm
I'm sorry for articulating this problem so badly, I don't know how to explain it better.
Any help would be much appreciated.
TL;DR: it's not empty.
How are you verifying that $a is empty? I think if you'll actually try just using it, it will work fine:
PS> $a = New-Object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\"
PS> $a.TempDir
C:\folder1\
PS> $a.AddFile("test1.txt",$false)
PS> $a.AddFile("test2.txt",$false)
PS> $a
C:\Users\Jaykul\AppData\Local\Temp\test2.txt
C:\Users\Jaykul\AppData\Local\Temp\test1.txt
PS>
In a nutshell, the problem you're having is that PowerShell unrolls collections (that is, it outputs the CONTENTS of the collection) when you send them through the pipeline. That's because otherwise, when you type:
PS> $a = Get-ChildItem
PS> $a
You would just see that it's an array: System.Object[] ...
In that sort of situation, you almost always want to see the contents of the array. As an aside: this is a great example of how PowerShell is first a shell language, and then a scripting language: it prioritizes the right behavior for a shell.
Anyway, there are a few exceptions for that unrolling of enumerable collections (strings are IEnumerable, but are not unrolled), and in fact, I think New-Object ... | is special-cased which is why it worked to pipe New-Object to Get-Member at all. Of course, once you wrapped it in parenthesis, you created a sub-expression which is parsed separately, so when you write:
(new-object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\") | gm
You basically have two pipelines, and the special treatment of New-Object vanishes, your collection is unrolled, and it's contents (nothing, because it's empty) are passed down the pipeline.
You can avoid this by using the unary comma operator:
$a = new-object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\"
,$a | gm
or
,(new-object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\") | gm
or by passing it to the -InputObject parameter directly instead of through the pipeline.
$a = new-object CodeDom.Compiler.TempFileCollection -argumentlist "C:\folder1\"
gm -InputObject $a
However, when you're using a fancy collection, and you want to see the values of the properties of the collection object itself, you'll probably need to do both:
PS> ft -in (,$a)
Count TempDir BasePath KeepFiles SyncRoot IsSynchronized
----- ------- -------- --------- -------- --------------
3 C:\folder1\ C:\folder1\t22jmahj False False