Powershell: Loading all items/properties into a new object - object

Take this code:
$logged_on_user = get-wmiobject win32_computersystem | select username
If I want to output the value into a new string I'd do something like:
$A = $logged_on_user.username
However, if I do the following:
$logged_on_user = get-wmiobject win32_computersystem | select *
..to try to assign all the values to a new "object", do I?:
$logged_on_user.items
$logged_on_user.value
$logged_on_user.text
$logged_on_user.propertry
I've tried them all and they don't work.
Anybody got any ideas?
Thanks
P.S. I think I may have got the title of this question wrong.

In your example:
$logged_on_user = get-wmiobject win32_computersystem | select username
creates a new PSCustomObject with a single property - username. When you do the following:
$A = $logged_on_user.username
you are assigning the return value of the PSCustomObject's username property to a variable $A. Because the return type of the username property is a string, $A will also be a string.
When executing the following:
$cs = get-wmiobject win32_computersystem
If you assign $cs to a new variable like in the following:
$newVariable = $cs
Then $newVariable will reference the same object $cs does, so all properties and methods that are accessible on $cs will also be accessible on $newVariable.
If you don't specify any properties or call any methods on an object when assigning a return value to another variable, then the return value is the object itself, not the return value of one of the object's properties or methods.
Additional info, but not directly related to the question:
When you pipe the output of get-wmiobject to select-object, like in the following:
$cs = get-wmiobject win32_computersystem | select-object *
The variable $cs is of type: PSCustomObject as opposed to ManagementObject (as it is when you do not pipe to Select-Object) which has all of the same properties and their values that the ManagementObject that was piped in did.
So, if you only want the property values contained by the ManagementObject, there is no need to pipe the output to Select-Object as this just creates a new object (of type PSCustomObject) with the values from the MangementObject. Select-Object is useful when you either want to select a subset of the properties of the object that is being piped in, or if you want to create a new PSCustomObject with different properties that are calculated through expressions.

I'm not sure if you're asking about copying the results of Get-WmiObject or PowerShell objects in general. In the former case, Get-WmiObject returns instances of the ManagementObject class, which implements the ICloneable interface that provides a Clone method. You can use it like this...
$computerSystem = Get-WmiObject -Class 'Win32_ComputerSystem';
$computerSystemCopy = $computerSystem.Clone();
After the above code executes, $computerSystem and $computerSystemCopy will be identical but completely separate ManagementObject instances. You can confirm this by running...
$areSameValue = $computerSystem -eq $computerSystemCopy;
$areSameInstance = [Object]::ReferenceEquals($computerSystem, $computerSystemCopy);
...and noting that $areSameValue is $true and $areSameInstance is $false.

Related

Need to apply an if condition based on a check in Powershell

I am new to Powershell. I am actually getting the details of the azure data factory linked services but after get I need to use contains to check if the element exists. In python I would just check if string in a list but powershell not quite sure. Please check the code below.
$output = Get-AzDataFactoryV2LinkedService -ResourceGroupName $ResourceGroupName -DataFactoryName "xxxxxxxx" | Format-List
The output of the below is :
sample output given below
LinkedServiceName : abcdef
ResourceGroupName : ghijk
DataFactoryName : lmnopq
Properties : Microsoft.Azure.Management.DataFactory.Models.AzureDatabricksLinkedService
So now I try to do this:
if ($output.Properties -contains "Microsoft.Azure.Management.DataFactory.Models.AzureDatabricksLinkedService") {
Write-Output "test output"
}
But $output.Properties gives us the properties of that json.
I need to check if "Microsoft.Azure.Management.DataFactory.Models.AzureDatabricksLinkedService" exists in output variable and perform the required operations. Please help me on this.
The -contains operator requires a collection and an element. Here's a basic example of its proper use:
$collection = #(1,2,3,4)
$element1 = 5
$element2 = 3
if ($collection -contains $element1) {'yes'} else {'no'}
if ($collection -contains $element2) {'yes'} else {'no'}
What you've done is ask PowerShell to look in an object that isn't a collection for an element of type [string] and value equal to the name of that same object.
What you need to do is inspect this object:
$output.Properties | format-list *
Then once you figure out what needs to be present inside of it, create a new condition.
$output.Properties.something -eq 'some string value'
...assuming that your value is a string, for example.
I would recommend watching some beginner tutorials.

