Foreach drive letter array - powershell

I'm putting together a script that checks whether or not a logical drive is USB or an iSCSI target. And if it is ignore the drive letters associated.
Get-WmiObject win32_logicaldisk -Filter "DriveType='3'" |
where-object{$_.DeviceID -ne $usbletters -and $_.DeviceID -ne $iSCSIletters} | %
{$_.Name} | out-file $kreports\avail.txt
My issue is that when multiple drives are detected of the same type they are simply ignored by the not equal to option. I'm assuming I need to do some form of foreach loop?
If someone could point me in the right direction that would be fantastic!
Here's the full script.
#Variables and Arguments
$kreports = "C:\Kworking\reports"
# Create kworking Reports folder
if (!(Test-Path -path $kreports))
{New-Item $kreports -type directory}
# USB Drive check
$diskdrive = gwmi win32_diskdrive | ?{$_.interfacetype -eq "USB"}
$usbletters = $diskdrive | %{gwmi -Query "ASSOCIATORS OF
{Win32_DiskDrive.DeviceID=`"$($_.DeviceID.replace('\','\\'))`"} WHERE
AssocClass = Win32_DiskDriveToDiskPartition"} | %{gwmi -Query "ASSOCIATORS
OF {Win32_DiskPartition.DeviceID=`"$($_.DeviceID)`"} WHERE AssocClass =
Win32_LogicalDiskToPartition"} | %{$_.DeviceID}
# iSCSI Drive check
$iSCSIdrive = gwmi win32_diskdrive | ?{$_.model -match "iSCSI"}
$iSCSIletters = $iSCSIdrive | %{gwmi -Query "ASSOCIATORS OF
{Win32_DiskDrive.DeviceID=`"$($_.DeviceID.replace('\','\\'))`"} WHERE
AssocClass = Win32_DiskDriveToDiskPartition"} | %{gwmi -Query "ASSOCIATORS
OF {Win32_DiskPartition.DeviceID=`"$($_.DeviceID)`"} WHERE AssocClass =
Win32_LogicalDiskToPartition"} | %{$_.DeviceID}
# Disk Information
Get-WmiObject win32_logicaldisk -Filter "DriveType='3'" | where-object{$_.DeviceID -ne
$usbletters -and $_.DeviceID -ne $iSCSIletters} | %{$_.Name} | out-file
$kreports\avail.txt
# Fix Output Line Spacing
$InputFile = "$kreports\avail.txt"
$OutputFile = "$kreports\availdisks.txt"
$Writer = New-Object IO.StreamWriter "$OutputFile"
$Writer.Write( [String]::Join("`r`n", $(Get-Content $InputFile)) )
$Writer.Close()

The problem is that Win32_LogicalDisk's member DeviceID contains drive letter and colon. As the value is, say, C: and you test for equality against C, the where-object doesn't find anything.
Either include the colon on drive letters or use a regexp the Powershell way.
$disks = gwmi win32_logicaldisk -Filter "DriveType='3'"
# Select all devices that do not have deviceids a,b,c,k or l, followed by colon
$avail = $disks | ? { $_.DeviceID -notmatch "[abckl]:"}
# Do something with filtered results

Related

Correlate Physical Device ID to Volume Device ID

I'm trying to utilize WMI via PowerShell to run through SAN storage on remote servers to grab the Windows disk management volume label.
The only way I've found to do this is to correlate the volume device id (\\?\Volume{34243...} with the physical disk device ID (\\.\PHYSICALDRIVE01).
However, I haven't been able to find out how to link those two fields together. Is this possible with WMI?
For volumes that were assigned a drive letter you can correlate disks and volumes like this:
Get-WmiObject Win32_DiskDrive | ForEach-Object {
$disk = $_
$partitions = "ASSOCIATORS OF " +
"{Win32_DiskDrive.DeviceID='$($disk.DeviceID)'} " +
"WHERE AssocClass = Win32_DiskDriveToDiskPartition"
Get-WmiObject -Query $partitions | ForEach-Object {
$partition = $_
$drives = "ASSOCIATORS OF " +
"{Win32_DiskPartition.DeviceID='$($partition.DeviceID)'} " +
"WHERE AssocClass = Win32_LogicalDiskToPartition"
Get-WmiObject -Query $drives | ForEach-Object {
$driveLetter = $_.DeviceID
$fltr = "DriveLetter='$driveLetter'"
New-Object -Type PSCustomObject -Property #{
Disk = $disk.DeviceID
DriveLetter = $driveLetter
VolumeName = $_.VolumeName
VolumeID = Get-WmiObject -Class Win32_Volume -Filter $fltr |
Select-Object -Expand DeviceID
}
}
}
}
Otherwise it doesn't seem possible with WMI.
On Windows 8/Server 2012 or newer you could use the Get-Partition cmdlet, though:
Get-Partition | Select-Object DiskNumber, DriveLetter, #{n='VolumeID';e={
$_.AccessPaths | Where-Object { $_ -like '\\?\volume*' }
}}
I have done a script that collects the most important stuff from volume and disk WMI. its used with getting information from a Remote Desktop server where a lot of disks are mounted but can be hard to find who is using which disk. its using AD to query the user and connect it with the SID to find the file path. so its a matter of first collecting all the data from the different disk commands and then combine the outputs. the most important command to bind disk data with volume data is the get-partition that shows deviceid
Function Get-VHDMount {
[cmdletbinding()]
Param(
[Parameter(Position=0,ValueFromPipeline=$True)]
[ValidateNotNullorEmpty()]
[OBJECT[]]$Computername,
[STRING]$RDSPATH = '\\rdsprofiles'
)
foreach ($computer in $Computername) {
$RDSItems = (Get-ChildItem $RDSPATH -Recurse -Filter *.vhdx)
$VolumeInfo = invoke-command -ComputerName $computer -scriptblock {Get-Volume | select *}
$VHDMountInfo = Get-WmiObject Win32_Volume -ComputerName $computer |where Label -eq 'user Disk'
$partitioninfo = invoke-command -ComputerName $computer -scriptblock {Get-Partition | Select-Object DiskNumber, #{n='VolumeID';e={$_.AccessPaths | Where-Object { $_ -like '\\?\volume*' }}}}
foreach ($VHDmount in $VHDMountInfo) {
$adinfo = Get-ADUser ($VHDmount.name | Split-Path -Leaf)
[PSCUSTOMOBJECT]#{
Computername = $computer
username = $VHDmount.name | Split-Path -Leaf
displayname = $adinfo.name
SID = $adinfo.SID
deviceid = $VHDmount.deviceid
capacity = ([MATH]::ROUND(($VHDmount.capacity) / 1gb))
HealthStatus = ($VolumeInfo | where ObjectId -eq ($VHDmount.deviceid)).HealthStatus
DiskNumber = ($partitioninfo | where Volumeid -eq ($VHDmount.deviceid)).DiskNumber
Path = ($RDSItems | where fullname -like "*$($adinfo.SID)*").FullName
}
}
}
}

How to extract server information

Objective: How to extract server information?
For each server name listed in servers.txt, I would like to get the following information (in this format):
Server name, IP Address, OS name, Total Physical Memory, Processors, each drive letter and size, System Model
Comma separated and new line for each server.
Below is my PowerShell code. Can your guys give a hint on why this does not work? Also why I get an error with New-Object statement?
foreach ($ComputerName in (Get-Content -Path .\servers.txt)) {
$HashProps = #{
'tHostname' = Get-WmiObject Win32_Computersystem -ComputerName $ComputerName | Select-Object -ExpandProperty Name
'tIP' = [System.Net.Dns]::GetHostAddresses($computername)
'tOS' = Get-WmiObject -ComputerName $ComputerName -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption
'tMemory' = Get-WmiObject Win32_PhysicalMemory | Measure-Object -Property capacity -Sum | foreach { "$("{0:n2}" -f ( $_.Sum/1GB ) )" }
'tcpu' = Get-WmiObject Win32_processor | Select-Object name, numberofcores
'tDisks' = Get-WmiObject Win32_LogicalDisk | foreach { "$($_.DeviceID) $("{0:n2}" -f ( $_.Size/ 1GB ) )" }
'tsysmodel' = Get-Wmiobject Win32_computersystem | Select-Object model
}
New-Object -TypeName psObject -Property $HashProps |
ConvertTo-Csv -NoTypeInformation | Out-File -Append .\output.csv
}
I am open for a other approach, if this is easier.
Have you verified that each of those lines actually return what you want?
I just threw this into the ISE and it works fine:
$f = gwmi win32_computersystem | select name,model,totalphysicalmemory
$hash = #{
'name' = $f.name
'model' = $f.model
'memory' = $("{0:n2}" -f ( $f.totalphysicalmemory/1GB ) )
}
New-Object -TypeName psobject -Property $hash | ConvertTo-Csv -NoTypeInformation | Out-File -Append .\test.csv
Also, if you want the properties to appear in a specific order in the CSV, it will take some additional magic, otherwise they're put in alphabetically.
A little bit pimped, maybe this will help you:
$Servers = Foreach ($ComputerName in (Get-Content -Path .\Servers.txt)) {
$CS = Get-WmiObject Win32_ComputerSystem -ComputerName $ComputerName
$OS = Get-WmiObject Win32_OperatingSystem -ComputerName $ComputerName
$PM = Get-WmiObject Win32_PhysicalMemory -ComputerName $ComputerName
$PR = Get-WmiObject Win32_processor -ComputerName $ComputerName
$LD = Get-WmiObject Win32_LogicalDisk -ComputerName $ComputerName
$IP = [System.Net.Dns]::GetHostAddresses($ComputerName)
[PSCustomObject]#{
ServerName = $CS | Select-Object -ExpandProperty Name
IPAddress = $IP | Select-Object -ExpandProperty IPAddressToString
OS = $OS | Select-Object -ExpandProperty Caption
Memory = $PM | Measure-Object -Property Capacity -Sum | foreach { "$("{0:n2}" -f ( $_.Sum/1GB ) )" }
CPU = $PR | Select-Object Name, NumberOfCores
Disks = $LD | foreach { "$($_.DeviceID) $("{0:n2}" -f ( $_.Size/ 1GB ) )" }
Model = $CS | Select-Object -ExpandProperty Model
}
}
$File = Join-Path $env:TEMP 'Ouptut.csv'
$Servers | Export-Csv -Path $File -NoTypeInformation -Delimiter ';'
Start-Process $File

