{"id":9049,"date":"2022-06-02T14:17:49","date_gmt":"2022-06-02T18:17:49","guid":{"rendered":"https:\/\/jdhitsolutions.com\/blog\/?p=9049"},"modified":"2022-06-02T14:17:54","modified_gmt":"2022-06-02T18:17:54","slug":"better-powershell-properties","status":"publish","type":"post","link":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/","title":{"rendered":"Better PowerShell Properties"},"content":{"rendered":"\n<p>I was chatting with my friend Gladys Kravitz recently about some PowerShell scripting she was doing with Active Directory and the DirectorySearcher object. For a number of reasons, the Active Directory module from Remote Server Administration Tools (RSAT) is not an option. But that's fine. Gladys is an experience AD admin and PowerShell scripter. Still, she had a question about the output from her commands, which led to some interesting work that you might find valuable. Even though I'm going to be discussing a specific object type, there are several principles that you can apply to your PowerShell scripting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Search Result Properties<\/h2>\n\n\n\n<p>Let's begin with some PowerShell code to find a user account using the DirectorySearcher object.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">$searcher = New-Object System.DirectoryServices.DirectorySearcher\n$searcher.filter =\"(&amp;(objectcategory=person)(objectclass=user))\"\n$searcher.FindOne().Properties<\/code><\/pre>\n\n\n\n<p>The DirectorySearcher has methods of FindOne() and FindAll(). The resulting object has a Properties property.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png\"><img loading=\"lazy\" decoding=\"async\" width=\"704\" height=\"622\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png\" alt=\"Active Directory search result properties\" class=\"wp-image-9050\" srcset=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png 704w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties-300x265.png 300w\" sizes=\"auto, (max-width: 704px) 100vw, 704px\" \/><\/a><\/figure>\n\n\n\n<p>You can use this in any number of ways.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">New-Object -typename PSObject -property $searcher.FindOne().Properties | Select Name,Description,WhenChanged<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/new-object-from-results.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"114\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/new-object-from-results-1024x114.png\" alt=\"new object from result properties\" class=\"wp-image-9051\" srcset=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/new-object-from-results-1024x114.png 1024w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/new-object-from-results-300x33.png 300w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/new-object-from-results-768x86.png 768w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/new-object-from-results-850x95.png 850w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/new-object-from-results.png 1148w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Leaving the formatting of the values aside, notice the property names. Functional, but not professional-looking. This is always an option.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">New-Object -typename PSObject -property $searcher.FindOne().Properties | \nSelect-Object @{Name=\"Name\";Expression = {$_.name}},\n@{Name=\"Description\";Expression = {$_.Description}},\n@{Name=\"WhenChanged\";Expression = {$_.WhenChanged}}<\/code><\/pre>\n\n\n\n<p>Or you can define a custom object with a type name and use a custom format file. But that's a lot of work and not flexible. <\/p>\n\n\n\n<p>Here's another approach to creating an object but with properly cased property names.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">$r = $searcher.FindOne()\n$r.Properties.GetEnumerator() | \nWhere-Object { $_.key -match \"Name|Description|WhenChanged|memberof|logoncount\" } | \nForEach-Object -Begin {\n    #initialize an empty hashtable\n    $h = @{}\n} -Process {\n    #convert to title case\n    $n = [cultureinfo]::CurrentCulture.TextInfo.ToTitleCase($_.name.tolower())\n    if ($_.value.count -gt 1) {\n        #get the array of values\n        $v = $_.value\n    }\n    else {\n        #get the single value\n        $v = $_.value[0]\n    }\n    #add the property name and value to the hashtable\n    $h.Add($n, $v)\n} -End { \n    #create a custom object from the hashtable\n    [pscustomobject]$h \n}<\/code><\/pre>\n\n\n\n<p>In this particular situation, the object I am formatting is a DirectoryServices.ResultCollection, which is, in essence, a hashtable, so it requires a different approach. The GetEnumerator() method writes each element in the collection as an object with Key and Value properties. My example is filtering keys for specific \"properties.\"<\/p>\n\n\n\n<p>The filtered objects are then piped to ForEach-Object. Before any objects are processed in the pipeline, I initialize an empty hashtable. Next, for each filtered result, I convert the name, which is an alias for 'key,' to title case. For this to work, I always make the string all lower case before invoking the method. This reformatted name and the corresponding value are added to the hashtable. After everything has been processed, I turn the hashtable into a custom object.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/title-case-properties.png\"><img loading=\"lazy\" decoding=\"async\" width=\"869\" height=\"178\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/title-case-properties.png\" alt=\"custom search result with proper title case\" class=\"wp-image-9052\" srcset=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/title-case-properties.png 869w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/title-case-properties-300x61.png 300w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/title-case-properties-768x157.png 768w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/title-case-properties-850x174.png 850w\" sizes=\"auto, (max-width: 869px) 100vw, 869px\" \/><\/a><\/figure>\n\n\n\n<p>This is promising but needs to be more flexible. The ForEach-Object structure is the same format as an advanced PowerShell function that takes pipeline input. In other words, I have a prototype.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating Configuration Data<\/h2>\n\n\n\n<p>My idea is to take a DirectorySearcher result and create a custom object replacing the property name with a properly formatted name and the value. But in looking at the output, I want more than title case. 'Logoncount' might look nicer formatted as 'LogonCount.' I prefer SamAccountName. I want to replace the incoming property name with a properly formatted name. I will need a list of all possible property names formatted the way I want. Because I know I will eventually want to use this list as a hashtable, I'll create a configuration data file that I can use later with Import-PowerShellDataFile.<\/p>\n\n\n\n<p>Here's the PowerShell script to generate different configuration data files depending on the object type.<\/p>\n\n\n\n<pre title=\"Create-ADPropertyFile.ps1\" class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">#requires -version 5.1\n\n&lt;# Create-ADPropertyFile.ps1\n proof-of-concept code to create an Active Directory property configuration data file\n .\\Create-ADPropertyFile.ps1 -Filter \"(&amp;(objectclass=organizationalunit)(Name=Employees))\" -FilePath .\\adou.psd1\n .\\Create-ADPropertyFile.ps1 Group -FilePath .\\adgroup.psd1\n#>\n\n[cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = \"byType\")]\nParam(\n    [Parameter(Position = 0, ParameterSetName = \"byType\")]\n    [ValidateSet(\"User\", \"Group\", \"Computer\", \"OU\")]\n    [string]$ObjectType = \"User\",\n\n    [Parameter(HelpMessage = \"Specify an LDAP search filter to a template object.\", ParameterSetName = \"byFilter\")]\n    [ValidateNotNullOrEmpty()]\n    [string]$Filter,\n\n    [Parameter(Mandatory, HelpMessage = \"Specify the filename and path to the psd1 file.\")]\n    [ValidateScript({ Test-Path (Split-Path $_) })]\n    [string]$FilePath\n)\n\nif ($pscmdlet.ParameterSetName -eq \"byType\") {\n\n    Switch ($ObjectType) {\n        \"User\" { $filter = \"(&amp;(objectcategory=person)(objectclass=user))\" }\n        \"Group\" { $filter = \"(Objectclass=group)\" }\n        \"Computer\" { $filter = \"(objectclass=computer)\" }\n        \"OU\" { $filter = \"(objectclass=organizationalunit)\" }\n    }\n}\n\n$searcher = New-Object System.DirectoryServices.DirectorySearcher\n$searcher.filter = $filter\n#don't need values from the search\n$searcher.PropertyNamesOnly = $true\n$searcher.FindOne().Properties.GetEnumerator() | Sort-Object -Property Key |\nForEach-Object -Begin {\n    #initialize a list\n    $list = [System.Collections.Generic.List[string]]::new()\n    $list.add(\"@{\")\n} -Process {\n    $value = [cultureinfo]::CurrentCulture.TextInfo.ToTitleCase($($_.key.ToLower()))\n    #add each property to the list\n    $List.Add(\"$($_.key) = '$value'\")\n} -End {\n    #close the psd1\n    $list.Add(\"}\")\n}\n#save the list to a file\n$list | Out-File -FilePath $FilePath\n\n#edit psd1 file with your desired formatting<\/code><\/pre>\n\n\n\n<p>The default filters will find the first matching result. However, the DirectorySearcher <strong>will only return property names that have been defined.<\/strong> You might want to create a template object with all the properties defined that you intend to use and then build the property file using a custom LDAP filter to that object.<\/p>\n\n\n\n<p>I'll use the script to create a psd1 file for user objects.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">c:\\scripts\\Create-ADPropertyFile.ps1 -ObjectType User -FilePath c:\\scripts\\aduser.psd1<\/code><\/pre>\n\n\n\n<p>I can edit the file and adjust the property names.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">@{\n    accountexpires         = 'AccountExpires'\n    admincount             = 'AdminCount'\n    adspath                = 'AdsPath'\n    badpasswordtime        = 'BadPasswordTime'\n    badpwdcount            = 'BadPwdCount'\n    cn                     = 'CN'\n    codepage               = 'CodePage'\n    countrycode            = 'CountryCode'\n    department             = \"Department\"\n    description            = 'Description'\n    displayname            = \"DisplayName\"\n    distinguishedname      = 'DistinguishedName'\n    dscorepropagationdata  = 'DscorePropagationData'\n    givenname              = \"GivenName\"\n    instancetype           = 'InstanceType'\n    iscriticalsystemobject = 'IsCriticalSystemObject'\n    lastlogoff             = 'LastLogoff'\n    lastlogon              = 'Lastlogon'\n    lastlogontimestamp     = 'LastLogonTimestamp'\n    logoncount             = 'LogonCount'\n    logonhours             = 'LogonHours'\n    memberof               = 'MemberOf'\n    name                   = 'Name'\n    objectcategory         = 'ObjectCategory'\n    objectclass            = 'ObjectClass'\n    objectguid             = 'ObjectGuid'\n    objectsid              = 'ObjectSid'\n    primarygroupid         = 'PrimaryGroupId'\n    pwdlastset             = 'PwdlastSet'\n    samaccountname         = 'SamAccountName'\n    samaccounttype         = 'SamAccountType'\n    sn                     = \"Surname\"\n    title                  = \"Title\"\n    useraccountcontrol     = 'UserAccountcontrol'\n    usnchanged             = 'UsnChanged'\n    usncreated             = 'UsnCreated'\n    whenchanged            = 'WhenChanged'\n    whencreated            = 'WhenCreated'\n}\n\n}<\/code><\/pre>\n\n\n\n<p>If you know additional LDAP property names, you could manually add them. The last step is to use this file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Optimize AD Search Result<\/h2>\n\n\n\n<p>We tend to avoid monolithic commands in PowerShell. Instead, we want to leverage the pipeline. I have code using the DirectorySearcher to find objects.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/search-results.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"205\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/search-results-1024x205.png\" alt=\"directory searcher results\" class=\"wp-image-9053\" srcset=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/search-results-1024x205.png 1024w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/search-results-300x60.png 300w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/search-results-768x154.png 768w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/search-results-850x170.png 850w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/search-results.png 1040w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>I want to take these results and optimize the property names. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">Function Optimize-ADSearchResult {\n    [cmdletbinding()]\n    Param(\n        [Parameter(\n            Position = 0,\n            Mandatory,\n            ValueFromPipeline,\n            HelpMessage = \"This should be the input from an ADSearcher FindOne() or FindAll() method.\"\n        )]\n        [System.DirectoryServices.SearchResult]$InputObject,\n\n        [Parameter(\n            Mandatory,\n            HelpMessage = \"Enter the path to the psd1 file with your property names. See Create-ADPropertyFile.\"\n        )]\n        [ValidateNotNullOrEmpty()]\n        [ValidateScript({ Test-Path $_ })]\n        [ValidatePattern('\\.psd1$')]\n        [string]$ConfigurationData,\n\n        [Parameter(HelpMessage = \"Specify a custom type name, like CorpADUser. You might add this if using a custom format file or type extensions.\")]\n        [ValidateNotNullOrEmpty()]\n        [string]$TypeName\n    )\n    Begin {\n        Write-Verbose \"[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)\"\n        #import the configuration data\n        Write-Verbose \"[$((Get-Date).TimeofDay) BEGIN  ] Importing configuration data from $(Convert-Path $ConfigurationData)\"\n        $PropertyData = Import-PowerShellDataFile -Path $ConfigurationData\n    } #begin\n    Process {\n        Write-Verbose \"[$((Get-Date).TimeofDay) PROCESS] Processing input\"\n\n        $InputObject.properties.GetEnumerator() |\n        ForEach-Object -Begin {\n            $new = [ordered]@{ }\n            if ($TypeName) {\n                Write-Verbose \"[$((Get-Date).TimeofDay) PROCESS] Using typename $TypeName\"\n                $new.Add(\"PSTypename\", $TypeName)\n            }\n        } -Process {\n            Write-Verbose \"[$((Get-Date).TimeofDay) PROCESS] Processing property $($_.key)\"\n            #Get formatted property name from configuration data\n            if ($PropertyData.Contains($_.key)) {\n                $name = $PropertyData[\"$($_.key)\"]\n            }\n            else {\n                $name = $_.key\n            }\n            if ($_.value.count -gt 1) {\n                $value = $_.value\n            }\n            else {\n                $value = $_.value[0]\n            }\n            $new.Add($name, $value)\n        } -End {\n            Write-Verbose \"[$((Get-Date).TimeofDay) PROCESS] Creating output\"\n            New-Object -TypeName psobject -Property $new\n        }\n    } #process\n    End {\n        Write-Verbose \"[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)\"\n    } #end\n}<\/code><\/pre>\n\n\n\n<p>The function looks at each search result and tries to match the property name from the configuration data. If it finds a match, it will be used. Otherwise, PowerShell will use the original property name.<\/p>\n\n\n\n<p>I could have stored all of the property name hashtables in the function, but that would have added complexity and unnecessary length. If I need to add a property, I have to edit the function. It is much better to separate data from the code that implements it. My function is a good example of using configuration data.<\/p>\n\n\n\n<p>I can take the search results, optimize them with this function, and then select what I need.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\"> $searcher = New-Object System.DirectoryServices.DirectorySearcher\n $searcher.filter = \"(&amp;(objectcategory=person)(objectclass=user)(department=sales))\"\n $searcher.FindAll() | \n Optimize-ADSearchResult -ConfigurationData C:\\scripts\\aduser.psd1 |\n Select-Object -property DistinguishedName,Givenname,Surname,Description,Title<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/optimzed-results.png\"><img loading=\"lazy\" decoding=\"async\" width=\"796\" height=\"526\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/optimzed-results.png\" alt=\"property optimized search results\" class=\"wp-image-9054\" srcset=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/optimzed-results.png 796w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/optimzed-results-300x198.png 300w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/optimzed-results-768x507.png 768w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/optimzed-results-350x230.png 350w\" sizes=\"auto, (max-width: 796px) 100vw, 796px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>There is a lot going on in this post\u2014many moving parts and possibly some new commands. I hope you'll review the code and at least walk through it in your head. Please feel free to leave comments and questions.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was chatting with my friend Gladys Kravitz recently about some PowerShell scripting she was doing with Active Directory and the DirectorySearcher object. For a number of reasons, the Active Directory module from Remote Server Administration Tools (RSAT) is not an option. But that&#8217;s fine. Gladys is an experience AD admin and PowerShell scripter. Still,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"New on the blog: Building Better #PowerShell Properties","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[7,4,8],"tags":[671,667,144,534,540],"class_list":["post-9049","post","type-post","status-publish","format-standard","hentry","category-active-directory","category-powershell","category-scripting","tag-configurationdata","tag-directorysearcher","tag-objects","tag-powershell","tag-scripting"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Better PowerShell Properties &#8226; The Lonely Administrator<\/title>\n<meta name=\"description\" content=\"PowerShell doesn&#039;t always give you what you want. But with a little scripting and patience, you can get what you need.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Better PowerShell Properties &#8226; The Lonely Administrator\" \/>\n<meta property=\"og:description\" content=\"PowerShell doesn&#039;t always give you what you want. But with a little scripting and patience, you can get what you need.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/\" \/>\n<meta property=\"og:site_name\" content=\"The Lonely Administrator\" \/>\n<meta property=\"article:published_time\" content=\"2022-06-02T18:17:49+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-06-02T18:17:54+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png\" \/>\n<meta name=\"author\" content=\"Jeffery Hicks\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@JeffHicks\" \/>\n<meta name=\"twitter:site\" content=\"@JeffHicks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeffery Hicks\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/\"},\"author\":{\"name\":\"Jeffery Hicks\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"headline\":\"Better PowerShell Properties\",\"datePublished\":\"2022-06-02T18:17:49+00:00\",\"dateModified\":\"2022-06-02T18:17:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/\"},\"wordCount\":779,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/result-properties.png\",\"keywords\":[\"ConfigurationData\",\"DirectorySearcher\",\"objects\",\"PowerShell\",\"Scripting\"],\"articleSection\":[\"Active Directory\",\"PowerShell\",\"Scripting\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/\",\"name\":\"Better PowerShell Properties &#8226; The Lonely Administrator\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/result-properties.png\",\"datePublished\":\"2022-06-02T18:17:49+00:00\",\"dateModified\":\"2022-06-02T18:17:54+00:00\",\"description\":\"PowerShell doesn't always give you what you want. But with a little scripting and patience, you can get what you need.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/#primaryimage\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/result-properties.png\",\"contentUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/result-properties.png\",\"width\":704,\"height\":622},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/active-directory\\\/9049\\\/better-powershell-properties\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Active Directory\",\"item\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/category\\\/active-directory\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Better PowerShell Properties\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/\",\"name\":\"The Lonely Administrator\",\"description\":\"Practical Advice for the Automating IT Pro\",\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\",\"name\":\"Jeffery Hicks\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"caption\":\"Jeffery Hicks\"},\"logo\":{\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Better PowerShell Properties &#8226; The Lonely Administrator","description":"PowerShell doesn't always give you what you want. But with a little scripting and patience, you can get what you need.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/","og_locale":"en_US","og_type":"article","og_title":"Better PowerShell Properties &#8226; The Lonely Administrator","og_description":"PowerShell doesn't always give you what you want. But with a little scripting and patience, you can get what you need.","og_url":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/","og_site_name":"The Lonely Administrator","article_published_time":"2022-06-02T18:17:49+00:00","article_modified_time":"2022-06-02T18:17:54+00:00","og_image":[{"url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png","type":"","width":"","height":""}],"author":"Jeffery Hicks","twitter_card":"summary_large_image","twitter_creator":"@JeffHicks","twitter_site":"@JeffHicks","twitter_misc":{"Written by":"Jeffery Hicks","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/#article","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/"},"author":{"name":"Jeffery Hicks","@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"headline":"Better PowerShell Properties","datePublished":"2022-06-02T18:17:49+00:00","dateModified":"2022-06-02T18:17:54+00:00","mainEntityOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/"},"wordCount":779,"commentCount":1,"publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png","keywords":["ConfigurationData","DirectorySearcher","objects","PowerShell","Scripting"],"articleSection":["Active Directory","PowerShell","Scripting"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/","url":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/","name":"Better PowerShell Properties &#8226; The Lonely Administrator","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/#primaryimage"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png","datePublished":"2022-06-02T18:17:49+00:00","dateModified":"2022-06-02T18:17:54+00:00","description":"PowerShell doesn't always give you what you want. But with a little scripting and patience, you can get what you need.","breadcrumb":{"@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/#primaryimage","url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png","contentUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/06\/result-properties.png","width":704,"height":622},{"@type":"BreadcrumbList","@id":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/9049\/better-powershell-properties\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Active Directory","item":"https:\/\/jdhitsolutions.com\/blog\/category\/active-directory\/"},{"@type":"ListItem","position":2,"name":"Better PowerShell Properties"}]},{"@type":"WebSite","@id":"https:\/\/jdhitsolutions.com\/blog\/#website","url":"https:\/\/jdhitsolutions.com\/blog\/","name":"The Lonely Administrator","description":"Practical Advice for the Automating IT Pro","publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/jdhitsolutions.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9","name":"Jeffery Hicks","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","url":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","caption":"Jeffery Hicks"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg"}}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":8916,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/8916\/managing-raw-directorysearcher-data\/","url_meta":{"origin":9049,"position":0},"title":"Managing Raw DirectorySearcher Data","author":"Jeffery Hicks","date":"February 24, 2022","format":false,"excerpt":"Recently, I was chatting with my friend Gladys. As part of her work, she often creates Active Directory PowerShell-related tools for her colleagues. It is always good to be the toolmaker! Anyway, she doesn't want to rely on the Active Directory module from Remote Server Administration Tools (RSAT). There's no\u2026","rel":"","context":"In &quot;Active Directory&quot;","block_context":{"text":"Active Directory","link":"https:\/\/jdhitsolutions.com\/blog\/category\/active-directory\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/02\/resultpropertiescollection.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/02\/resultpropertiescollection.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/02\/resultpropertiescollection.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/02\/resultpropertiescollection.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":7700,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7700\/active-directory-objects-and-the-powershell-pipeline\/","url_meta":{"origin":9049,"position":1},"title":"Active Directory Objects and the PowerShell Pipeline","author":"Jeffery Hicks","date":"September 28, 2020","format":false,"excerpt":"This article is something I've been meaning to write for sometime. As often as I tell people PowerShell is easy to use once you understand its core concepts, that isn't always the case.\u00a0 This is a problem my friend Gladys Kravitz brought to my attention some time ago. Like her,\u2026","rel":"","context":"In &quot;Active Directory&quot;","block_context":{"text":"Active Directory","link":"https:\/\/jdhitsolutions.com\/blog\/category\/active-directory\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/Get-bits-revised-ad.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/Get-bits-revised-ad.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/Get-bits-revised-ad.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/Get-bits-revised-ad.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/Get-bits-revised-ad.jpg?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":37,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/37\/get-active-directory-user-information-in-powershell\/","url_meta":{"origin":9049,"position":2},"title":"Get Active Directory User Information in PowerShell","author":"Jeffery Hicks","date":"July 5, 2006","format":false,"excerpt":"One feature that PowerShell will likely be missing when it first ships is solid support for ADSI and working with Active Directory. You can use .NET DirectoryEntry objects but it feels more like programming and less like scripting. Another option for working with Active Directory in PowerShell is to use\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2330,"url":"https:\/\/jdhitsolutions.com\/blog\/scripting\/2330\/friday-fun-get-latest-powershell-scripts\/","url_meta":{"origin":9049,"position":3},"title":"Friday Fun: Get Latest PowerShell Scripts","author":"Jeffery Hicks","date":"May 18, 2012","format":false,"excerpt":"Probably like many of you I keep almost all of my scripts in a single location. I'm also usually working on multiple items at the same time. Some times I have difficult remembering the name of a script I might have been working on a few days ago that I\u2026","rel":"","context":"In &quot;Friday Fun&quot;","block_context":{"text":"Friday Fun","link":"https:\/\/jdhitsolutions.com\/blog\/category\/friday-fun\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2012\/05\/get-latestscript-300x133.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":8400,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/8400\/friday-fun-custom-grouping-with-powershell\/","url_meta":{"origin":9049,"position":4},"title":"Friday Fun &#8211; Custom Grouping with PowerShell","author":"Jeffery Hicks","date":"May 14, 2021","format":false,"excerpt":"The other day I was answering a question in the PowerShell Facebook group. This person was getting data from Active Directory and trying to organize the results in a way that met his business requirements. My suggestion was to use Group-Object and a custom grouping property. I am assuming you\u2026","rel":"","context":"In &quot;Active Directory&quot;","block_context":{"text":"Active Directory","link":"https:\/\/jdhitsolutions.com\/blog\/category\/active-directory\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/05\/custom-grouping.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/05\/custom-grouping.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/05\/custom-grouping.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/05\/custom-grouping.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":112,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/112\/powershell-mini-conference\/","url_meta":{"origin":9049,"position":5},"title":"PowerShell Mini-Conference","author":"Jeffery Hicks","date":"June 11, 2007","format":false,"excerpt":"If you've got some free time in early November and want to get up to speed on PowerShell in the least amount of time, then come to Las Vegas. Don Jones (PoweShell MVP) and I will be running a 2 day PowerShell mini-conference. This will be an intense 2 days\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/9049","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=9049"}],"version-history":[{"count":0,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/9049\/revisions"}],"wp:attachment":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=9049"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=9049"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=9049"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}