How can a Powershell function specify a ComObject parameter type?

Let's say that I'm trying to write a Powershell function that prints a result set to an Excel worksheet, like this:
function Write-ToWorksheet {
param (
[Parameter( Position = 0, Mandatory = $true )]
[MyLibrary.MyCustomResultType[]]
$ResultSet,
[Parameter( Position = 1, Mandatory = $true )]
[Excel.Worksheet]
$Worksheet
)
# ... Implementation goes here ...
}
And let's say that I'm calling it in a way something like this:
$excel = New-Object -ComObject Excel.Application
$wb = $excel.Workbooks.Add()
$results = Get-MyResults # Never mind what this does.
Write-ToWorksheet -ResultSet $results -Worksheet $wb.Sheets[ 1 ]
And this code will almost work, except that it chokes on my type specification of [Excel.Worksheet].
I realize that it is not necessary to specify the parameter type, and that the code will work just fine without it, as this answer points out.
But to please my inner pedant, is there any way to constrain the parameter type using a reference to a COM object type like Excel.Worksheet?
The reason that PowerShell is complaining about your Excel.Worksheet type is because it's not the name of the true .NET class/interface.
The parameter type you'd need to specify is Microsoft.Office.Interop.Excel.Worksheet instead (once the Excel interop assembly has been loaded, either directly via Add-Type or after the call to New-Object -ComObject Excel.Application as that will load the desired library too)
With that said, I don't believe this will work as intended because of the way that PowerShell handles COM objects by creating a transparent COM adapter layer between the true type of the variable exposed in PowerShell.
Interestingly there appear to be differences in the way that PowerShell handles parameter conversions when supplying them via Named arguments vs Positional arguments as can be seen with my demo code below:
function Get-WorksheetName {
param (
[Parameter( Position = 1, Mandatory = $true )]
[Microsoft.Office.Interop.Excel.Worksheet]
$Worksheet
)
return $Worksheet.Name
}
Calling the function using named arguments fails:
Whereas calling the function via positional arguments works as expected:
If positional arguments aren't something you'd like to use, then another alternative would be to drop the parameter type constraint and instead check the type using the ValidateScript attribute instead. This still ensures type safety:
function Get-WorksheetName {
param (
[Parameter(Position = 1, Mandatory = $true)]
[ValidateScript({$_ -is [Microsoft.Office.Interop.Excel.Worksheet]})]
$Worksheet
)
return $Worksheet.Name
}
Passing a different type of object would result in this:

Error on type conversion in Powershell EXCEL Com object

The following code:
$xl = New-Object -ComObject Excel.Application
$constants = $xl.gettype().assembly.getexportedtypes() | GM
where-object {$_.IsEnum -and $_.name -eq 'constants'}
$pso = new-object psobject
[enum]::getNames($constants) | foreach { $pso | Add-Member -MemberType NoteProperty $_ ($constants::$_) }
$xlConstants = $pso
Fails in the [enum]::getNames with the ff. message from Powershell 5.1 ISE:
Cannot convert argument "enumType", with value: "System.Object[]", for "GetNames" to type "System.Type":
"Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Type"."
At line:9 char:1
Would be grateful for some guidance.
The code was copied from a 2010 answer to a post, which wanted to extract the Excel Enum constants.
There's an extraneous GM (Get-Member) call in your code, and the Where-Object call - which should be where GM is - is disconnected from the pipeline above (which makes it a no-op).
$constants is therefore an array of objects (output by Get-Member), and passing an array to [enum]::GetNames() fails with the error you saw.
Find a corrected version of your code below, but your problem can be solved more simply, combining the solutions from the post your code came from as shown in this answer.
Here's a corrected version of your code that also shows you a faster PSv5+ solution for creating the custom object whose properties are named for the enumeration values' symbolic names and whose property values are the enumeration values themselves.
As the linked simpler solution shows, this isn't really necessary, however.
# Get the [enum]-derived type named 'Constants' from among
# the types that the Excel interop assembly exports.
$xl = New-Object -ComObject Excel.Application
$constantsType = $xl.GetType().Assembly.GetExportedTypes() |
Where-Object { $_.IsEnum -and $_.Name -eq 'constants' }
# Construct a custom object that reflects the enum type's
# enumeration values as properties.
$xlConstants = New-Object pscustomobject
[enum]::GetNames($constantsType).ForEach({
$xlConstants.psobject.properties.Add([psnoteproperty]::new($_, $constantsType::$_))
})
If you'd rather use the raw [int] values as the property values, use [psnoteproperty]::new($_, $constantsType::$_.value__) instead.
.psobject.properties provides access to any object's properties, and the .Add() method allows creating properties. [psnoteproperty]::new() creates a note property (shown as type NoteProperty by Get-Member), i.e., a property with a static value; the first argument is the property name, and the 2nd the property value, which can be of any type.

