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

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

Related

Is there any way to export the contents of my Teams Chats for one day, by chat and time?

Is there any way to get and list out all my conversations in teams chats for one day? When I'm logging time, I lose track of what I worked on when I'm helping others, and the find function just gives me 8 messages on a big screen, which means I have to scroll through dozens of screens to get the "oh, this is what we're doing". I'd rather have a list with the chat name, time, and what was said. Powershell, CLI, KQL, don't care how. Thanks.
John Doe Chat: Doe, John [10:35] what is this about
John Doe Chat: Me, Myself [10:36] trying to come up with an example
PeopleChatOne: Joe [10:37] what are we, chopped liver?
Okay, after digging for a bit... turns out the answer is, as #sayali-msft and #anothervictimofthemouse mentioned, is the Graph API - which has a module. Note that you DO have a certain amount of access to your own stuff without needing an admins help, but you will have to explicitly grant it to each "application" that will connect to it. And there's a good amount of playing you can do with the Graph Explorer.
The below code will grab all of your chats, then grab the most recent 200 messages, filtered for the past day, and then will return the pertinent info.
Still missing at this point:
time zones seem wonky, specifically people in other time zones.
piping to OGV gives you all lines, when I really only want the first.
Fortunately, format-table DOES limit to one line per message
stripping out HTML works but is not great.
members may not be working.
Install-Module Microsoft.Graph
Import-Module Microsoft.Graph.Teams
$RequiredScopes = #("Chat.ReadBasic", "Chat.ReadWrite")
Connect-MgGraph -Scopes $RequiredScopes
#at this point a browser window should appear - allow it.
#I had to find my user id in graph explorer UI by running GET V1.0 https://graph.microsoft.com/v1.0/me
#unsure how to get it otherwise - but you don't need it with get-mgchat
get-mgchat
#take one of those IDs
#let's look at this chat:
get-mgchat -ChatId 19:deadbeefb2d949d88d4455f5279e5d8b#thread.v2
get-mgchatmessage -ChatId 19:deadbeefb2d949d88d4455f5279e5d8b#thread.v2
#this nests and walks through properly, strips HTML, but lord this will be slow. And shows more than one line per message, even when I try now to.
#By default you can get all your chats by running get-mgchat. -all and -pagesize 50 is required for the module to paginate the request and get you everything. But in my case it grabbed all 2000 chats. The -first 5 is for testing.
$tzone = Get-TimeZone # conversion from GMT to local time. https://jdhitsolutions.com/blog/powershell/7962/convert-to-local-time-with-powershell/
$mychats = get-mgchat -all -PageSize 50 |select -first 5
$all_chat_info = #() #force-setting to an array
$all_chat_info = foreach ($chat in $mychats) {
$chatinfo = get-mgchat -ChatId $chat.id #get base details about the CHAT itself
#set some details about the chat itself for the later query
$chatname = $chat.Topic
$members = $chat.Members
$chattype = $chat.chattype
#now get every message from that chat since midnight yesterday. Note LastModifiedDateTime is GMT. The jdhit page says -($tzone...), but all I had to do was .tolocaltime() ... I think.
#the -top 200 -pagesize 50 is to get the most recent 200 messages, and again you have to paginate.
$recentchatmessages = get-mgchatmessage -ChatId $chat.id -top 200 -pagesize 50 |where {$_.LastModifiedDateTime.tolocaltime() -gt (get-date).date.AddDays(-1)} # all from after midnight yesterday |select -first 5
#and now use select expression to add the above fields and parse the below fields, stripping out HTML (but I can't seem to only get the first line in OGV)
$recentchatmessages | select #{Label='LastModified';Expression={($_.LastModifiedDateTime.tolocaltime())}}, #{Label='ChatName';Expression={($chatname)}}, #{Label='members';Expression={($members)}}, #{Label='ChatType';Expression={($chattype)}},
#{Label='From';Expression={($_.from.user.displayname)}}, #{Label='Body';Expression={ ($_.Body.content -split '\n')[0] -replace '<[^>]+>',''}}
##{Label='From';Expression={($_.from.user.displayname)}}, #{Label='Body';Expression={( ($_.Body.content -replace '<[^>]+>','').split([Environment]::NewLine)|select -first 1)}}
}
$all_chat_info|format-table
#and now close/disconnect
Disconnect-MgGraph
Several problems present themselves:
All and PageSize appear broken (update: use "-all -pagesize 50")
Expanding the fields. Probably trivial for others, but a pain for me.
currently comes back like
Microsoft.Graph.PowerShell.Models.MicrosoftGraphItemBody
Microsoft.Graph.PowerShell.Models.MicrosoftGraphChatMessageFromIdentitySet
Some references I found while getting it working.
https://practical365.com/connect-microsoft-graph-powershell-sdk/ - getting the scope set
graph explorer
https://www.powershellgallery.com/packages/Microsoft.Graph/1.9.2
https://github.com/microsoftgraph/msgraph-sdk-powershell - the github for the actual modules. the readme is gold specifically https://github.com/microsoftgraph/msgraph-sdk-powershell/blob/dev/samples/5-Teams.ps1

