Get all the names of the applications in a site - powershell

I am trying to write a powershell script which gets the names of each application in a website in IIS in order to change the app pool associated with it. I am able to get the website, but I don't see a clear way to fetch each of the names?
Eg. I want to loop through them all: Api, Services, etc.. and then use
Set-ItemProperty "IIS:\Sites\RootSite\$loopedValue" -Name 'applicationPool' -Value $NewPool
I'm trying this:
Get-WebApplication -Site 'ABC'
Name Application pool Protocols Physical Path
---- ---------------- --------- -------------
Api ABC http C:\Api
Services ABC http C:\Services
Director ABC http C:\Director
ReportingServer ABC http C:\ReportingServer
But there is no way to get the Name from the members of Get-WebApplication.
Get-WebApplication | Get-Member
TypeName: Microsoft.IIs.PowerShell.Framework.ConfigurationElement#site#application
Name MemberType Definition
---- ---------- ----------
ClearLocalData Method void ClearLocalData()
Copy Method void Copy(Microsoft.IIs.PowerShell.Framework.ConfigurationElement target, bool recurse)
Delete Method void Delete()
Equals Method bool Equals(System.Object obj), bool IEquatable[ConfigurationElement].Equals(Microsoft.IIs.PowerShell.Framework.Conf...
GetAttribute Method Microsoft.IIs.PowerShell.Framework.ConfigurationAttribute GetAttribute(string attributeName)
GetAttributeValue Method System.Object GetAttributeValue(string attributeName)
GetChildElement Method Microsoft.IIs.PowerShell.Framework.ConfigurationElement GetChildElement(string elementName), Microsoft.IIs.PowerShel...
GetCollection Method Microsoft.IIs.PowerShell.Framework.ConfigurationElementCollection GetCollection(string collectionName), Microsoft.II...
GetHashCode Method int GetHashCode()
GetMetadata Method System.Object GetMetadata(string metadataType)
GetParentElement Method Microsoft.IIs.PowerShell.Framework.ConfigurationElement GetParentElement()
GetType Method type GetType()
LoadProperties Method void LoadProperties(System.Collections.Generic.Dictionary[string,System.Object] propCollection)
SetAttributeValue Method void SetAttributeValue(string attributeName, System.Object value)
SetMetadata Method void SetMetadata(string metadataType, System.Object value)
ToPSObject Method psobject ToPSObject(Microsoft.IIs.PowerShell.Framework.ConfigurationElement parent)
ToString Method string ToString()
Update Method void Update(Microsoft.IIs.PowerShell.Framework.ConfigurationElement source), bool Update(psobject data)
UpdateCollection Method bool UpdateCollection(psobject[] arr)
applicationPool NoteProperty string applicationPool=ABC
Collection NoteProperty psobject[] Collection=System.Management.Automation.PSObject[]
ConfigurationPathType NoteProperty ConfigurationPathNodeType ConfigurationPathType=Location
enabledProtocols NoteProperty string enabledProtocols=http
ItemXPath NoteProperty string ItemXPath=/system.applicationHost/sites/site[#name='ABC' and #id='1']/application[#path='/API']
Location NoteProperty string Location=
path NoteProperty string path=/ApiDoc
preloadEnabled NoteProperty bool preloadEnabled=False
PSPath NoteProperty string PSPath=MACHINE/WEBROOT/APPHOST
serviceAutoStartEnabled NoteProperty bool serviceAutoStartEnabled=False
serviceAutoStartProvider NoteProperty string serviceAutoStartProvider=
virtualDirectoryDefaults NoteProperty Microsoft.IIs.PowerShell.Framework.ConfigurationElement#application#virtualDirectoryDefaults virtualDirectoryDefault...
Item ParameterizedProperty System.Object Item(string attributeName) {get;set;}
Attributes Property Microsoft.IIs.PowerShell.Framework.ConfigurationAttributeCollection Attributes {get;}
ChildElements Property Microsoft.IIs.PowerShell.Framework.ConfigurationChildElementCollection ChildElements {get;}
ElementTagName Property string ElementTagName {get;}
Methods Property Microsoft.IIs.PowerShell.Framework.ConfigurationMethodCollection Methods {get;}
Schema Property Microsoft.IIs.PowerShell.Framework.ConfigurationElementSchema Schema {get;}
PhysicalPath ScriptProperty System.Object PhysicalPath {get=$pquery = $this.ItemXPath + "/virtualDirectory[#path='/']/#physicalPath"...
I don't really want to parse a string value from the path or PhysicalPath to do this. Is there another way?

An alternative way I came up with was to join the two results from Get-WebSite and Get-WebApplication
(get-website | select-object #{n='Site'; e={$_.Name}},#{n='Location'; e={$_.PhysicalPath}}) + (get-webapplication | select-object #{n='Site'; e= {$_.GetParentElement().Attributes['name'].value + $_.path }},#{n='Location'; e= {$_.PhysicalPath}})
Output:
Site WebType Location
---- ------- --------
MEDIA WebSite c:\mir\data
DATA WebSite d:\data
Tu***.M*.A***o.Sa****x.N***e WebSite C:\WebSite\Tu***.M*.A***o.Sa****x.N***e
Tu***.***********on.*****ime WebSite C:\WebSite\Tu***.***********on.*****ime
Tu***.**.*****.***********ion WebSite C:\WebSite\Tu***.**.*****.***********ion
Tu***.********.**.****App WebSite C:\WEB\Tu***.********.**.****App
Tu***.********.**.****App/api WebApplication C:\Web\Tu***.********.**.****vate

The following will provide the application names for all sites:
(Get-ChildItem -Path 'IIS:\Sites' -Recurse | Where-Object {$_.NodeType -eq 'application'}).Name
The following will return the application names for Default Web Site:
(Get-ChildItem -Path 'IIS:\Sites\Default Web Site' | Where-Object {$_.NodeType -eq 'application'}).Name

Related

Get-ItemProperty with wildcard; results as array

Given a registry key of
registry::HKEY_LOCAL_MACHINE\SOFTWARE\TestKey
and text properties called TestString1, TestString2 & NotValid
I can use
Get-ItemProperty -path:"registry::HKEY_LOCAL_MACHINE\SOFTWARE\TestKey" -name:"TestString*"
and I will get back
TestString1 : One
TestString2 : Two
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\TestKey
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE
PSChildName : TestKey
PSProvider : Microsoft.PowerShell.Core\Registry
But what I want is an array of just the two properties who's names match the wildcard.
Get-ItemProperty -path:"registry::HKEY_LOCAL_MACHINE\SOFTWARE\TestKey" -name:"TestString*" | Get-Member
produces
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
PSChildName NoteProperty string PSChildName=TestKey
PSParentPath NoteProperty string PSParentPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE
PSPath NoteProperty string PSPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\TestKey
PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\Registry
TestString1 NoteProperty string TestString1=One
TestString2 NoteProperty string TestString2=Two
which makes me think
Get-ItemProperty -path:"registry::HKEY_LOCAL_MACHINE\SOFTWARE\TestKey" -name:"TestString*" | Where-Object ({$_.Name -like "TestString*"})
should do what I want, but it actually returns nothing. What am I missing here?
EDIT: OK, this is weird. And I guess typical Microsoft "thinking". If I actually have a property called TestString*, which is a perfectly valid name, because Microsoft is never consistent, the code above returns all three, plus the other stuff. But, if I change the -Path to -LiteralPath, then I only get the one -Name. Which is... crazy. I have never seen any documentation saying that -LiteralPath also makes -Name behave like -LiteralName.

Combining Get-ACL results into 1 object

Please excuse my beginner level powershell.
I want to be able to combine the results of different results of Get-ACL into one object that'll later be exported
At the very basic level all I want is to combine different results of different folders for code below:
$test = (get-acl $path).Access | select -ExpandProperty IdentityReference
This gives me a result of:
Value
-----
NT AUTHORITY\SYSTEM
BUILTIN\Administrators
Etc
Etc
I want an object that will be like some thing like this (plus more columns, about 4-5 total):
Folder1 Folder2
----- -------
NT AUTHORITY\SYSTEM NT AUTHORITY\SYSTEM
BUILTIN\Administrators BUILTIN\Administrators
Etc Etc
Etc Etc
I tried exploring building a custom object, but I couldn't find a way to list the objects values properly like my first results
$Custom = New-Object PSObject
$Custom | Add-Member -type NoteProperty -name Folder1 -value $test.value
Gives me:
Folder1
-------
{NT AUTHORITY\SYSTEM, BUILTIN\Administrators, etc, etc ...}
How can I handle this to give me a result like the first object and then in turn add more to the custom object?
Thanks in advance,
Lou
Based on your description, I think what you need is simply a collection of objects, i.e., $aclObjectList
This script captures a collection where each object is the type of object returned by get-acl. I do this just so I can show you the path property of each object to demonstrate that each object is for one of the three folders involved
Then, the script loops through the array of get-acl objects and outputs the path and IdentityReference of each
If you want to export a single object, then export $aclObjectList
cls
#define and declare an array. A System.Collections.ArrayList can be big and is fast
$aclObjectList = New-Object System.Collections.ArrayList
$aclObjectList.clear()
$path = "C:\Temp\topFolder\Folder 1"
$aclObject = (get-acl $path)
$aclObjectList.Add($aclObject) | Out-Null
$path = "C:\Temp\topFolder\Folder 2"
$aclObject = (get-acl $path)
$aclObjectList.Add($aclObject) | Out-Null
$path = "C:\Temp\topFolder\Folder 3"
$aclObject = (get-acl $path)
$aclObjectList.Add($aclObject) | Out-Null
foreach ($aclObject in $aclObjectList)
{
write-host ($aclObject.Path)
$aclAccessObject = $aclObject.Access | select -ExpandProperty IdentityReference
foreach ($aclAccessItem in $aclAccessObject)
{
write-host ("item=" + $aclAccessItem.Value)
}
write-host
}
Output is:
Microsoft.PowerShell.Core\FileSystem::C:\Temp\topFolder\Folder 1
item=BUILTIN\Administrators
item=NT AUTHORITY\SYSTEM
item=BUILTIN\Users
item=NT AUTHORITY\Authenticated Users
item=NT AUTHORITY\Authenticated Users
Microsoft.PowerShell.Core\FileSystem::C:\Temp\topFolder\Folder 2
item=BUILTIN\Administrators
item=NT AUTHORITY\SYSTEM
item=BUILTIN\Users
item=NT AUTHORITY\Authenticated Users
item=NT AUTHORITY\Authenticated Users
Microsoft.PowerShell.Core\FileSystem::C:\Temp\topFolder\Folder 3
item=BUILTIN\Administrators
item=NT AUTHORITY\SYSTEM
item=BUILTIN\Users
item=NT AUTHORITY\Authenticated Users
item=NT AUTHORITY\Authenticated Users
By the way, the datatype of the object returned by get-acl is a System.Security.AccessControl.DirectorySecurity. You can see this by, e.g., piping one of the $aclObject variables to Get-Member:
$aclObject | Get-Member
TypeName: System.Security.AccessControl.DirectorySecurity
Name MemberType Definition
---- ---------- ----------
Access CodeProperty System.Security.AccessControl.AuthorizationRuleCollection Access{get=GetAccess;}
CentralAccessPolicyId CodeProperty System.Security.Principal.SecurityIdentifier CentralAccessPolicyId{get=GetCentralAccessPolicyId;}
CentralAccessPolicyName CodeProperty System.String CentralAccessPolicyName{get=GetCentralAccessPolicyName;}
Group CodeProperty System.String Group{get=GetGroup;}
Owner CodeProperty System.String Owner{get=GetOwner;}
Path CodeProperty System.String Path{get=GetPath;}
Sddl CodeProperty System.String Sddl{get=GetSddl;}
AccessRuleFactory Method System.Security.AccessControl.AccessRule AccessRuleFactory(System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited...
AddAccessRule Method void AddAccessRule(System.Security.AccessControl.FileSystemAccessRule rule)
AddAuditRule Method void AddAuditRule(System.Security.AccessControl.FileSystemAuditRule rule)
AuditRuleFactory Method System.Security.AccessControl.AuditRule AuditRuleFactory(System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, ...
Equals Method bool Equals(System.Object obj)
GetAccessRules Method System.Security.AccessControl.AuthorizationRuleCollection GetAccessRules(bool includeExplicit, bool includeInherited, type targetType)
GetAuditRules Method System.Security.AccessControl.AuthorizationRuleCollection GetAuditRules(bool includeExplicit, bool includeInherited, type targetType)
GetGroup Method System.Security.Principal.IdentityReference GetGroup(type targetType)
GetHashCode Method int GetHashCode()
GetOwner Method System.Security.Principal.IdentityReference GetOwner(type targetType)
GetSecurityDescriptorBinaryForm Method byte[] GetSecurityDescriptorBinaryForm()
GetSecurityDescriptorSddlForm Method string GetSecurityDescriptorSddlForm(System.Security.AccessControl.AccessControlSections includeSections)
GetType Method type GetType()
ModifyAccessRule Method bool ModifyAccessRule(System.Security.AccessControl.AccessControlModification modification, System.Security.AccessControl.AccessRule rule, [ref] bool modi...
ModifyAuditRule Method bool ModifyAuditRule(System.Security.AccessControl.AccessControlModification modification, System.Security.AccessControl.AuditRule rule, [ref] bool modified)
PurgeAccessRules Method void PurgeAccessRules(System.Security.Principal.IdentityReference identity)
PurgeAuditRules Method void PurgeAuditRules(System.Security.Principal.IdentityReference identity)
RemoveAccessRule Method bool RemoveAccessRule(System.Security.AccessControl.FileSystemAccessRule rule)
RemoveAccessRuleAll Method void RemoveAccessRuleAll(System.Security.AccessControl.FileSystemAccessRule rule)
RemoveAccessRuleSpecific Method void RemoveAccessRuleSpecific(System.Security.AccessControl.FileSystemAccessRule rule)
RemoveAuditRule Method bool RemoveAuditRule(System.Security.AccessControl.FileSystemAuditRule rule)
RemoveAuditRuleAll Method void RemoveAuditRuleAll(System.Security.AccessControl.FileSystemAuditRule rule)
RemoveAuditRuleSpecific Method void RemoveAuditRuleSpecific(System.Security.AccessControl.FileSystemAuditRule rule)
ResetAccessRule Method void ResetAccessRule(System.Security.AccessControl.FileSystemAccessRule rule)
SetAccessRule Method void SetAccessRule(System.Security.AccessControl.FileSystemAccessRule rule)
SetAccessRuleProtection Method void SetAccessRuleProtection(bool isProtected, bool preserveInheritance)
SetAuditRule Method void SetAuditRule(System.Security.AccessControl.FileSystemAuditRule rule)
SetAuditRuleProtection Method void SetAuditRuleProtection(bool isProtected, bool preserveInheritance)
SetGroup Method void SetGroup(System.Security.Principal.IdentityReference identity)
SetOwner Method void SetOwner(System.Security.Principal.IdentityReference identity)
SetSecurityDescriptorBinaryForm Method void SetSecurityDescriptorBinaryForm(byte[] binaryForm), void SetSecurityDescriptorBinaryForm(byte[] binaryForm, System.Security.AccessControl.AccessContr...
SetSecurityDescriptorSddlForm Method void SetSecurityDescriptorSddlForm(string sddlForm), void SetSecurityDescriptorSddlForm(string sddlForm, System.Security.AccessControl.AccessControlSectio...
ToString Method string ToString()
PSChildName NoteProperty string PSChildName=Folder 1
PSDrive NoteProperty PSDriveInfo PSDrive=C
PSParentPath NoteProperty string PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\Temp\topFolder
PSPath NoteProperty string PSPath=Microsoft.PowerShell.Core\FileSystem::C:\Temp\topFolder\Folder 1
PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem
AccessRightType Property type AccessRightType {get;}
AccessRuleType Property type AccessRuleType {get;}
AreAccessRulesCanonical Property bool AreAccessRulesCanonical {get;}
AreAccessRulesProtected Property bool AreAccessRulesProtected {get;}
AreAuditRulesCanonical Property bool AreAuditRulesCanonical {get;}
AreAuditRulesProtected Property bool AreAuditRulesProtected {get;}
AuditRuleType Property type AuditRuleType {get;}
AccessToString ScriptProperty System.Object AccessToString {get=$toString = "";...
AuditToString ScriptProperty System.Object AuditToString {get=$toString = "";...

Select-Object -ExpandProperty vs. Get-ItemPropertyValue

Most likely there's something fundamental I don't understand about PowerShell. I really don't like when writing even medium sized pipes, where grabbing a property breaks the workflow by having to put parens around the statement up to that point, for eg.
(Get-ChildItem ~\.gitconfig).Length
This is tedious. Because Length looks very much like a property, one would think
Get-ChildItem ~\.gitconfig | Get-ItemPropertyValue -Name Length
would work. However, it does not. Taking a look at the interface of the System.IO.FileSystemInfo object returned by the File System PSDrive provider, one sees that it doesn't have a Length property. It does have a FullName property though, hence
Get-ChildItem ~\.gitconfig | Get-ItemPropertyValue -Name FullName
works as expected. To retrieve the size (Length) of a file using the pipe, one has to use Select-Object with the -ExpandProperty like
Get-ChildItem ~\.gitconfig | Select-Object -ExpandProperty Length
How does one know up front whether placing a . after an object and iterating through the results of tab completion, if the entry is an object or a property? It's very annoying that even common operations are confusing as hell, given for instance that reading environmental variables goes by
Get-Item -Path Env:\USERNAME
returns
Name Value
---- -----
USERNAME mnagy
If it's an item, Get-ItemProperty and Get-ItemPropertyValue must play a role here. Because of the Name:Value structure of the result, newcomers might be intrigued to obtain the actual value saying
Get-Item -Path Env:\USERNAME | Get-ItemPropertyValue
or actually reading how Get-ItemPropertyValue should be used modify the query to
Get-ItemPropertyValue -Path Env:\ -Name USERNAME
which in fact results in
Get-ItemPropertyValue : Cannot use interface. The IPropertyCmdletProvider interface is not supported by this provider.
At line:1 char:1
+ Get-ItemPropertyValue -Path Env:\ -Name USERNAME
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotImplemented: (:) [Get-ItemPropertyValue], PSNotSupportedException
+ FullyQualifiedErrorId : NotSupported,Microsoft.PowerShell.Commands.GetItemPropertyValueCommand
This entire construction seems utterly inconsistent to me and most vexing, but hopefully not by design, but because I look at it from the wrong angle.
To your first note: Length, for .hg directoy, worked form me just fine (giving you the number of files within):
Ps C:\> (Get-ChildItem .hg).Length
18
I tend to use get-member to check what is supported and what is not.
If I check it for my directory reports:
(Get-ChildItem reports) | gm
TypeName: System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
LinkType CodeProperty System.String LinkType{get=GetLinkType;}
Mode CodeProperty System.String Mode{get=Mode;}
Target CodeProperty System.Collections.Generic.IEnumerable`1[[System.String, mscorlib, Version=...
AppendText Method System.IO.StreamWriter AppendText()
CopyTo Method System.IO.FileInfo CopyTo(string destFileName), System.IO.FileInfo CopyTo(s...
Create Method System.IO.FileStream Create()
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
CreateText Method System.IO.StreamWriter CreateText()
Decrypt Method void Decrypt()
Delete Method void Delete()
Encrypt Method void Encrypt()
Equals Method bool Equals(System.Object obj)
GetAccessControl Method System.Security.AccessControl.FileSecurity GetAccessControl(), System.Secur...
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, Sys...
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
MoveTo Method void MoveTo(string destFileName)
Open Method System.IO.FileStream Open(System.IO.FileMode mode), System.IO.FileStream Op...
OpenRead Method System.IO.FileStream OpenRead()
OpenText Method System.IO.StreamReader OpenText()
OpenWrite Method System.IO.FileStream OpenWrite()
Refresh Method void Refresh()
Replace Method System.IO.FileInfo Replace(string destinationFileName, string destinationBa...
SetAccessControl Method void SetAccessControl(System.Security.AccessControl.FileSecurity fileSecurity)
ToString Method string ToString()
PSChildName NoteProperty string PSChildName=jv_libgdbs_tests-20180822-Test.xml
PSDrive NoteProperty PSDriveInfo PSDrive=C
PSIsContainer NoteProperty bool PSIsContainer=False
PSParentPath NoteProperty string PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\prg_sdk\stx8-j...
PSPath NoteProperty string PSPath=Microsoft.PowerShell.Core\FileSystem::C:\prg_sdk\stx8-jv_swin...
PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem
Attributes Property System.IO.FileAttributes Attributes {get;set;}
CreationTime Property datetime CreationTime {get;set;}
CreationTimeUtc Property datetime CreationTimeUtc {get;set;}
Directory Property System.IO.DirectoryInfo Directory {get;}
DirectoryName Property string DirectoryName {get;}
Exists Property bool Exists {get;}
Extension Property string Extension {get;}
FullName Property string FullName {get;}
IsReadOnly Property bool IsReadOnly {get;set;}
LastAccessTime Property datetime LastAccessTime {get;set;}
LastAccessTimeUtc Property datetime LastAccessTimeUtc {get;set;}
LastWriteTime Property datetime LastWriteTime {get;set;}
LastWriteTimeUtc Property datetime LastWriteTimeUtc {get;set;}
Length Property long Length {get;}
Name Property string Name {get;}
BaseName ScriptProperty System.Object BaseName {get=if ($this.Extension.Length -gt 0){$this.Name.Re...
VersionInfo ScriptProperty System.Object VersionInfo {get=[System.Diagnostics.FileVersionInfo]::GetVer...
How does one know up front whether placing a . after an object and
iterating through the results of tab completion, if the entry is an
object or a property?
You check it with with Get-Member.
For the Get-Item -Path Env:\USERNAME you can again check:
PS C:\> Get-Item -Path Env:\USERNAME | gm
TypeName: System.Collections.DictionaryEntry
Name MemberType Definition
---- ---------- ----------
Name AliasProperty Name = Key
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
PSDrive NoteProperty PSDriveInfo PSDrive=Env
PSIsContainer NoteProperty bool PSIsContainer=False
PSPath NoteProperty string PSPath=Microsoft.PowerShell.Core\Environment::USERNAME
PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\Environment
Key Property System.Object Key {get;set;}
Value Property System.Object Value {get;set;}
Now check the USERNAME (you see the key you are asking and its value):
PS C:\> (Get-Item -Path Env:\USERNAME).key
USERNAME
PS C:\> (Get-Item -Path Env:\USERNAME).value # my login
gurun

Under what circumstances would one need the information emitted when -NoTypeInformation is *not* passed to ConvertTo-Csv or Export-Csv?

It occurred to me today that, after so many years of habitually passing -NoTypeInformation to Export-Csv/ConvertTo-Csv to prevent that undesirable comment line from being emitted, perhaps Import-Csv/ConvertFrom-Csv would be able to reconstruct objects with their original property types (instead of all of them being String) if only I hadn't suppressed that type information. I gave it a try...
PS> Get-Service | ConvertTo-Csv
...and, having not actually seen in a long time what gets emitted by omitting -NoTypeInformation, was reminded that it only includes the type of the input objects (just the first object, in fact), not the types of the members...
#TYPE System.ServiceProcess.ServiceController
"Name","RequiredServices","CanPauseAndContinue","CanShutdown","CanStop","DisplayName","DependentServices","MachineName","ServiceName","ServicesDependedOn","ServiceHandle","Status","ServiceType","StartType","Site","Container"
...
Comparing the result of serializing with type information and then deserializing...
PS> Get-Service | ConvertTo-Csv | ConvertFrom-Csv | Get-Member
TypeName: CSV:System.ServiceProcess.ServiceController
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
CanPauseAndContinue NoteProperty string CanPauseAndContinue=False
CanShutdown NoteProperty string CanShutdown=False
CanStop NoteProperty string CanStop=True
Container NoteProperty object Container=null
DependentServices NoteProperty string DependentServices=System.ServiceProcess.ServiceController[]
DisplayName NoteProperty string DisplayName=Adobe Acrobat Update Service
MachineName NoteProperty string MachineName=.
Name NoteProperty string Name=AdobeARMservice
RequiredServices NoteProperty string RequiredServices=System.ServiceProcess.ServiceController[]
ServiceHandle NoteProperty string ServiceHandle=
ServiceName NoteProperty string ServiceName=AdobeARMservice
ServicesDependedOn NoteProperty string ServicesDependedOn=System.ServiceProcess.ServiceController[]
ServiceType NoteProperty string ServiceType=Win32OwnProcess
Site NoteProperty string Site=
StartType NoteProperty string StartType=Automatic
Status NoteProperty string Status=Running
...to the result of serializing without type information and then deserializing...
PS> Get-Service | ConvertTo-Csv -NoTypeInformation | ConvertFrom-Csv | Get-Member
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
CanPauseAndContinue NoteProperty string CanPauseAndContinue=False
CanShutdown NoteProperty string CanShutdown=False
CanStop NoteProperty string CanStop=True
Container NoteProperty object Container=null
DependentServices NoteProperty string DependentServices=System.ServiceProcess.ServiceController[]
DisplayName NoteProperty string DisplayName=Adobe Acrobat Update Service
MachineName NoteProperty string MachineName=.
Name NoteProperty string Name=AdobeARMservice
RequiredServices NoteProperty string RequiredServices=System.ServiceProcess.ServiceController[]
ServiceHandle NoteProperty string ServiceHandle=
ServiceName NoteProperty string ServiceName=AdobeARMservice
ServicesDependedOn NoteProperty string ServicesDependedOn=System.ServiceProcess.ServiceController[]
ServiceType NoteProperty string ServiceType=Win32OwnProcess
Site NoteProperty string Site=
StartType NoteProperty string StartType=Automatic
Status NoteProperty string Status=Running
...the only difference is that the TypeName changes from CSV:System.ServiceProcess.ServiceController (the same type specified by the #TYPE comment prefixed with CSV:) to System.Management.Automation.PSCustomObject. All the members are the same, all the properties are of type String, and in both cases you have deserialized objects that do not contain the methods of and are not in any way connected to or proxies of the original objects.
Evidently Microsoft felt that not only could it be desirable to include this type information in the CSV output, but that it should be done by default. Since I can't really ask "Why did they do this?", what I'm wondering is how could this information potentially be useful? Do the *-Csv serialization cmdlets use it for anything other than setting the TypeName of each object? Why would I ever want this type information communicated "in-band" in the CSV output as opposed to just...knowing that this file containing service information came from System.ServiceProcess.ServiceController instances, or even just not caring what the original type was as long as it has the properties I expect?
The only two use cases I can think of are if you receive a CSV file created by an unknown PowerShell script then having the type information can aide in determining what application/library/module was used to produce the data, or if you had a ridiculously general script that attempted to refresh arbitrary input based on the type information, like this...
Import-Csv ... `
| ForEach-Object -Process {
# Original type name of the first record, not necessarily this record!
$firstTypeName = $_.PSObject.TypeNames[0];
if ($firstTypeName -eq 'CSV:System.ServiceProcess.ServiceController')
{
Get-Service ...
}
elseif ('CSV:System.IO.DirectoryInfo', 'CSV:System.IO.FileInfo' -contains $firstTypeName)
{
Get-ChildItem ...
}
elseif ($firstTypeName -eq 'CSV:Microsoft.ActiveDirectory.Management.ADObject')
{
Get-ADObject ...
}
...
}
...but those aren't very compelling examples, especially considering that, as I noted, this type information was deemed so important that it is included by default. Are there other use cases I'm not thinking of? Or is this simply a case of "It's better (and cheap) to include it and not need it than to need it and not include it"?
Related: PowerShell GitHub issue with discussion about making -NoTypeInformation the default in a then-future version (6.0, according the cmdlet documentation), as well as an apparent consensus that there's no point in ever omitting -NoTypeInformation.
If you have a thing which understands the header and also knows how to construct an other thing of that type, then that first thing could conceivably create one or more those other things from the string representation. I'm not saying it's useful but I am saying that is a potential use if a .csv might be usable where a file of another type might not. I may or may not have actually done this for reasons similar to what I mention here in answer to your question.

Enumerating a PSCustomObject

Does anyone have know of a way to "break" open a hash table source from a custom object. There is no .getenumerrator on a custom object but I have this hashtable: #{0=0.05; 1024=0.050; 51200=0.050; 512000=0.050; 1024000=0.045; 5120000=0.037}. I am pulling this information via the Azure RateCard REST API and need to break it down so I have access to each tier of rates to generate an accurate report of usage cost. Any suggestions? Sample outputs:
MeterId : d23a5753-ff85-4ddf-af28-8cc5cf2d3882
MeterName : Standard IO - Page Blob/Disk (GB)
MeterCategory : Storage
MeterSubCategory : Locally Redundant
Unit : GB
MeterTags : {}
MeterRegion :
MeterRates : #{0=0.05; 1024=0.050; 51200=0.050; 512000=0.050; 1024000=0.045; 5120000=0.037}
EffectiveDate : 2014-02-01T00:00:00Z
IncludedQuantity : 0.0
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
EffectiveDate NoteProperty System.String EffectiveDate=2014-02-01T00:00:00Z
IncludedQuantity NoteProperty System.Decimal IncludedQuantity=0.0
MeterCategory NoteProperty System.String MeterCategory=Storage
MeterId NoteProperty System.String MeterId=d23a5753-ff85-4ddf-af28-8cc5cf2d3882
MeterName NoteProperty System.String MeterName=Standard IO - Page Blob/Disk (GB)
MeterRates NoteProperty System.Management.Automation.PSCustomObject MeterRates=#{0=0.05; 1024=0.050; 51200=0.050; 512000=0.050; 1024000=0.045; 5120000=0.037}
MeterRegion NoteProperty System.String MeterRegion=
MeterSubCategory NoteProperty System.String MeterSubCategory=Locally Redundant
MeterTags NoteProperty System.Object[] MeterTags=System.Object[]
Unit NoteProperty System.String Unit=GB
Not sure what it is you mean by break open but this should give you the keys:
$object.MeterRates.keys
This will give you the values:
$object.MeterRates.keys | % {$object.MeterRates[$_]}
Are you after the total value? I guessing it might be something like this:
($object.MeterRates.keys | % {$object.MeterRates[$_] * $_} | Measure-Object -Sum).Sum
There's probably a better way to do this but my particular version of powershell hacking produced something like this -
$a = "#{0=0.05; 1024=0.050; 51200=0.050; 512000=0.050; 1024000=0.045; 5120000=0.037}"
$b = ConvertFrom-StringData `
-StringData $a.Replace("; ","`n").Replace("#","").Replace("{","").Replace("}","")
This presumes that the entire string is available, replace the semicolons with newlines, ditches the other bits and give it to ConvertFrom-StringData
Found this code on a similar question. Solves my problem:
$js | Get-Member -MemberType NoteProperty).Name