Powershell runspace output behaves differently depending on how returning custom object is defined

I am experimenting with Powershell runspaces and have noticed a difference in how output is written to the console depending on where I create my custom object. If I create the custom object directly in my script block, the output is written to the console in a table format. However, the table appears to be held open while the runspace pool still has open threads, i.e. it creates a table but I can see the results from finished jobs being appended dynamically to the table. This is the desired behavior. I'll refer to this as behavior 1.
The discrepancy occurs when I add a custom module to the runspace pool and then call a function contained in that module, which then creates a custom object. This object is printed to the screen in a list format for each returned object. This is not the desired behavior. I'll call this behavior 2
I have tried piping the output from behavior 2 to Format-Table but this just creates a new table for each returned object. I can achieve the desired effect somewhat by using Write-Host to print a line of the object values but I don't think this is appropriate considering it seems there is a built in behavior that can achieve my desired result if I can understand it.
My thoughts on the matter are that it has something to do with the asynchronous behavior of the runspace. I'm new to powershell but perhaps when the custom object comes directly from the script block there is a hidden method or type declaration telling powershell to hold the table open and wait for result? This would be overridden when using the second technique because its coming from my custom function?
I would like to understand why this is occurring and how I can achieve behavior 1 while being able to use the custom module, which will eventually be very large. I'm open to a different method technique as well, so long as its possible to essentially see the table of outputs grow as jobs finish. The code used is below.
$ISS = [InitialSessionState]::CreateDefault()
[void]$ISS.ImportPSModule(".\Modules\Test-Item.psm1")
$Pool = [RunspaceFactory]::CreateRunspacePool(1, 5, $ISS, $Host)
$Pool.Open()
$Runspaces = #()
# Script block to run code in
$ScriptBlock = {
Param ( [string]$Server, [int]$Count )
Test-Server -Server $Server -Count $Count
# Uncomment the three lines below and comment out the two
# lines above to test behavior 1.
#[int] $SleepTime = Get-Random -Maximum 4 -Minimum 1
#Start-Sleep -Seconds $SleepTime
#[pscustomobject]#{Server=$Server; Count=$Count;}
}
# Create runspaces and assign to runspace pool
1..10 | ForEach-Object {
$ParamList = #{ Server = "Server A" Count = $_ }
$Runspace = [PowerShell]::Create()
[void]$Runspace.AddScript($ScriptBlock)
[void]$Runspace.AddParameters($ParamList)
$Runspace.RunspacePool = $Pool
$Runspaces += [PSCustomObject]#{
Id = $_
Pipe = $Runspace
Handle = $Runspace.BeginInvoke()
Object = $Object
}
}
# Check for things to be finished
while ($Runspaces.Handle -ne $null)
{
$Completed = $Runspaces | Where-Object { $_.Handle.IsCompleted -eq $true }
foreach ($Runspace in $Completed)
{
$Runspace.Pipe.EndInvoke($Runspace.Handle)
$Runspace.Handle = $null
}
Start-Sleep -Milliseconds 100
}
$Pool.Close()
$Pool.Dispose()
The custom module I'm using is as follows.
function Test-Server {
Param ([string]$Server, [int]$Count )
[int] $SleepTime = Get-Random -Maximum 4 -Minimum 1
Start-Sleep -Seconds $SleepTime
[pscustomobject]#{Server = $Server;Item = $Count}
}
What you have mentioned sounds completely normal to me. That is how powershell is designed because it shares the burden of display. If the user has not specified how to display, PowerShell decides how to.
I couldn't reproduce your issue with the code provided but I think this will solve your problem.
$FinalTable = foreach ($Runspace in $Completed)
{
$Runspace.Pipe.EndInvoke($Runspace.Handle)
$Runspace.Handle = $null
}
$FinalResult will now have the table format you expect.
It appears that my primary issue, aside from errors in my code, was a lack of understanding related to powershell's default object handling. Powershell displays the output of objects as a table when there are less than four key-value pairs and as a list when there are more.
The custom object returned in my test module had more than for key-value pairs while the custom object I returned directly only had two. This resulted in what I thought was odd behavior. I compounded the issue by removing some key-value pairs in my posted code to shorten it and then didn't test it (sorry).
This stackoverflow post has a lengthy answer explaining the behavior some and providing examples for changing the default output.