Loop Issue - Remote Server

I wrote a small script to get some basic information off a few remote servers. But my output it is a bit odd. I believe my issue is with my $DRIVE function.
Code:
$serversList = 'svr01.xxx.com',
'svr03.xxx.com',
'svr05.xxx.com',
'svr06.xxx.com',
'svr08.xxx.com'
#End of Server List
Write-Output "Start of Hal0 `n";
ForEach ($server in $serversList) {
$OS = (Get-WmiObject -class Win32_OperatingSystem).Caption
$SYSNAME = (Get-WmiObject -class Win32_LogicalDisk -ComputerName $server).SystemName
$DRIVE = {
Get-WmiObject -Class Win32_LogicalDisk -ComputerName $server |
Where-Object {$_.DriveType -eq 3} |
Select-Object DeviceID, Description,`
#{"Label"="DiskSize(GB)";"Expression"={"{0:N}" -f ($_.Size/1GB) -as [float]}}, `
#{"Label"="FreeSpace(GB)";"Expression"={"{0:N}" -f ($_.FreeSpace/1GB) -as [float]}} |
FT -AutoSize
}
$server + ' | ' + $SYSNAME + ' | ' + $OS + ' | '
}
Write-Output "`n End of Hal0";
Results:
Start of Hal0
svr01.xxx.com | SVR01 SVR01 SVR01 SVR01 | Mic
rosoft Windows 8.1 Enterprise |
svr03.xxx.com | SVR03 SVR03 SVR03 SVR03 | Mic
rosoft Windows 8.1 Enterprise |
svr05.xxx.com | SVR05 SVR05 SVR05 SVR05 | Mic
rosoft Windows 8.1 Enterprise |
svr06.xxx.com | SVR06 SVR06 SVR06 SVR06 | Mic
rosoft Windows 8.1 Enterprise |
svr08.xxx.com | SVR08 SVR08 SVR08 SVR08 | Mic
rosoft Windows 8.1 Enterprise |
End of Hal0
What I was hoping my result would be a clean System Full Name, System Short Name, OS, Hard Drive Free/Used Space for each of the 5 servers.
svr08.xxx.com | svr08 | Windows 8 | C: 1111 MB Free/500 MB Used, E: 11 MB Free/10 MB Used.
You never output $DRIVE anywhere, and the expression for $DRIVE shouldn't be in a scriptblock in the first place. The computer name is repeated several times, because you get the SystemName property for each logical disk object. Also, $OS gets the OS name for the local computer, not the remote computer.
Change your code to something like this:
$serversList | ForEach-Object {
$os = Get-WmiObject -Class Win32_OperatingSystem -Computer $_
$disks = Get-WmiObject -Class Win32_LogicalDisk -Computer $_ |
Where-Object {$_.DriveType -eq 3} |
ForEach-Object {
'{0} {1:D} MB Free/{2:D} MB Used' -f $_.DeviceID,
[int]($_.FreeSpace/1GB), [int]($_.Size/1GB)
}
New-Object -Type PSCustomObject -Property #{
'FQDN' = $_
'ServerName' = $os.PSComputerName
'OperatingSystem' = $os.Caption
'Disks' = $disks -join ', '
}
} | Export-Csv 'C:\output.csv' -Delimiter '|' -NoType
If you want the output echoed instead of written to a file use ConvertTo-Csv instead of Export-Csv.
Addendum: If you want to import the output file in a database use commas as the field separators for the CSV and join the disk information with some other character:
$serversList | ForEach-Object {
...
New-Object -Type PSCustomObject -Property #{
'FQDN' = $_
'ServerName' = $os.PSComputerName
'OperatingSystem' = $os.Caption
'Disks' = $disks -join '|'
}
} | Export-Csv 'C:\output.csv' -NoType

