PowerShell Set-ItemProperty vs. dot to set a property - powershell

I'm trying to set a property of for example an ApplicationPool with PowerShell (version 2).
(I've a Windows 7 64 bit machine if that matters)
I see that this example uses Set-ItemProperty and this example uses a dot . to set a property of an object / element:
$pool | Set-ItemProperty -Name "managedRuntimeVersion" -Value "v4.0"
versus:
$pool.managedRuntimeVersion = "v4.0"
So what's the difference? I think that the second one is much more readable, but I don't know what the implications are.
EDIT:
I noticed that (at least in this case) there is a difference, the Set-ItemProperty does save the value of the property directly, while the other method does set the value (while debugging) but does not save it afterwards. I've not found out yet why this happens. (Do you need to call something like save or commit?) See #moonstom's answer, for Powershell 2.0 Set-ItemProperty is the only way or $pool | Set-Item for Powershell 3+ (see sample).

You're working on a representation of that app pool. If you check the type of that object, you'll get a configuration element. So after setting it up, you need to push your settings back with $pool | Set-Item, available in PS 3.0 and above. Otherwise your only alternative is Set-ItemProperty

There is no difference. In the first one you pass the object to the Set-ItemProperty commandlet via the pipe and the commandlet setting the object property.
The second one you're setting it directly on the object. But they are functionally the same. With the second one you could also retrieve the property's value like this:
$value = $pool.managedRuntimeVersion

Related

How to Set Machine Environment Variable longer than 4095 characters in Powershell?

I have a very long string I am trying to store in a Machine level Environment Variable.
Set-Item -Path Env:\... seems to work, but I dont see a way to set a machine level environment variable with this, only local variables.
[Environment]::SetEnvironmentVariable(..., ..., "Machine")
Has a 4095 character limit and truncates.
$Enviro = ([Microsoft.Win32.Registry]::LocalMachine).OpenSubKey("SYSTEM\CurrentControlSet\Control\Session Manager\Environment", $True)
$Enviro.SetValue(..., ..., [Microsoft.Win32.RegistryValueKind]::ExpandString);
$Enviro.Close()
Also seems to have a similar limit and truncates the string.
$Enviro = "registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\"
Set-ItemProperty -Path $Enviro -Name ... -Value ...
Also, once again, same issue. Truncates to 4095.
What other options do I have?
To people that keep linking the "Environment variables not working properly" article, that doesnt answer this question. That article covers reading from the environment/registery, not writing to it. This is the exact opposite use case. Please go over my code above and you will notice I tried Set-ItemProperty and it does not work.

Powershell: Cannot read a specific Registry-Value

I am struggling to read this REG-value via Powershell 5:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/uri:urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/01]
"Driver"="{6bdd1fc6-810f-11d0-bec7-08002be2092f}\\0000"
Even the autocomplete-function in Powershell showing me the REG-path to that key is not working properly.
Why is it failing? How can I get this value?
This is the code which is surprisingly NOT working as expected:
$sub = 'urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/uri:urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/01'
get-Item -literalPath "HKLM:\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\$sub"
Here a screenshot of the subkey that I cannot read:
I could now drill it down to this situation:
subkey 'urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42' -> OK
subkey 'uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/u' -> OK
subkey 'urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/u' -> fail!
subkey 'urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/u' under HKLM:\Software -> OK
Using Sysinternals Process Explorer, I've discovered what happens.
PowerShell replaces the forward slashes in the path unconditionally with backslashes, even when you use -LiteralPath.
That's clearly a bug.
To work around it, you can use the PSPath of the registry key, apparently PowerShell leaves those alone. For the local registry, the PSPath always starts like this:
Microsoft.PowerShell.Core\Registry::
and after that goes on with the regular key name as it would appear in RegEdit.
$path = "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\urn:uuid:e3248000-80ce-11db-8000-30055c83410f/uri:e3248000-80ce-11db-8000-30055c83410f/PrinterService"
Get-Item $path
PSPaths are an integral part of anything that Powershell treats as one of its drives. You can select them, or access the .PSPath property:
$path = "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider"
Get-ChildItem $path | Select -ExpandProperty PSPath
(Get-Item C:\).PSPath
At the end it turns out, that I had to use a different Syntax for the REG-Path to make the call work - very strange!
See this code:
$prefix1 = "Registry::HKEY_LOCAL_MACHINE"
$prefix2 = "HKLM:"
$subDir = "urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/uri:urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/01"
get-item "$prefix1\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\$subDir"
get-item "$prefix2\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\$subDir"
The first "get-item" call using prefix1 is working fine while the second one is not returning anything back.
Lession learned: Better use the longer REG-Prefix like in the original PSPATH to avoid any unexpected side-effects.
From Microsoft's PowerShell documentation, you can decide if you would like to view the entries as a list or to retrieve a single registry key.
https://learn.microsoft.com/en-us/powershell/scripting/samples/working-with-registry-entries?view=powershell-7.1

