Why is the external IP not returned from a UPnP GetExternalIPAddress query using powershell/COM? - powershell

I am trying to use powershell to query/control UPnP devices.
I researched and came up with using the UPnP.UPnPDeviceFinder object to get the list of upnp devices.
Now as a first step to learn how to control a upnp device with powershell I want to get the external address of the router
using the upnp WANConnectionDevice that my router contains.
The closest example I could find was for VBScript at https://msdn.microsoft.com/en-us/library/windows/desktop/aa381230(v=vs.85).aspx
Note the goal is to understand how to control upnp devices this way - there are better ways I know of to get the external IP.
get all the UPnP devices
$finder = New-Object -ComObject UPnP.UPnPDeviceFinder
$devices = $finder.FindByType("upnp:rootdevice", 0)
from my earlier experimenting I know the UPnP WANConnectionDevice is the grandchild of the router COM object
$router = $devices | where friendlyname -eq RT-N66U
$routerchild = $router | % {$_.Children} # simple $router.Children syntax does not work, methods and properties are not available.
$routergrandchild = $routerchild | % {$_.Children}
this shows $routergrandchild is the WANConnectionDevice
$routergrandchild.FriendlyName
WANConnectionDevice
get the services object from this
$wanconnectservice = $routergrandchild | % {$_.services}
examine the invokeaction method
$wanconnectservice | gm -MemberType Method invokeaction
TypeName: System.__ComObject#{a295019c-dc65-47dd-90dc-7fe918a1ab44}
Name MemberType Definition
---- ---------- ----------
InvokeAction Method Variant InvokeAction (string, Variant, Variant)
From the get-member signature above and from my earlier experimenting I know the GetExternalIPAddress action has only 1 output arg
and no input args. So pass an empty array for input and for the 1 output arg pass a [ref]
$xip = #()
$wanconnectservice.InvokeAction('GetExternalIPAddress', #(), [ref]$xip)
The result of this is a request is sent to the router and it responds correctly with the externalip (confirmed by wireshark)
I would also expect an S_OK to be returned and have $xip contain the externalip string.
What I get is no response (no errors either) and $xip is unchanged.
I tried setting $xip = #('') and got the same result.
Is my syntax wrong or are my expectations wrong? Maybe because the external IP has not changed I get nothing in $xip?
Note that
$wanconnectservice.QueryStateVariable('ExternalIPAddress')
does return the current address - but this is cached. There is no wireshark activity since ExternalIPAddress is an evented
variable as noted in the documentation at:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa382244(v=vs.85).aspx

Well, I am embarrassed to publish this but the solution is very simple:
$xip = **$null**
$wanconnectservice.InvokeAction('GetExternalIPAddress', #(), [ref]$xip)
$xip

Related

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 *

-ExpandProperty doesn't show all the properties via Remote PowerShell

When I run the following code in Exchange PowerShell on an Exchange server it shows all the properties:
PS> Get-Mailbox Testeria | select -ExpandProperty EmailAddresses
SmtpAddress : Tester_IA#contoso.com
AddressString : Tester_IA#contoso.com
ProxyAddressString : smtp:Tester_IA#contoso.com
Prefix : SMTP
IsPrimaryAddress : False
PrefixString : smtp
SmtpAddress : TesterIA#contoso.com
AddressString : TesterIA#contoso.com
ProxyAddressString : SMTP:TesterIA#contoso.com
Prefix : SMTP
IsPrimaryAddress : True
PrefixString : SMTP
SmtpAddress : TesterIA#outlook.contoso.com
AddressString : TesterIA#outlook.contoso.com
ProxyAddressString : smtp:TesterIA#outlook.contoso.com
Prefix : SMTP
IsPrimaryAddress : False
PrefixString : smtp
But when I try to use Remote PowerShell on the local machine via
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri ("http://" + $Server + "/PowerShell/") -Authentication Kerberos
Import-PSSession $Session
and run the same code it show only this:
PS> Get-Mailbox Testeria | select -ExpandProperty EmailAddresses
smtp:Tester_IA#contoso.com
SMTP:TesterIA#contoso.com
smtp:TesterIA#outlook.contoso.com
How to understand this behaviour? How to get all the properties via Remote PowerShell?
PSVersion on the local machine is 5.1.14409.1005
PSVersion on the Exchange Server is 4.0
This probably occurs because when you access objects via PSRemoting the results are deserialized. You can see this is the case by looking at the TypeName of the resulting object by pipling it to Get-Member. You will see Deserialized prefixed to the Type:
Objects that have the "Deserialized." prefix in their type names are property bags that contain a deserialized representation of public
properties of the corresponding remote, live objects. As you can see
in the output of Get-Member those property bags don’t expose any
methods except ToString(), because usually methods cannot be invoked
in the remote session (for example, System.Diagnostics.Process.Kill()
can’t act on a remote process). Similarly setting and getting property
values of the property bags doesn’t execute any code (for example
WorkingSet property of
Deserialized.System.Diagnostics.Process.WorkingSet is only a snapshot
and doesn’t get updated when the remote process uses more memory).
https://blogs.msdn.microsoft.com/powershell/2010/01/07/how-objects-are-sent-to-and-from-remote-sessions/
My assumption is that the EmailAddresses property is a Script Property, which means it executes a script when called to get its sub properties. When you retrieve the object via Remoting you lose the ability to execute this script.
Unfortunately I don't have an Exchange system to verify this on at the moment.
Mark Wragg's answer provides helpful pointers, but let me elaborate:
Objects transmitted via PowerShell's remoting infrastructure undergo XML-based serialization at the remote source, and deserialization on receipt by the caller.
With the exception of a select few well-known types, type fidelity is lost during serialization / deserialization, and the deserialized objects are emulations of the original objects, as indicated by the Deserialized. prefix in their type name (as you can see by accessing .pstypenames[0] on a deserialized instance of by piping it to Get-Member); specifically, the limitation of these emulations - beyond not having the same type identity as the originals - are:
Deserialized objects lack the methods of the original.
For nested (scalar) objects (objects composed of multiple properties whose values too are objects composed of multiple properties, ...), the object graph is limited to a depth of 1.
In practice, this means that instances of non-well-known (scalar) types serialize such that property values that aren't themselves instances of well-known types are replaced by their .ToString() representations.
See this answer for a more comprehensive overview of serialization as part of PowerShell's remoting infrastructure.
Applied to your case:
Via remoting, the collection of rich email-object instances originally contained in the .EmailAddresses property of the objects returned by the Get-MailBox cmdlet is converted to a collection of strings, by calling .ToString() on each email-object instance, which seemingly returns the .ProxyAddressString property value.
Example:
For simplicity, the following demonstrates the recursion-depth (stringification) problem via a local background job created with Start-Job call (background jobs also use PowerShell's remoting infrastructure):
PS> Start-Job {
# Define a custom class, which is by definition not a well-known type.
# Give it a property of another non-well-known type, [regex].
class Foo { $Bar = [regex] 'hi' }
# Output an instance of the custom class.
[Foo]::new()
} | Receive-Job -Wait -AutoRemove |
ForEach-Object {
$_.Bar # Output the .Bar property value...
$_.Bar.GetType().FullName # ... and its data type.
}
hi
System.String
As you can see, the [regex] instance stored in property .Bar was replaced by its .ToString() representation.

Multiple Write-Output

I'm trying to write a PowerShell script that will compare two variables, one containing a list of services currently running and the other a pre-defined list of ones that should to see the differences.
I want to display the differences whilst also displaying the current services running.
$compared = Compare-Object $spServices $spServices23 -SyncWindow 0
Write-Output $compared
if($sTask -eq "Read")
{
foreach ($spService in $spServices)
{
$out = new-object psobject
$out | add-member noteproperty TypeName $spService.TypeName
$out | add-member noteproperty Status $spService.Status
Write-Output $out
}
}
However, when I output the Compare-Object results it shows them but then comes up blank for the output of the $out variable.
Any help on how I can do this whilst keeping the output formatted.
PowerShell ALWAYS does its best to try to make sure it converts output into the most useful format. One of the ways it does this is by seeing the type of object that it is first displaying in a function, and ensuring that all future objects also match this format. Sometimes it's possible, and sometimes it's not.
In the case of your code, PowerShell executes and then tries to emit the results of Compare-Object, and succeeds. 'Compare-Object' emits an object that has these properties.
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
InputObject NoteProperty System.ServiceProcess.ServiceController InputObject=AdobeARMservice
SideIndicator NoteProperty string SideIndicator==>
These properties set the stage for what can also be emitted within this command, unless you do some fancy tricks. The reason you're not seeing the output of your later commands is that they don't also output the same properties.
To illustrate this quirk in action, see this code:
function Ham2{
[pscustomobject]#{Name='FoxDeploy';Job="Coder"}
[pscustomobject]#{Name='Ham';Profession="Coder"}
}
When this executes, the properties of the FIRST object emitted determine what gets displayed later on in the code. For example:
>ham2
Name Job
---- ---
FoxDeploy Coder
Ham
Working around this
There are a few ways to work around this.
First and foremost, a PowerShell best practice is that your scripts should ONLY emit one type of object. This is why functions have an .OUTPUT declaration available in their Help and [CmdletBinding()], PowerShell expects a command to issue only one type of object, plus maybe some -Verbose, or ErrorStream messages.
If you REALLY want to emit two types of objects, you could ensure that the first object has all of the properties you might ever want to display. Going back to my earlier example, if I added a Profession property to the first object, now my second object's Profession property will now become visible.
function Ham2{
[pscustomobject]#{Name='FoxDeploy';Job="Coder";Profession=''}
[pscustomobject]#{Name='Ham';Profession="Coder"}
}
PS C:\Users\Stephen> ham2
Name Job Profession
---- --- ----------
FoxDeploy Coder
Ham Coder
Probably what you want but not recommended
If you REALLY want to emit two or more different types of objects (which you surely don't, right?) then you can get around this quirk by using Format-List or Format-Table. Be warned: these convert the output into text formatting commands and you'll lose Object properties and people will generally think it was a hacky thing to do. But it will work.
function iFeelIcky{
$spservices = gsv AdobeARMservice,bits
$compared = Compare-Object (get-service bits,winrm) $spservices -SyncWindow 0
$compared | Format-Table
foreach ($spService in $spServices)
{
$out = new-object psobject
$out | add-member noteproperty DisplayName $spService.DisplayName
$out | add-member noteproperty Status $spService.Status
$out
}
}
C:\Users\Stephen> iFeelIcky
InputObject SideIndicator
----------- -------------
AdobeARMservice =>
bits <=
bits =>
winrm <=
DisplayName Status
----------- ------
Adobe Acrobat Update Service Running
Background Intelligent Transfer Service Running
}
I hope that helped! Let me know if you'd like me to dive deeper or for some reason want me to stay up on this soap box :-)

Powershell UIAutomation - how to identify methods supported by an object?

I'm just learning a bit of UIA with PoSH. So far so good, albeit somewhat basic.... I have worked out the following:
$np = get-uiawindow -class 'notepad' -name '*'
$npedit = $np | Get-UiaDocument
# $npedit | send-keys "abc"
$npedit | Set-UiaControlText "def"
$currtext = $npedit | Get-UiaDocumentRangeText
So then the next obvious stage was to automate driving ie, or chrome...
$ie = Get-UiaWindow -class 'ieframe' -name 'environment agenc*'
$addbar = $ie | Get-UiaPane -automationid '40966' -class "rebarwindow32"
$addbar | Set-UiaControlText "news.bbc.co.uk"
But it seems objects of class rebarwindow32 do not support "Set-UiaControlText".
Similarly, in my earlier experimenting, I also tried automating the windows calculator. Clicking buttons etc is easy enough, but trying to read the results of any calculation proved more challenging - the UIAutomationSpy showed it as a UiaText object of class "Static".
So all this is aimed at asking a fairly simple question - how can I identify what methods are supported by any one object - I'm not asking just HOW to get the current value in the calculator results, or HOW to set the IE browser address bar... but HOW to FIND OUT for myself, what methods any one object I have identified supports.
Everything I've googled so far has shown specific examples of "How to do this or that" (eg select a menu, set some text) - but all very specific.
Based on comments from mike z below, I've now tried GetSupportedPatterns, which (based on a bit of googling on GetSupportedPatterns, is exactly what I need!).
However, if (based on my sample code above) I do $np.GetSupportedPatterns() I get:
Cached Current
------ -------
UIAutomation.UiaWindowPattern+Window... UIAutomation.UiaWindowPattern+Window...
UIAutomation.UiaTransformPattern+Tra... UIAutomation.UiaTransformPattern+Tra...
Which doesn't seem to tell me very much - and I cannot widen my window? Also, why only the cached operations, rather than the full list?
Thanks
Steve

How to call a method with output parameters in 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