How to format the output so that they are all aligned in the .log file?

I am working on a script for my college assignment that basically gathers your computer information and outputs it to a .log file. I've written the script already but when it outputs the information to the .log file, the Installed Software Names, Installed Software GUIDs, and the name of all users in the computer are listed like this:
But I want it to look like this:
Anyway I can edit my script to make it like this? Here's my script:
#Checking For Log File
$LogLocation = "$Home\Desktop\"
$LogFile = "Baabbasi.log"
$TestPathResult = Test-Path $Home\Desktop\Baabbasi.log
If ($TestPathResult -eq "False") {New-Item -Path $LogLocation -Name $LogFile -ItemType File}
#The Process After
Clear-Host
$TodaysDate = Get-Date
$ComputerName = $env:ComputerName
$BiosName = Get-WMIObject Win32_BIOS | Select-Object -ExpandProperty Name
$BiosVersion = Get-WMIObject Win32_BIOS | Select-Object -ExpandProperty Version
$HDSizes = Get-WMIObject Win32_LogicalDisk -filter "DriveType=3" | Select-Object #{Name="size(GB)";Expression={"{0:N2}" -f($_.size/1gb)}}
$TotalHDSize = ($HDSizes | Measure-Object "size(GB)" -Sum).Sum
$PhysicalMemory = (Get-WMIObject Win32_PhysicalMemory).Capacity
$PhysicalMemoryinGB = $PhysicalMemory/1gb
$OSVersion = (Get-WmiObject -Class Win32_OperatingSystem).Version
$OSName = $env:OS
$InstalledSoftwareNames = Get-WMIObject Win32_Product | Select-Object -ExpandProperty Name | Out-String
$InstalledSoftwareGUID = Get-WMIObject Win32_Product | Select-Object -ExpandProperty IdentifyingNumber| Out-String
$LatestHotfix = Get-Hotfix | select-object HotFixID,InstalledOn | Sort-Object InstalledON -descending | Select -first 1 | Select-Object -ExpandProperty HotfixID
$UserAccount = [Environment]::UserName
$AllUserAccounts = Get-WmiObject Win32_UserAccount | Select-Object -ExpandProperty Name | Out-String
Add-Content $Home\Desktop\Baabbasi.log "
Date: $TodaysDate
======================================================================
Computer Name: $ComputerName
================ ======================================================
BIOS Name: $BiosName
BIOS Version: $BiosVersion
HD Size: $TotalHDSize GB
RAM Size: $PhysicalMemoryinGB GB
Operating System: $OSName
Operating System Version: $OSVersion
Installed Software Name:
$InstalledSoftwareNames
Installed Software GUID:
$InstalledSoftwareGUID
Last Installed Hot Fix: $LatestHotfix
Name of Registered System User: $UserAccount
Names of All Registered System Users on the System:
$AlluserAccounts
========================================================================
========================================================================
"
Change the statement
$InstalledSoftwareGUID = Get-WMIObject Win32_Product |
Select-Object -ExpandProperty IdentifyingNumber| Out-String
to something like this:
$InstalledSoftwareGUID = Get-WMIObject Win32_Product |
Select-Object -ExpandProperty IdentifyingNumber |
% { (' ' * 20) + $_ } | Out-String
That will prepend each GUID with 20 spaces (adjust the number to your desired indention depth) before converting the list to a single string.

