Get mapped network drives labels - powershell

Is there a way to get mapped network drives labels?
I know it's possible to get multiple properties through the
Get-Object Win32_MappedLogicalDisk
But none of them are labels (please do not misunderstand, I do not want Name i.e. K:, I want labels i.e. My Network drive)

You could use the Com Shell.Application object for this:
$shell = New-Object -ComObject Shell.Application
(Get-WmiObject -Class Win32_MappedLogicalDisk).DeviceID |
# or (Get-CimInstance -ClassName Win32_MappedLogicalDisk).DeviceID |
# or ([System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Network' }).Name |
Select-Object #{Name = 'Drive'; Expression = {$_}},
#{Name = 'Label'; Expression = {$shell.NameSpace("$_").Self.Name.Split("(")[0].Trim()}}
# when done, clear the com object from memory
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
Output:
Drive Label
----- -----
X: MyCode
Some explanation for the above:
Using the COM object Shell.Application, you can drill down through its properties and methods.
.NameSpace create and return a Folder object for the specified folder
.Self gets a Read-Only duplicate System.Shell.Folder object
.Name from that we take the Name property like 'MyCode (X:)'
.Split this name we split on the opening bracket '(',
[0] take the first part of the splitted name and
.Trim() get rid of any extraneous whitespace characters
Another way is to go into the registry, but remember that after a mapped network folder is unmapped, the old registry value remains.
This is why below code still uses one of two methods to find active network mappings first:
# the registry key to search in
$regKey = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2'
# list the mapped network drives and loop through
# you can also use Get-CimInstance -ClassName Win32_MappedLogicalDisk
Get-WmiObject -Class Win32_MappedLogicalDisk | ForEach-Object {
# create the full registry key by replacing the backslashes in the network path with hash-symbols
$key = Join-Path -Path $regKey -ChildPath ($_.ProviderName -replace '\\', '#')
# return an object with the drive name (like 'X:') and the Label the user gave it
[PsCustomObject]#{
Drive = $_.DeviceID
Label = Get-ItemPropertyValue -Path $key -Name '_LabelFromReg' -ErrorAction SilentlyContinue
}
}
Output here also:
Drive Label
----- -----
X: MyCode

I am not aware of a cmdlet that will give you that info. I believe you can find it by looking at the registry with gci, but you would need to cleanup the output.
get-childitem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2"

Related

I want to export computer data using Get-CimInstance to a csv but also include the computer name

