PowerShell is a superb tool for digging directly into the systems and getting the information you need. Sometimes though, getting exactly what you’re looking for isn’t readily available. For example, say I want to get the ACLs for my scripting directory, I just:
Get-Acl -Path c:\scripts | Format-Table -AutoSize -Wrap
And I get this:
Cool to be sure, but if you look closely, you’ll see that the Access field is concatenated data. The user, the access control type, and file system rights are all one string. Not bad for reading, but if I want some kind of report to send to the boss (a la CSV), or use the output at a later time to reapply/compare permissions, it might not be acceptable. Furthermore, we’ve formatted the data, making it essentially useless at this point if we want to move it through the pipeline. But if you expand the Access property like so:
$Path = "c:\scripts" $Directory = Get-Acl -Path $Path $Directory.Access
We get the data broken down by it’s subproperties:
This is something we can work with! But maybe I want the path too. So let’s see if it’s available.
$Directory.Access | Get-Member
And we get:
Not exactly screaming out any winners here. But we did have the file path in the Get-ACL. So how do we put this all together in a single return? How about with a PSCustomObject?
PSCustomObjects are a lot like hash tables. They use name-value pairs like hash tables, but offer you a little more flexibility like allowing you to control the order in which they’re presented. This makes them very nice for gathering the data you need, ordering it however you like (as far left as possible), and letting you move it through the pipe to be used or exported. There’s some good reading to be had regarding PSCustomObjects in about_Object_Creation in PowerShell’s Get-Help or on TechNet.
So for our first example, let’s get the file path, the directory owner, and then the group, the access type, and the folder rights. To do this, we’ll need to grab data from our Path variable, our Get-ACL cmdlet, and the Access property. So we modify our original lines of code with a ForEach statement to call out the Access property to get our group list:
$Path = "c:\scripts" $Directory = Get-Acl -Path $Path ForEach ($Dir in $Directory.Access){}
And then we’ll create our PSCustomObject table inside the ForEach statement:
$Path = "c:\scripts" $Directory = Get-Acl -Path $Path ForEach ($Dir in $Directory.Access){ [PSCustomObject]@{ Path = $Path Owner = $Directory.Owner Group = $Dir.IdentityReference AccessType = $Dir.AccessControlType Rights = $Dir.FileSystemRights }#EndPSCustomObject }#EndForEach
And then we execute…
Success! So now we can make our script a function and feed it some parameters and we can get our ACLs by group for a directory!
But we’re not done yet!
Oftentimes, I get asked from my colleagues if there’s a quick and easy way to retrieve network adapter information from machines on the network. Those requests often include IP Address, DNS Settings, driver information and link speed. Using Get-CIMInstance, we can retrieve the network adapter configuration for a machine by leveraging the Win32_NetworkAdapterConfiguration class.
This class gives us a wealth of information on the adapter config, but lacks the driver info and link speed that I mentioned earlier. Using the Get-NetAdapter cmdlet, however, will net us that information that we’re missing.
For your information, you could also use the Get-CIMInstance cmdlet and target the MSFT_NetAdapter class in the root\standardcimv2 Namespace, but since we have a cmdlet that does all of the heavy lifting for us, we’ll go ahead and use that.
So, now that we know how to get the data we want, let’s go ahead and put together our list in a PSCustomObject like before:
$Computer = "server01" $AdapterCfg = (Get-CIMInstance Win32_NetworkAdapterConfiguration -ComputerName $Computer).where({$PSItem.IPEnabled}) $NetAdapter = Get-NetAdapter -CimSession $Computer
[PSCustomObject]@{ System = $AdapterCfg.PSComputerName Description = $AdapterCfg.Description IPAddress = $AdapterCfg.IPAddress SubnetMask = $AdapterCfg.IPSubnet DefaultGateway = $AdapterCfg.DefaultIPGateway DNSServers = $AdapterCfg.DNSServerSearchOrder DNSDomain = $AdapterCfg.DNSDomain DNSSuffix = $AdapterCfg.DNSDomainSuffixSearchOrder FullDNSREG = $AdapterCfg.FullDNSRegistrationEnabled WINSLMHOST = $AdapterCfg.WINSEnableLMHostsLookup WINSPRI = $AdapterCfg.WINSPrimaryServer WINSSEC = $AdapterCfg.WINSSecondaryServer DOMAINDNSREG = $AdapterCfg.DomainDNSRegistrationEnabled DNSEnabledWINS = $AdapterCfg.DNSEnabledForWINSResolution TCPNETBIOSOPTION = $AdapterCfg.TcpipNetbiosOptions IsDHCPEnabled = $AdapterCfg.DHCPEnabled AdapterName = $NetAdapter.name Status = $NetAdapter.status LinkSpeed = $NetAdapter.linkspeed Driverinformation = $NetAdapter.driverinformation DriverFilename = $NetAdapter.DriverFileName MACAddress = $AdapterCfg.MACAddress }#EndPSCustomObject
Success! Well…almost…
Once thing you might have noticed is that the data you’ve collected concatenates the data for all network adapters, so if you have multiple ethernet or wireless adapters, the data is all together and you might not want that. Also, for my purposes, I only want the adapters that are connected to the network. So:
$Computer = "server01" $NetAdapter = (Get-NetAdapter -CimSession $Computer).where({$PSItem.LinkSpeed -gt 1})
This will give me only my network adapters that are showing a link speed greater than 1.
Now that I’ve got my adapters, I’ll feed them through a loop to gather the additional data from the Win32_NetworkAdapterConfiguration class. I’ll use the MAC Address to match up the network adapters. To do that, I need to modify the MAC Address string because Get-NetAdapter displays the MAC Address with dashes, and Win32_NetworkAdapterConfiguration stores it with colons.:
ForEach ($Net in $NetAdapter){ $NetMAC = $Net.MACAddress -replace "-",":" $AdapterCfg = (Get-CIMInstance Win32_NetworkAdapterConfiguration -ComputerName $Net.PSComputerName).where({$PSItem.MACAddress -eq $NetMAC}) }
So for the one MAC Address, we have two instances in our WMI class. But looking further, we see that only one is actually IPEnabled. So let’s go with that one:
(Get-CIMInstance Win32_NetworkAdapterConfiguration -ComputerName $Net.PSComputerName).where({$PSItem.MACAddress -eq $NetMAC}) | Where-Object IPEnabled -EQ $True
Now that looks a bit better! So now let’s create our PSCustomObject table.
[PSCustomObject]@{ System = $AdapterCfg.PSComputerName Description = $AdapterCfg.Description IPAddress = $AdapterCfg.IPAddress SubnetMask = $AdapterCfg.IPSubnet DefaultGateway = $AdapterCfg.DefaultIPGateway DNSServers = $AdapterCfg.DNSServerSearchOrder DNSDomain = $AdapterCfg.DNSDomain DNSSuffix = $AdapterCfg.DNSDomainSuffixSearchOrder FullDNSREG = $AdapterCfg.FullDNSRegistrationEnabled WINSLMHOST = $AdapterCfg.WINSEnableLMHostsLookup WINSPRI = $AdapterCfg.WINSPrimaryServer WINSSEC = $AdapterCfg.WINSSecondaryServer DOMAINDNSREG = $AdapterCfg.DomainDNSRegistrationEnabled DNSEnabledWINS = $AdapterCfg.DNSEnabledForWINSResolution TCPNETBIOSOPTION = $AdapterCfg.TcpipNetbiosOptions IsDHCPEnabled = $AdapterCfg.DHCPEnabled AdapterName = $Net.name Status = $Netr.status LinkSpeed = $Net.linkspeed Driverinformation = $Net.driverinformation DriverFilename = $Net.DriverFileName MACAddress = $AdapterCfg.MACAddress InterfaceName = $Net.InterfaceDescription }#EndPSCustomObject
And our return looks good!
Now we can put the finishing touches on our script to make it a full function!
PSCustomObjects can help you create some exciting tools to gather information in your environment, or feed information from different sources into other applications (such as Active Directory or SQL). So take a little time to get to know the PSCustomObject class and it might save you some time on the back-end!
Go here if you’d like to download a copy of my ACL function.
Go here if you’d like to download a copy of my NetAdapter
Sidenote: You might notice that I’m using Where-Object as a method in my examples (.where), but my downloads leverage Where-Object in the code. This is something specific to PowerShell 4 and later that I learned from Jeff Hicks’ presentation on PowerShell V4 New Features on Pluralsight. If you have access to the course, I highly recommend taking a look! I’ll be talking about the different methods available in PowerShell v4 at a later date, and why I’ve gotten so hooked on them!