Get the drive letter of USB drive in PowerShell

I've seen articles in C# and some other languages that explain how to achieve what I'm looking for but I don't know how to convert them.
The following link explains how to get the answer:
How can I get the drive letter of an USB device?
Win32_DiskDrive-> Win32_DiskDriveToDiskPartition -> Win32_DiskPartition -> Win32_LogicalDiskToPartition -> Win32_LogicalDisk
The answer by GEOCHET explains also explains how to achieve the answer but again, not in PowerShell: How to find USB drive letter?
Try:
gwmi win32_diskdrive | ?{$_.interfacetype -eq "USB"} | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskDrive.DeviceID=`"$($_.DeviceID.replace('\','\\'))`"} WHERE AssocClass = Win32_DiskDriveToDiskPartition"} | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=`"$($_.DeviceID)`"} WHERE AssocClass = Win32_LogicalDiskToPartition"} | %{$_.deviceid}
Tested with one and more than one USB device plugged-in.
I know the subject has been dropped for a while, but since it's something I come back to pretty often, I thought I'd update things a bit.
If using Windows 7 and above, a much simpler solution would be:
Get-WmiObject Win32_Volume -Filter "DriveType='2'"
And if you want to avoid magic numbers:
Get-WmiObject Win32_Volume -Filter ("DriveType={0}" -f [int][System.IO.DriveType]::Removable)
References:
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/vdswmi/win32-volume
https://learn.microsoft.com/en-us/dotnet/api/system.io.drivetype
get-volume | where drivetype -eq removable | foreach driveletter
volume | ? drivetype -eq removable | % driveletter
Beginning with PowerShell v3.0, Microsoft introduce the Get-Cim* commands which make this easier than the ugliness of the Get-WmiObject ASSOCIATORS query method:
Get-CimInstance -Class Win32_DiskDrive -Filter 'InterfaceType = "USB"' -KeyOnly |
Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition -KeyOnly |
Get-CimAssociatedInstance -ResultClassName Win32_LogicalDisk |
Format-List *
Or:
Get-CimInstance -Class Win32_DiskDrive -Filter 'InterfaceType = "USB"' -KeyOnly |
Get-CimAssociatedInstance -Association Win32_DiskDriveToDiskPartition -KeyOnly |
Get-CimAssociatedInstance -Association Win32_LogicalDiskToPartition |
Format-List *
The above commands are equivalent.
$FlashDrives = (get-volume | Where-Object drivetype -eq removable).DriveLetter
foreach($elment in $FlashDrives)
{
if($null -eq $elment)
{
}
else
{
$FlashDriveLetter = $elment + ":\"
Write-Host $FlashDriveLetter
}
}
$HardDiskDrives = (get-volume | Sort-Object -Property DriveLetter | Where-Object drivetype -eq Fixed).DriveLetter
foreach($elment in $HardDiskDrives)
{
if($null -eq $elment)
{
}
else
{
$HardDiskDriveLetter = $elment + ":\"
Write-Host $HardDiskDriveLetter
}
}