Right now I'm running
Get-CimInstance -ComputerName $i.DNSHostName -Class CIM_Processor | Select-Object "Name", "NumberOfCores" | Export-Csv -Path .\test.csv -NoTypeInformation -Append`
This will put the CPU name and core count in a CSV fine but how can I add in the hostname as another column?
This is a good case for using a PsCustomObject, which allows you to dynamically create an object with arbitrary properties/values.
$cimProcessor = Get-CimInstance -ComputerName $hostName -Class CIM_Processor
$row = [PSCustomObject]#{
ComputerName = $hostName
Name = $cimProcessor.Name
NumberOfCores = $cimProcessor.NumberOfCores
}
$row | Export-Csv -Path test.csv -NoTypeInformation -Append
In the case where you want to retrieve information from a remote machine first, then you can skip the calculated property and simply select the PSComputerName property instead:
Note: The ellipses ... indicate the code before or after from your original sample.
... | Select-Object PSComputerName, Name, NumberOfCores | ...
Any cmdlet which connects to remote systems via WinRM should have this property automatically set when data is returned over a remote session.
If you are running this from a local session, you could use Select-Object to create a calculated property, then call the hostname command to populate its value:
... | Select-Object "Name", "NumberOfCores", #{
Name = 'ComputerName';
Expression = { hostname };
} | ...
This solution is often suitable for cross platform scripts since a hostname binary is available out of the box on Windows, MacOS, and most major distributions of Linux.
Explaining Calculated Properties
Calculated properties work by defining a hashtable in a specific format and providing the hashtable as a property to be computed just as you would use a string for a real property on the object:
$property = #{
Name = 'PropertyName';
Expression = { 'ScriptBlock' };
}
# Note that the hashtable can be specified inline as shown above
# or as a variable like shown here
[PSCustomObject]#{ Name = 'Bender'; Loves = 'Bending' } |
Select-Object Name, Loves, $property
Keep in mind that within the Expression ScriptBlock, $PSItem/$_ is set to the current object in the pipeline. Use this to reference static or instance properties from the current object you are selecting information from.

powershell how do I Create/format a dynamic list from a group membership to use in a for each loop

bit of a noob question.
I have the following cmd which grabs the server members of a group which I can copy into a text list. however as the group changes I need to modify the text list manually.
Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | select name
when I have that output in a text list, the following works fine.
$listpath = "C:\Scripts\servers.txt"
[System.Collections.ArrayList]$list = #(Get-content $listpath)
foreach($ComputerName in $list)
{
Get-Uptime -ComputerName $ComputerName
I want to know if it is possible to use a variable that I can use again in a for each loop. I've tried to do so, however the format of the list is not the same when is goes into a variable, thus the function (get-uptime) against the server doesn't work, anyone know what I can do to format the output so I only get the server name?
EG.
$WSUS_7PM = Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | select name
PS C:\Windows\system32> $WSUS_7PM
name
----
AXXXXX003
BXXXXX005
CXXXXX006
DXXXXX007
PS C:\Windows\system32> foreach($Name in $WSUS_7PM) {Write-Host $Name}
#{name=AXXXXX003}
#{name=BXXXXX005}
#{name=CXXXXX006}
#{name=DXXXXX007}
so when I run the same cmds as above modified with the variable instead of the text list, I get the following as the server name is obviously incorrect.
$listpath = $WSUS_7PM
[System.Collections.ArrayList]$list = #(Get-content $WSUS_7PM)
foreach($ComputerName in $list)
{
Get-Uptime -ComputerName $ComputerName
WARNING: Unable to connect to #{name=AXXXXX003}
WARNING: Unable to connect to #{name=BXXXXX005}
I hope that makes sense to someone, appreciate the help in understanding what the difference is in the object output.
Thanks
Alzoo
When you use Select-Object name you are creating a list of objects with a name property. You can either expand it ahead of time
$WSUS_7PM = Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | Select-Object -ExpandProperty name
or reference the name property later
foreach($Name in $WSUS_7PM.name) {Write-Host $Name}

shortened output of property values in recursive function

I want to recursively enumerated all WMI namespaces.I have this function:
function Get-WmiNamespace {
Param(
[parameter()]
[string]$Namespace = 'root',
[parameter()]
[string]$Locale = 'MS_409',
[parameter()]
[switch]$Recurse
)
Begin {
$WMIParams = #{
Namespace = $Namespace
Class = '__NAMESPACE'
Locale = $Locale
ErrorAction = 'SilentlyContinue'
}
}
Process {
Get-WmiObject #WMIParams |
Sort-Object -Property Name -CaseSensitive -Culture "en-US" |
ForEach-Object {
$WMIParams.Namespace = "{0}\{1}" -f $_.__NAMESPACE, $_.Name
$object = [PSCustomObject] #{
Namespace = $WMIParams.Namespace
}
$object.PSTypeNames.Insert(0,'Wmi.Namespace.Name')
$object
if ($recurse) {
$PSBoundParameters.Namespace = $WMIParams.Namespace
Get-WMINamespace #PSBoundParameters
}
}
}
}
Inspired here:
[https://learn-powershell.net/2014/05/09/quick-hits-list-all-available-wmi-namespaces-using-powershell/]
I get this output:
Namespace
---------
ROOT\Appv
ROOT\CIMV2
ROOT\CIMV2\mdm
ROOT\CIMV2\mdm\dmmap
ROOT\CIMV2\mdm\MS_405
ROOT\CIMV2\ms_405
ROOT\CIMV2\ms_409
ROOT\CIMV2\power
ROOT\CIMV2\power\m...
ROOT\CIMV2\power\m...
ROOT\CIMV2\Security
ROOT\CIMV2\Securit...
ROOT\CIMV2\Securit...
ROOT\CIMV2\Termina...
ROOT\CIMV2\Termina...
ROOT\Cli
ROOT\Cli\MS_405
ROOT\Cli\MS_409
ROOT\DEFAULT
ROOT\DEFAULT\ms_405
ROOT\DEFAULT\ms_409
ROOT\directory
ROOT\directory\LDAP
ROOT\directory\LDA...
ROOT\directory\LDA...
ROOT\Hardware
ROOT\Hardware\ms_405
ROOT\Hardware\ms_409
ROOT\Intel_ME
ROOT\IntelNCS2
ROOT\IntelNCS2\ms_409
ROOT\Interop
ROOT\Interop\ms_405
ROOT\Interop\ms_409
ROOT\Microsoft
ROOT\Microsoft\Hom...
ROOT\Microsoft\pro...
ROOT\Microsoft\Sec...
ROOT\Microsoft\Uev
ROOT\Microsoft\Win...
...
ROOT\Microsoft\Win...
ROOT\msdtc
ROOT\PEH
ROOT\Policy
ROOT\Policy\ms_405
ROOT\Policy\ms_409
ROOT\RSOP
ROOT\RSOP\Computer
ROOT\RSOP\User
ROOT\SECURITY
ROOT\SecurityCenter
ROOT\SecurityCenter2
ROOT\ServiceModel
ROOT\StandardCimv2
ROOT\StandardCimv2...
ROOT\StandardCimv2...
ROOT\StandardCimv2...
ROOT\StandardCimv2...
ROOT\subscription
ROOT\subscription\...
ROOT\subscription\...
ROOT\WMI
ROOT\WMI\ms_405
ROOT\WMI\ms_409
Namespaces names are truncated.
I guess the reason is the width of Name column is set in first iteration of function Get-WmiNamespace according longest value (ROOT\SecurityCenter2).
It can be fixed by piping output to Format-Table with -AutoSize parameter:
Namespace
---------
ROOT\Appv
ROOT\CIMV2
ROOT\CIMV2\mdm
ROOT\CIMV2\mdm\dmmap
ROOT\CIMV2\mdm\MS_405
ROOT\CIMV2\ms_405
ROOT\CIMV2\ms_409
ROOT\CIMV2\power
ROOT\CIMV2\power\ms_405
ROOT\CIMV2\power\ms_409
ROOT\CIMV2\Security
ROOT\CIMV2\Security\MicrosoftTpm
ROOT\CIMV2\Security\MicrosoftVolumeEncryption
ROOT\CIMV2\TerminalServices
ROOT\CIMV2\TerminalServices\ms_405
ROOT\Cli
ROOT\Cli\MS_405
ROOT\Cli\MS_409
...
What would be the best way to solve this behavior?
The entire name is stored in the namespace property. If you just want to see it in the command output, you could use -ExpandProperty from Select-Object
Get-WmiNamespace -Recurse | select -ExpandProperty namespace
Also, just adding Sort shows the full name
Get-WmiNamespace -Recurse | Sort
Without using Expand and Doug is points out, you can just dot it and avoid the format stuff or futzing with trying to change the function itself, etc. Well, at least for a single column.
(Get-WmiNamespace -Recurse).Namespace
# Results
<#
(Get-WmiNamespace -Recurse).Namespace
ROOT\Appv
...
ROOT\CIMV2\Security\MicrosoftTpm
ROOT\CIMV2\Security\MicrosoftVolumeEncryption
ROOT\CIMV2\TerminalServices
ROOT\CIMV2\TerminalServices\ms_409
...
ROOT\Microsoft\SqlServer\ComputerManagement15
ROOT\Microsoft\SqlServer\ComputerManagement15\MS_409
ROOT\Microsoft\SqlServer\ServerEvents
ROOT\Microsoft\SqlServer\ServerEvents\MSSQLSERVER
...
ROOT\Microsoft\Windows\DesiredStateConfigurationProxy
ROOT\Microsoft\Windows\DesiredStateConfigurationProxy\MS_409
...
#>
If you saying you want, like a Linux column file list of these then other steps are needed You can do this Linux like multi-column using the Format-Wide cmdlet.
For Example:
Get-ChildItem -Path 'C:\Program Files' -Recurse |
Format-Wide -Property Name
Get-ChildItem -Path 'C:\Program Files' -Recurse |
Format-Wide -Property Name -Column 5
So, for this list to get a table-like view, do the same thing.
Get-WmiNamespace -Recurse |
Format-Wide -Property namespace -Column 3
# Results
<#
ROOT\Appv ROOT\aspnet ROOT\CIMV2
ROOT\CIMV2\mdm ROOT\CIMV2\mdm\dmmap ROOT\CIMV2\mdm\MS_409
ROOT\CIMV2\ms_409 ROOT\CIMV2\NV ROOT\CIMV2\NV\Events
....
#>
Use whatever column count that fits your screen. No, you cannot use columns and autosize together as they are mutually exclusive.
So, as Doug points out you can make your own formatter, or as per your comment...
'My point was whether there is a way to modify function itself to get
table formated '
...you can doctor the function to use aforementioned for the results you are after.

Unable to show export-csv in PoweSshell

I have been researching the web to see what am I missing and can't find out, I run the command it goes thru the list of computers but the export doc is always empty.
Here is the code
foreach ($computer in Get-Content "\\NETWORK PATH\user-computers.txt") {
Write-host $computer
$colDrives = Get-WmiObject Win32_MappedLogicalDisk -ComputerName $computer
$Report = #()
# Set our filename based on the execution time
$filenamestring = "$computer-$(get-date -UFormat "%y-%b-%a-%H%M").csv"
foreach ($objDrive in $colDrives) {
# For each mapped drive – build a hash containing information
$hash = #{
ComputerName = $computer
MappedLocation = $objDrive.ProviderName
DriveLetter = $objDrive.DeviceId
}
# Add the hash to a new object
$objDriveInfo = new-object PSObject -Property $hash
# Store our new object within the report array
$Report += $objDriveInfo
}}
# Export our report array to CSV and store as our dynamic file name
$Report | Export-Csv -LiteralPath "\\NETWORK PATH\Drive-Maps.csv" -NoTypeInformation
I want to know what each computer currently got mapped network drives, thanks for all your help and guidance.
I'm not sure why you're not getting output. I've rewritten your script for a few reasons I'd like to point out. First, your variable naming is not very clear. I'm guessing you come from a VBScripting background. Next, you're creating an array and then adding to it - this is simply not needed. You can capture the output of any loop/scriptblock/etc directly by assigning like tihs.
$Report = foreach($thing in $manythings){Do lots of stuff and everything in stdout will be captured}
If you write your script in a way that takes advantage of the pipeline, you can do even more. Next, creating the object with New-Object is slow compared to using the [PSCustomObject] type accelerator introduced in V3. Finally, it seems you create a custom csv for each computer but in the end you just export everything to one file. I'm going to assume you are wanting to collect all this info and put in one CSV.
My recommendation for you to help troubleshoot, run this against your machines and confirm the output on the screen. Whatever you see on the screen should be captured in the report variable. (Except write-host, it's special and just goes to the console)
$computerList = "\\NETWORK PATH\user-computers.txt"
$reportFile = "\\NETWORK PATH\Drive-Maps.csv"
Get-Content $computerList | ForEach-Object {
Write-host $_
$mappedDrives = Get-WmiObject Win32_MappedLogicalDisk -ComputerName $_
foreach ($drive in $mappedDrives)
{
# For each mapped drive – build a hash containing information
[PSCustomObject]#{
ComputerName = $_
MappedLocation = $drive.ProviderName
DriveLetter = $drive.DeviceId
}
}
} -OutVariable Report
Once you know you have all the correct info, run this to export it.
$Report | Export-Csv -LiteralPath $reportFile -NoTypeInformation

DISKPART vs PowerShell Get-Volume

In DISKPART.EXE, I get the info when multiple mount points are assigned to a volume. In the picture we see that Drive G: can also be accessed using D:\SQL\MSSQL13.MSSQLSERVER\DATA\ or D:\BlaBla:
But I can't find the same info using PowerShell's Get-Volume or Get-WMIObject -Class Win32_Volume. Does anyone knows how to extract this info using a native PowerShell function?
I thought of extracting the info by calling DISKPART.EXE inside PowerShell but I would prefer an native PowerShell function like Get-Volume.
Perhaps surprisingly, you can look up mount points via the Win32_MountPoint class:
Get-WmiObject Win32_MountPoint | Select-Object Directory, Volume
Further details can be obtained by looking up the references:
Get-WmiObject Win32_MountPoint | ForEach-Object {
$dir = [wmi]$_.Directory | Select-Object -Expand Name
$vol = [wmi]$_.Volume
New-Object -Type PSObject -Property #{
Directory = $dir
Label = $vol.Label
DriveLetter = $vol.DriveLetter
FileSystem = $vol.FileSystem
DeviceId = $vol.DeviceId
}
}