Find specific sentence in a web page using powershell

I need to use powershell to resolve IP addresses via whois. My company filters port 43 and WHOIS queries so the workaround I have to use here is to ask powershell to use a website such as https://who.is, read the http stream and look for the Organisation Name matching the IP address.
So far I have managed to get the webpage read into powershell (example here with a WHOIS on yahoo.com) which is https://who.is/whois-ip/ip-address/206.190.36.45
So here is my snippet:
$url=Invoke-WebRequest https://who.is/whois-ip/ip-address/206.190.36.45
now if I do :
$url.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False HtmlWebResponseObject Microsoft.PowerShell.Commands.WebResponseObject
I see this object has several properties:
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
AllElements Property Microsoft.PowerShell.Commands.WebCmdletElementCollection AllElements {get;}
BaseResponse Property System.Net.WebResponse BaseResponse {get;set;}
Content Property string Content {get;}
Forms Property Microsoft.PowerShell.Commands.FormObjectCollection Forms {get;}
Headers Property System.Collections.Generic.Dictionary[string,string] Headers {get;}
Images Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Images {get;}
InputFields Property Microsoft.PowerShell.Commands.WebCmdletElementCollection InputFields {get;}
Links Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Links {get;}
ParsedHtml Property mshtml.IHTMLDocument2 ParsedHtml {get;}
RawContent Property string RawContent {get;}
RawContentLength Property long RawContentLength {get;}
RawContentStream Property System.IO.MemoryStream RawContentStream {get;}
Scripts Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Scripts {get;}
StatusCode Property int StatusCode {get;}
StatusDescription Property string StatusDescription {get;}
but every time I try commands like
$url.ToString() | select-string "OrgName"
Powershell returns the whole HTML code because it interprets the text string as a whole. I found a workaround dumping the output into a file and then read the file through an object (so every line is an element of an array) but I have hundreds of IPs to check so that's not very optimal to create a file all the time.
I would like to know how I could read the content of the web page https://who.is/whois-ip/ip-address/206.190.36.45 and get the line that says :
OrgName: Yahoo! Broadcast Services, Inc.
and just that line only.
Thanks very much for your help! :)
There are most likely better ways to parse this but you were on the right track with you current logic.
$web = Invoke-WebRequest https://who.is/whois-ip/ip-address/206.190.36.45
$web.tostring() -split "[`r`n]" | select-string "OrgName"
Select-String was returning the match as it, previously, was one long string. Using -split we can break it up to just get the return you expected.
OrgName: Yahoo! Broadcast Services, Inc.
Some string manipulation after that will get a cleaner answer. Again, many ways to approach this as well
(($web.tostring() -split "[`r`n]" | select-string "OrgName" | Select -First 1) -split ":")[1].Trim()
I used Select -First 1 as select-string could return more than one object. It would just ensure we are working with 1 when we manipulate the string. The string is just split on a colon and trimmed to remove the spaces that are left behind.
Since you are pulling HTML data we could also walk through those properties to get more specific results. The intention of this was to get 1RedOne answer
$web = Invoke-WebRequest https://who.is/whois-ip/ip-address/206.190.36.45
$data = $web.AllElements | Where{$_.TagName -eq "Pre"} | Select-Object -Expand InnerText
$whois = ($data -split "`r`n`r`n" | select -index 1) -replace ":\s","=" | ConvertFrom-StringData
$whois.OrgName
All that data is stored in the text of the PRE tag in this example. What I do is split up the data into its sections (Sections are defined with blank lines separating them. I look for consecutive newlines). The second group of data contains the org name. Store that in a variable and pull the OrgName as a property: $whois.OrgName. Here is what $whois looks like
Name Value
---- -----
Updated 2013-04-02
City Sunnyvale
Address 701 First Ave
OrgName Yahoo! Broadcast Services, Inc.
StateProv CA
Country US
Ref http://whois.arin.net/rest/org/YAHO
PostalCode 94089
RegDate 1999-11-17
OrgId YAHO
You can also make that hashtable into a custom object if you prefer dealing with those.
[pscustomobject]$whois
Updated : 2017-01-28
City : Sunnyvale
Address : 701 First Ave
OrgName : Yahoo! Broadcast Services, Inc.
StateProv : CA
Country : US
Ref : https://whois.arin.net/rest/org/YAHO
PostalCode : 94089
RegDate : 1999-11-17
OrgId : YAHO
it it very simple to use whois app this is for microsoft put app in System32 or windir and in powershell use whois command then get-string get "orgname" like this
PS C:\> whois.exe -v 206.190.36.45 | Select-String "Registrant Organization"
Registrant Organization: Yahoo! Inc.
I advise you this app because has more information for your work
Here you go, the way to do this is in fact to do an Invoke-WebRequest. If we take a look at some of the properties of the object we get from Invoke-WebRequest, we can see that PowerShell has already parsed some of the HTML and text for us.
All that we have to do is pick out some of the values we'd like to work with. For instance, taking a peek at the ParsedText field, we see these results.
These fields begin on about line 30 or so. In my approach to solving this problem we know that we'll find good data like this mid-way down the page, so if we could scrape the values from these lines, we'd be on our way to working with the data. The code to accomplish this first part is this:
$url = "https://who.is/whois-ip/ip-address/$ipaddress"
$Results = Invoke-WebRequest $url
$ParsedResults = $Results.ParsedHtml.body.outerText.Split("`n")[30..50]
Now, PowerShell has a number of very powerful commands to import and convert data into various formats. For instance, if we could only replace the ':' colon character with an equals sign '=', we could send the whole mess over to ConverFrom-StringData and have rich PowerShell objects to work with. It turns out that we can easily do that using the universal -Replace operator, like this
$Results.ParsedHtml.body.outerText.Split("`n")[30..50] -replace ":","="
I figured you might want to do this again in the future, so I took the entire thing and made it into a simple five line function for you. Throw this into your $Profile and enjoy.
So the finished result looks like this:
Function Get-WhoIsData {
param($ipaddress='206.190.36.45')
$url = "https://who.is/whois-ip/ip-address/$ipaddress"
$Results = Invoke-WebRequest $url
$ParsedResults = $Results.ParsedHtml.body.outerText.Split("`n")[30..50] -replace ":","=" | ConvertFrom-StringData
$ParsedResults }
and using it works this way:
PS C:\windows\system32> Get-WhoIsData -ipaddress 206.190.36.45
Name Value
---- -----
NetRange 206.190.32.0 - 206.190.63.255
CIDR 206.190.32.0/19
NetName NETBLK1-YAHOOBS
NetHandle NET-206-190-32-0-1
Parent NET206 (NET-206-0-0-0-0)
NetType Direct Allocation
OriginAS
Organization Yahoo! Broadcast Services, Inc. (YAHO)
RegDate 1995-12-15
Updated 2012-03-02
Ref http=//whois.arin.net/rest/net/NET-206-190-32-0-1
OrgName Yahoo! Broadcast Services, Inc.
OrgId YAHO
Address 701 First Ave
City Sunnyvale
StateProv CA
PostalCode 94089
You can then select any of the properties you'd like using normal Select-Object or Where-Object commands. For example, to pull out just the orgName property, you'd use this command:
(Get-WhoIsData).OrgName
>Yahoo! Broadcast Services, Inc.

Resources