O365 Powershell | Breaking up a long list into two sets of 100

I am looking to create a rule in Office 365 applied to all of the members in our org.
I would like this rule to append a warning on all incoming email from outside the organization with the same Display Names as our users.
When I attempt to apply it to all of the users in our org I get an error stating that the rule is too long.
In order to solve that I pulled a group, but I am still about 1000 characters over the limit.
I would like to make two variables, that each hold one half of the list, created by this command:
(Get-DistibutionGroupMember -Identity email#contoso.com -ResultSize Unlimited).DisplayName
I have attempted to modify the ResultSize parameter, but what I would need is result 1-100 and then 100-200 from the same list.
Another caveat to this problem is that the list cannot be static. It is something that the script will have to update every time it is run.
There is a sub-string command that you can use on a particular username that I have utilized when I made something for AD, but I am not aware of any way to break up a list like this.
If anyone has any other ways to solve this issue I would be more than open to any suggestion.
Thanks for taking the time to read this!
There are many ways of doing it. I found it very readable.
My favorite one is this one:
$ObjectList = 1..1000
$Step = 100
$counter = [pscustomobject] #{ Value = 0 }
$ObjectListSplitted = $ObjectList | Group-Object -Property { math]::Floor($counter.Value++ / $step) }
Then if you want to show the third subset just use this format :
$ObjectListSplitted[3].Group
Have a look to this solution already explained.
As a note other languages are capable of slicing an array of object with a start, stop and a step, have a look here if you're curious.

Are there publics Enums for ExtendedPropertyDefinition IDs?

Much like in the example from this question I see many code snippets on the web using magic numbers when making ExtendedPropertyDefinition. Example:
Dim PR_DELETED_ON As New ExtendedPropertyDefinition(26255, MapiPropertyType.SystemTime)
Dim PR_SEARCH_KEY As New ExtendedPropertyDefinition(12299, MapiPropertyType.Binary)
I have sort of found a reference location for these on MSDN. I can look them up individually as supposed to one large table. Here is the one for PR_DELETED_ON like in the above example
+------------------------+---------------+
| Associated properties: | PR_SEARCH_KEY |
+------------------------+---------------+
| Identifier: | 0x300B |
+------------------------+---------------+
| Data type: | PT_BINARY |
+------------------------+---------------+
| Area: | ID properties |
+------------------------+---------------+
0x300b being 12299 in decimal
I hate magic numbers so I was looking for an enum for this in the EWS API. I wrote this snippet to (hopefully) show me all the enums exposed.
$obj = [Reflection.Assembly]::LoadFile("C:\Program Files (x86)\EWSManagedAPI\Microsoft.Exchange.WebServices.dll")
$obj.GetTypes() | Where-object{$_.isenum -and ($_.ispublic -or $_.isnestedpublic)} | ForEach-Object{
$props = #{Name = $_.FullName}
[enum]::GetValues($_) | ForEach-Object{
$props.Integer = [int64]$_
$props.Text = $_
[pscustomobject]$props
}
}
I didn't see anything in the output that matched what I was looking at above. Does anyone know if there is a preexisting enum for these properties? If not that is fine. I just assumed there would be something out there.
Not the end of the world but I couldn't find them myself. Might explain why code snippets keep referencing to them.
No there is nothing in the EWS Managed API for this and AFAIK there is no master list maintained by Microsoft. There are also different types of Properties eg Tagged and Named properties and to use an Extended property in EWS you need to first define and tell Exchange to either return or set that property so EWS doesn't allow you to enumerate all the Extended properties on a Item like MAPI. The closest list that I know of is the one from the EWSEditor which is pretty comprehensive https://ewseditor.codeplex.com/SourceControl/latest#EWSEditor/PropertyInformation/KnownExtendedPropertiesData.cs . The Mapi include files also have a good list eg https://github.com/openchange/openchange/blob/master/properties_enum.h (but these are only tagged properties).

Get specific objects from Win32_PnpAllocatedResource

So I want to get those Pnp Devices, which use a given memory area (for example starting address = 655360). I'm using CIM/WMI and and the following command will get back resource and PnpEntity associations:
Get-CimInstance -ClassName Win32_PnpAllocatedResource
But after that how can I get the Win32_PnpEntity associated to Win32_DeviceMemoryAddress which has the starting address 655360?
So the field that you want is the Antecedent property. You only want the ones that refer to memory allocation, and of those you only want the ones that start at 655360. This is really basic stuff, and if you need to ask how to use a Where statement you probably need to go look on the internet on how to filter an array or something for a more detailed explanation to walk you through things.
Get-CimInstance -ClassName Win32_PnpAllocatedResource | Where{$_.Antecedent -like 'Win32_DeviceMemoryAddress (StartingAddress = 655360)'}
That will only return the entries where the starting address is 655360. You should, in theory, be able to use -eq but for this case it doesn't seem to work as expected so the value may have hidden characters, or may be a value type other than [String] so we have to use -Like or -Match and in this case -Like works fine, and is less complicated.

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

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