How to call a method with output parameters in PowerShell? - powershell

I'm working on a script to get started in PowerShell. I'm trying to convert a working VBScript script that enumerates mapped network drives on a remote Windows computer.
One of the tasks is to use remote WMI to read the registry and find the process owner of explorer.exe in order to determine who is logged in. This seems easy enough going by this guide.
However, the WMI method I need to call is GetOwner() from Win32_Process, which requires two output parameters to store its return value.
How can I call a method with output parameters? When I try to give it two strings, I get the error: Cannot find an overload for "GetOwner" and the argument count: "2".. The MSDN page says there are two parameters, so I'm not sure what I'm doing wrong.

Using the [ref] modifier:
SomeMethod( [ref] $a );
Notable blog entries
http://geekswithblogs.net/Lance/archive/2009/01/14/pass-by-reference-parameters-in-powershell.aspx
http://weblogs.asp.net/soever/archive/2009/03/26/powershell-return-values-from-a-function-through-reference-parameters.aspx

$explorer = gwmi Win32_Process -computerName computerName -filter "Name='explorer.exe' and SessionID=0"
$explorer.GetOwner() | select user,domain

Related

How to accept either a "live" object or a deserialized object of the same type in a param block? [duplicate]

This question already has an answer here:
Can a PowerShell function handle multiple input types?
(1 answer)
Closed 1 year ago.
I have a script that deals with Active Directory User objects (Microsoft.ActiveDirectory.Management.ADUser). I explicitly list the type in the function that processes these objects:
function Write-ADUser {
param (
[Microsoft.ActiveDirectory.Management.ADUser]$user
)
(...)
I also want this function to be able to take objects from remote sessions. The challenge is that objects returned from remote sessions are of the deserialized variety:
C:\> icm -session $sess { get-aduser -identity testuser -credential $cred } | gm
TypeName: Deserialized.Microsoft.ActiveDirectory.Management.ADUser
Is there a way to have my function param block accept either the "live" object or the deserialized variant? My function doesn't need to use methods - the deserialized variant has (or can be made to have) what I need.
The parameter sets idea was interesting and a helpful lead. After reviewing the documentation, this is the best option I could come up with:
function Write-ADUser {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[ValidateScript({
if ($_.GetType().Name -notin #('ADUser', 'PSObject')) {
throw ("Error: Invalid type ````{0}'' - expecting ADUser.") -f $_.GetType().Name
} else {
$true
}
})]
$user
)
...
One other comment. When looking into parameter sets I kept getting an error about ADUser. However, upon further digging I believe that error is because the Microsoft Active Directory PowerShell module isn't installed on my test computer. Therefore, the 'ADUser' type isn't defined. Because I want this script to run on computers that don't necessarily have the ADModule I am using the above logic. However, if I could guarantee that ADModule was present then I think parameter sets would be the way to go.
Apologies for not providing clearer requirements. I'm still learning PowerShell...
Note - updated based on feedback from #zett42

How to get service recovery options from Powershell?

I'm having issues figuring out an easy way to get the recovery options of a particular service in powershell.
Using command line sc: sc qfailure [servicename] [buffer size] works.
I also know that HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\\[service] will contain a FailureActions but i cant find any documentation on interpreting those values.
Is it just a matter of executing SC.EXE and parsing that data or is there a better way of doing this?
This will provide you Binary Value and you will have interpret it as follow which is tough part.
$actions = get-itemproperty hklm:\system\currentcontrolset\services\<ServiceShortName> | select -Expand FailureActions
typedef struct _SERVICE_FAILURE_ACTIONS {
DWORD dwResetPeriod;
LPTSTR lpRebootMsg;
LPTSTR lpCommand;
DWORD cActions;
SC_ACTION *lpsaActions;
} SERVICE_FAILURE_ACTIONS, *LPSERVICE_FAILURE_ACTIONS;
If you are using .NET
Follow this.
jborean93 has created a custom type that exposes the native C# service objects and methods to PowerShell. The included Get-ServiceRecovery and Set-ServiceRecovery functions make it easy to view and change service recovery settings within PowerShell.
https://gist.github.com/jborean93/889288b56087a2c5def7fa49b6a8a0ad
.\ServiceRecovery.ps1
(Get-ServiceRecovery -Name 'MyService').Actions
The ServiceController object that Get-Service doesn't contain all the properties for what a service can do.
To get access to more things try connecting to WMI. Try this command to see the properties we can see in WMI.
Get-WmiObject Win32_service | select -first 1 -property *

Setting a DateTime to $null/empty using PowerShell and the SCSM cmdlets

I'm currently trying to sync additional attributes from the AD (Active Directory) for user objects in SCSM (System Center Service Manager) using a PowerShell script.
The extension I wrote for this, includes an attribute for the expiration date of a AD user account (DateTime value, named DateTimeAttribute in the example) if the user account doesn't expire it should be empty/null.
Using Import-SCSMInstance, which should be similar to a CSV import, it kind of works by passing "null" for the field. The problem is that Import-SCSMInstance seems to be quite unreliable and it doesn't offer any kind of information of why it works or doesn't work. Using Update-SCSMClassInstance seems to work a lot better but I can't figure out a way to clear the field using this and even using [DateTime]::MinValue will result in an error, stating that it's an invalid value.
So would anyone have an idea on how to clear the value using Update-SCSMClassInstance or figure out why Import-SCSMInstance might or might not work?
A simple example for this could look like the following:
$server = "<ServerName>"
$extensionGuid = "<GUID>"
Import-Module 'C:\Program Files\System Center 2012 R2\Service Manager\Powershell\System.Center.Service.Manager.psd1'
New-SCManagementGroupConnection -ComputerName $server
$extensionClass = Get-SCSMClass -Id $extensionGuid
$scsmUserObject = Get-SCSMClassInstance -Class $extensionClass -Filter 'UserName -eq "<User>"'
# Error
$scsmUserObject.DateTimeAttribute = ""
# Works but fails on Update-SCSMClassInstance
$scsmUserObject.DateTimeAttribute = $null
$scsmUserObject.DateTimeAttribute = [DateTime]::MinValue
# Works
$scsmUserObject.DateTimeAttribute = "01-01-1970"
Update-SCSMClassInstance -Instance $scsmUserObject
It seems that you can't clear a date once it's filled. When you write $null, it sets the date to 1-1-0001 01:00:00, which is an invalid date causing the update-scsmclassinstance to fail.
What we have set as a user's AD property when we don't want something to expire, is 2999-12-31 (yyyy-MM-dd). Perhaps this could help, although it's not directly what you asked for...
Also, you can use the pipeline to update a property in SCSM:
Get-SCSMClassInstance -Class $extensionClass -Filter 'UserName -eq "<User>"' | % {$_.DateTimeAttribute = <date>; $_} | update-scsmclassinstance
It doesn't look like it's currently possible to clear custom date attributes using the PowerShell cmdlets.

PowerShell Error - Method Not Found

When using $Computer.StartsWith("WI-") I get the following error
Method invocation failed because [Microsoft.ActiveDirectory.Management.ADComputer] does not contain a method named 'StartsWith'
I am under the impression that this is a default method. Is there something I have to import to use this?
Try this instead
$env:COMPUTERNAME.StartsWith("WI-")
That error is pretty clear: an object of [Microsoft.ActiveDirectory.Management.ADComputer] type does not contain a method named 'StartsWith'.
Where the $Computer comes from? From Get-ADComputer cmdlet? Read How to list all AD computer object properties
Running $Computer | Get-Member | ft -AutoSize should prompt more.
Run $Computer.GetType() as well. For instance, next could work if $Computer is not an array:
$Computer.Name.StartsWith("WI-")
$Computer.CN.StartsWith("WI-")
$Computer.DisplayName.StartsWith("WI-")
However, next similar expressions could give another results:
$Computer.Name.ToUpper().StartsWith("WI-")
$Computer.CN.ToUpper().StartsWith("WI-")
$Computer.DisplayName.ToUpper().StartsWith("WI-")

returning and referencing remote powershell variable results

I'm very new to powershell so looking some assistance. I am trying to run remote powershell script to check health of or VDI enviroment using Citrix Commandlets. (I am implementing the script on Microsoft orchestrator .Net Activity). So I have the following code:
#2012 VDI Desktop check
$vdims = "MyCitrixPowershellSDKServer"
function vdidesktopcheck
{
asnp Citrix*
$result = Get-BrokerDesktop -InMaintenanceMode $True -DesktopGroupName Unidesk
$result
}
$output = invoke-command -computer $vdims -scriptblock ${function:vdidesktopcheck}
$machinename = $output.machinename
$machinename
$state = $output.RegistrationState
$state
So when I use orchestrator to expose the variables $machinename, $state - I get the 'last' result from the involked Get-BrokerDesktop query. However Get-Brokerdesktop query may have multiple machines returned so I am hoping to be able to reference each of the machines that match the query output. Thats the basic requirement - what I am hoping to be able to do is further refine the basic Get-BrokerDesktop query to be able to count the number on machines output to say > 3 (ie more than 3 machines in MaintMode is unacceptable) and also check that the MachineName property is not equal to a particular value, ie the 3 test machine names in the environment which may be expected to be in MaintenanceMode.
Hope this makes sense, if not I'll try and elaborate. Any help much appreciated!!
One of the limitations of Orchestrator is that you can only pass strings across the data bus, and you need to pass an array of objects. You need to serialize the object array to a string. One way to do that is to convert the array to json, then use convertfrom-json when you get it back to get an object array to work with.
Don't have a SCORCH server handy to test with, so this isn't tested at all.
$vdims = "MyCitrixPowershellSDKServer"
function vdidesktopcheck
{
asnp Citrix*
$result = Get-BrokerDesktop -InMaintenanceMode $True -DesktopGroupName Unidesk
$result
}
$output = invoke-command -computer $vdims -scriptblock ${function:vdidesktopcheck} |
select machinename,RegistrationState | ConvertTo-Json
$Output