In PowerShell, how do you get an object's property value by specifying its name (a string)? I want something like the following:
$obj = get-something
# View the object's members:
$obj | gm
# I could retrieve a property by doing so:
write-host $obj.SomeProp
# But for many purposes, I would really want to:
write-host $obj | Get-PropertyByName "SomeProp"
Is there something similar to "Get-PropertyByName" in PowerShell?
Sure
write-host ($obj | Select -ExpandProperty "SomeProp")
Or for that matter:
$obj."SomeProp"
Expanding upon #aquinas:
Get-something | select -ExpandProperty PropertyName
or
Get-something | select -expand PropertyName
or
Get-something | select -exp PropertyName
I made these suggestions for those that might just be looking for a single-line command to obtain some piece of information and wanted to include a real-world example.
In managing Office 365 via PowerShell, here was an example I used to obtain all of the users/groups that had been added to the "BookInPolicy" list:
Get-CalendarProcessing conferenceroom#example.com | Select -expand BookInPolicy
Just using "Select BookInPolicy" was cutting off several members, so thank you for this information!
You can get a property by name using the Select-Object cmdlet and specifying the property name(s) that you're interested in. Note that this doesn't simply return the raw value for that property; instead you get something that still behaves like an object.
[PS]> $property = (Get-Process)[0] | Select-Object -Property Name
[PS]> $property
Name
----
armsvc
[PS]> $property.GetType().FullName
System.Management.Automation.PSCustomObject
In order to use the value for that property, you will still need to identify which property you are after, even if there is only one property:
[PS]> $property.Name
armsvc
[PS]> $property -eq "armsvc"
False
[PS]> $property.Name -eq "armsvc"
True
[PS]> $property.Name.GetType().FullName
System.String
As per other answers here, if you want to use a single property within a string, you need to evaluate the expression (put brackets around it) and prefix with a dollar sign ($) to declare the expression dynamically as a variable to be inserted into the string:
[PS]> "The first process in the list is: $($property.Name)"
The first process in the list is: armsvc
Quite correctly, others have answered this question by recommending the -ExpandProperty parameter for the Select-Object cmdlet. This bypasses some of the headache by returning the value of the property specified, but you will want to use different approaches in different scenarios.
-ExpandProperty <String>
Specifies a property to select, and indicates that an attempt should
be made to expand that property
https://technet.microsoft.com/en-us/library/hh849895.aspx
[PS]> (Get-Process)[0] | Select-Object -ExpandProperty Name
armsvc
powershell variables
Try this :
$obj = #{
SomeProp = "Hello"
}
Write-Host "Property Value is $($obj."SomeProp")"
Here is an alternative way to get an object's property value:
write-host $(get-something).SomeProp
$com1 = new-object PSobject #Task1
$com2 = new-object PSobject #Task1
$com3 = new-object PSobject #Task1
$com1 | add-member noteproperty -name user -value jindpal #Task2
$com1 | add-member noteproperty -name code -value IT01 #Task2
$com1 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$com2 | add-member noteproperty -name user -value singh #Task2
$com2 | add-member noteproperty -name code -value IT02 #Task2
$com2 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$com3 | add-member noteproperty -name user -value dhanoa #Task2
$com3 | add-member noteproperty -name code -value IT03 #Task2
$com3 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$arr += $com1, $com2, $com3 #Task4
write-host "windows version of computer1 is: "$com1.ver() #Task3
write-host "user name of computer1 is: "$com1.user #Task6
write-host "code of computer1 is: "$com1,code #Task5
write-host "windows version of computer2 is: "$com2.ver() #Task3
write-host "user name of computer2 is: "$com2.user #Task6
write-host "windows version of computer3 is: "$com3.ver() #Task3
write-host "user name of computer3 is: "$com1.user #Task6
write-host "code of computer3 is: "$com3,code #Task5
read-host
Related
Is there a way to automatically address/select the only member in a object?
In my case the 3rd level member jobf can vary. But on that level it will always only have one member.
### get aeObject ###
try {
$response = Invoke-RestMethod -Method GET -Uri "$client_id/objects/$($i -replace "#","%23")" -Headers $header
}
catch {
Write-Error -Message "$($_.Exception.Message)"
$Error[0] | Format-List -Force
}
### modify JSON and import it ###
$JSON = $response | ConvertTo-Json -Depth 100 | ConvertFrom-Json
$JSON.data
if ($JSON.data.jobf.general_attributes.time_zone) {
$JSON.data.jobf.general_attributes.time_zone = "$TZ"
}
else {
$JSON.data.jobf.general_attributes | Add-Member -Name "time_zone" -MemberType NoteProperty -Value "$TZ"
}
enter image description here
You can use Get-Member to iterate the members at the third level, then filter out the noise and select the name of the first (and only one) member.
# Get the third level single item (eg: jobf, jobx, etc)
$Job = ($JSON.data | Get-Member -MemberType NoteProperty | Select-Object -First 1).Name
# Instead of "jobf", we use the value stored in $Job
if ($JSON.data.$Job.general_attributes.time_zone) {
$JSON.data.$Job.general_attributes.time_zone = "$TZ"
}
else {
$JSON.data.$Job.general_attributes | Add-Member -Name "time_zone" -MemberType NoteProperty -Value "$TZ"
}
References
Get-Member - Gets the properties and methods of objects.
We have the script here in the company when the user is fired, for a few months we leave the user disabled and do not delete it, however I need to clean our shared mailboxes and check which of these unblown users still have access to these mailboxes
Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize:Unlimited | Get-MailboxPermission |Select-Object Identity,User,AccessRights | Where-Object {($_.user -like '#')}|Export-Csv C:\Temp\sharedfolders.csv -NoTypeInformation
I already have my script that checks all shared mailboxes, but I need to create a kind of filter where the results show me only users who are no longer active in AD, someone can help me, please?
I think this is what you are looking for.
$mailboxes = Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize unlimited
$mailboxes | Get-MailboxPermission | ForEach-Object{
$perm = $_
$ADuser = Get-ADUser $perm.user.securityidentifier.value
$perm | Add-Member -MemberType NoteProperty -Name UserEnabled -Value $ADuser.Enabled
$perm
} | Select-Object Identity, User, AccessRights, UserEnabled | Where-Object { ($_.user -like '#') -and $_.UserEnabled -eq $false } | Export-Csv C:\Temp\sharedfolders.csv -NoTypeInformation
Note it does require the Active Directory module to use the Get-ADuser command.
I want to use the "URI style launching" for UWP apps (for example: MS ToDo) from a win32 Application, command line or UWP apps.
UWP has a specific shell URI-Schemas available to launch them.
For example you can press win+R and enter ms-todo: and MS ToDo will start.
Respectively open cmd and enter start ms-todo: and MS ToDo will start.
How can i get a list with all local available URIs?
tl;dr
They are listed in Settings > Apps > Default Apps > Choose default apps by protocol
Getting details for all apps with Powershell
$result = [System.Collections.ArrayList]#();
foreach ($appx in Get-AppxPackage)
{
$location = $appx.InstallLocation
$manifest = "$location\AppxManifest.xml"
if($location -ne $null -and (Test-Path $manifest -PathType Leaf))
{
[xml]$xml = Get-Content $manifest
$ns = new-object Xml.XmlNamespaceManager $xml.NameTable
$ns.AddNamespace("main", "http://schemas.microsoft.com/appx/manifest/foundation/windows10")
$ns.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10")
$ns.AddNamespace("uap3", "http://schemas.microsoft.com/appx/manifest/uap/windows10/3")
$ns.AddNamespace("uap5", "http://schemas.microsoft.com/appx/manifest/uap/windows10/5")
$uriElements = $xml.SelectNodes("//uap:Extension[#Category = 'windows.protocol']/uap:Protocol/#Name", $ns)
$uris = $uriElements | select -ExpandProperty '#text'
$appIds = $xml.SelectNodes("//main:Application/#Id", $ns) | select -ExpandProperty '#text'
if ($appIds.Count -eq 0) {
continue;
}
$aliases = $xml.SelectNodes("//main:Extensions/uap5:Extension[#Category = 'windows.appExecutionAlias']/uap5:AppExecutionAlias/uap5:ExecutionAlias/#Alias", $ns) | select -ExpandProperty '#text'
#$result[$appx.Name] = $uris
$tmp = New-Object -TypeName PSObject
$tmp | Add-Member –MemberType NoteProperty –Name Name –Value "$($appx.Name)"
$tmp | Add-Member –MemberType NoteProperty –Name URIs –Value ($uris -join "`n")
$tmp | Add-Member –MemberType NoteProperty –Name Aliases –Value ($aliases -join "`n")
$tmp | Add-Member –MemberType NoteProperty –Name Package –Value "$($appx.PackageFamilyName)"
$tmp | Add-Member –MemberType NoteProperty –Name AppIds –Value ($appIds -join "`n")
$tmp | Add-Member –MemberType NoteProperty –Name Folder –Value $appx.InstallLocation
$null = $result.Add($tmp)
}
}
$result | Sort-Object -Property Name | Out-GridView
Result
The manual way for a single App:
I'm only aware of a slighly cumbersome manual way have to do do for every single app.
In this case "Microsoft.Todos"
Execute
Get-AppxPackage -Name Microsoft.Todos
Read Property "InstallationLocation"
C:\Program Files\WindowsApps\Microsoft.Todos_2.27.32662.0_x64__8wekyb3d8bbwe
Open AppxManifest.xml
C:\Program Files\WindowsApps\Microsoft.Todos_2.27.32662.0_x64__8wekyb3d8bbwe\AppxManifest.xml
Look for protocol entries:
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="ms-to-do">
<uap:DisplayName>ms-resource:app_name_ms_todo</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="ms-todo">
<uap:DisplayName>ms-resource:app_name_ms_todo</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
As you can see "ms-to-do" and "ms-todo" are the associated URIs.
Basically, I am trying to write some code that will run off of a scheduled task any time the event ID 4725 is triggered. This specific event states that a particular user had their AD account disabled (Windows Server 2016).
What I need to do is take the username from this event ID and output it as a variable #UserName to be used in the restmethod URI.
# Variables
$params = #{"action"="move";"destination"="/Shared/IT/Archived User Data/"}
$json = $params|ConvertTo-Json
$eventRecordId = 4725
$eventChannel = "Security"
# Gets the latest "disabled user account" event log and outputs the disabled user's name to a variable called $UserName
Get-EventLog –Log Security -InstanceId 4725 -Newest 1 | $UserName = ?{Group-Object -Property "TargetUserName"}
Echo $UserName
# Calls the Egnyte API to move the disabled user's home folder to the archive folder
Invoke-RestMethod `
-Method Post `
-body $json `
-Uri 'https://xxxxxx.egnyte.com/pubapi/v1/fs/private/"$UserName"' `
-Headers #{Authorization = "Bearer xxxxxxxxxxxxxxxxxxxxxxx"
Contenttype = "application/json"}
Expected results: Take the username from the target-username field in the security event-log ID 4725, output it to variable "#UserName", and then input it into the rest-method API.
Actual results: The variable is not being created.
OK Lets take a look at this Event 4725 template
(Get-WinEvent -ListProvider * -ErrorAction Ignore).Events |
Where-Object {$_.Id -eq 4725} |
select * |
Format-List
We can see there is a TargetUserName
<data name="TargetUserName" inType="win:UnicodeString" outType="xs:string"/>
So lets parse that Message into XML,
Get-EventLog returns a type System.Diagnostics.EventLogEntry
So first we need to change that object into a System.Diagnostics.Eventing.Reader.EventLogRecord so we can convert to XML
We can ether Make a new call using Get-WinEvent which will return the type System.Diagnostics.Eventing.Reader.EventLogRecord which has a method for turning data into XML
Or We can get the Index from the current record and call Get-WinEvent looking for the EventRecordID.
Below is a function i wrote that will parse the message field and make a new psobject with property ParsedMessage
function Parse-WindowsEvents(){
param(
[Parameter(Position=1, ValueFromPipeline)]
#[System.Diagnostics.Eventing.Reader.EventRecord[]]$Events
[object[]]$Events
)
process{
$ArrayList = New-Object System.Collections.ArrayList
$Events | %{
$EventObj = $_
$EventObjFullName = $_.GetType().FullName
if($EventObjFullName -like "System.Diagnostics.EventLogEntry"){
$EventObj = Get-WinEvent -LogName security -FilterXPath "*[System[EventRecordID=$($_.get_Index())]]"
}elseif($EventObjFullName -like "System.Diagnostics.Eventing.Reader.EventLogRecord"){
}else{
throw "Not An Event System.Diagnostics.Eventing.Reader.EventLogRecord or System.Diagnostics.EventLogEntry"
}
$PsObject = New-Object psobject
$EventObj.psobject.properties | %{
$PsObject | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_.Value
}
$XML = [xml]$EventObj.toXml()
$PsObject2 = New-Object psobject
$XML.Event.EventData.Data | %{
$PsObject2 | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_."#text"
}
$PsObject | Add-Member -MemberType NoteProperty -Name ParsedMessage -Value $PsObject2
$ArrayList.add($PsObject) | out-null
}
return $ArrayList
}
}
$Username = Get-EventLog –Log Security -InstanceId 4725 -Newest 1 | Parse-WindowsEvents | select -ExpandProperty ParsedMessage | select TargetUserName
$Username.TargetUserName
#or
$Username = (Get-EventLog –Log Security -InstanceId 4725 -Newest 1 | Parse-WindowsEvents | select -ExpandProperty ParsedMessage).TargetUserName
$Username
Can we defer a variable initialization untill it is needed ?
What I would like to do is predefine some variables in my profile that will contain a list of AD computer:
let's say I want:
$OU1_workstation to be fill with computers found in OU=workstations,OU=OU1,dc=domain,dc=com
$OU2_workstation fill with computers found in
OU=workstations,OU=OU2,dc=domain,dc=com and so on...
I use the following script to do it but it takes 30sec to compute, so currently I can't put that in my profile...
Get-ADOrganizationalUnit -SearchScope onelevel -Filter "*" -Properties "name","distinguishedname" |%{
set-Variable -Name "$($_.name)_workstation" -value (Get-ADComputer -Searchbase "OU=workstations,$($_.Distinguishedname)" -Filter * )
}
What options are available in powershell ?
Finally, based on #Richard's reply of a previous question of mine, I've chosen the following path to achieve some sort of lazy loading : using a scriptproperty of a PSCustomObject.
So I can put this in my profile
#requires -module activedirectory
$server=New-Object PSCustomObject
Get-ADOrganizationalUnit -SearchScope onelevel -Filter "*" -Properties "name","distinguishedname" |
?{
$_.name -notmatch 'Administrateurs|Administration|Comptes de deploiement|Contacts|Domain Controllers|Groupes|Serveurs|Services'
} |
%{
$OU=$_.name
$s=[scriptblock]::Create("Get-ADComputer -SearchBase ""OU=servers,OU=$OU,DC=domain,DC=com"" -Filter 'name -notlike "" *old""' |select -expand name")
$server| Add-Member -MemberType ScriptProperty -name $OU -value $s -Force
}
then when needed I can call $server.OU1 to get all server under this OU, $server.OU2 etc...