getfolderpath & Program Data

I have been successfully using [environment]::getfolderpath("ProgramFiles") to get the path to program Files, but now I have a need to also access Program Data, and it looks from this enumeration like ProgramData is not available with this method. Is that true, or am I missing something here?
$env: to access environmental variables
$env:ProgramData
The quickest way is to use $env:ProgramData as BenH already pointed out in your question comments.
Using the .net Specialfolder, you would have needed to use the CommonApplicationData
Instead of using a string though such as your initial example:
[Environment]::GetFolderPath('CommonApplicationData')
I'd suggest using the enumeration as you will get the possible enumeration values directly into the intellisense while developping.
[Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonApplicationData)
Finally, because you knew the path you were looking for but not the corresponding variable, you could have listed them all neatly using something like:
$SpecialFolders = New-Object -TypeName psobject
[Environment+SpecialFolder]::GetNames([Environment+SpecialFolder]) | sort |
foreach {Add-Member -InputObject $SpecialFolders -Type NoteProperty -Name
($_) -Value ([Environment]::GetFolderPath($_)) }
$SpecialFolders | fl
Using that snippet, you could have determined that c:\programdata was a special folder path belonging to CommonApplicationData.
The enumeration can still be handy if a specified folder is not in the $env scope (example: My documents special folder).

Powershell 5.0 / ISE

I have an example code snippet that suggests using
(Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}).Count
to return the count of all processess using > 20Mb.
It works, but when typing, neither Intellisense or the "Tab" key shows this property, rather they show the properties of an individual process - which I find misleading.
I understand, that specifying an item property will give me the list of that property only, but is there a way to easily see, in general, what ALL the valid propeties are, including list aggregates etc?
Even assigning to a variable
$processes = Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}
does not show me "Count" as a valid property of $processes until AFTER the assignment has been actually run and the value assigned - when writing the script it still shows the properties for an individual item.
For me, Intellisense / Tab help that does not cover all the options kind of defeats the purpose ... (not having to remember hundreds objects/functions and their properties / parameters).
Is there any way to improve this situation? Some syntax trick have I missed?
The correct way to find out all of the properties of an object is to pipe the output to Get-Member:
Get-Process | Get-Member
Sometimes there are hidden properties and methods that can only be seen if you add the -force switch:
Get-Process | Get-Member -Force
The count property is an automatic property that is always usable on any collection object but that isn't explicitly listed as a property. Another example of an automatic property is length.
Using #() to force an array type is handy when that is what is wanted.
e.g. $processes = #(Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}). will show you "Count" and the other array properties.
Other than that, let's say the Intellisense has various limitations / shortcomings that I will just have to learn... sigh.

How to check if the output of a function is redirected?

On *nix systems I can easily detect if the program is run on tty or the output is plain stream (pipe or file) and thus adjust the output accordingly (e.g. pretty print or serialize). How to do the same in PowerShell?
I'd like my function to either output the result as object for manual processing (when executed in script or piped to other commands) or use something like Format-Table -AutoSize when invoked directly from PS prompt.
What's the proper way to achieve that in PS?
I'd recommend leaving the decision of whether or not to use formatting cmdlets to the user. If anything I'd add a default display property set to the output objects, so that PowerShell displays only a select subset of properties by default (full property set can still be displayed by calling the format cmdlet with -Property *).
$properties = 'a', 'b', ... # list of property names
$object = ... # your object
$defaultPropertySet = New-Object Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$properties)
$standardMembers = [Management.Automation.PSMemberInfo[]]#($defaultPropertySet)
$object | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $standardMembers
Note that this requires PowerShell v3 or newer.