VMWare VMDK mapping to Windows Drive Letters - windows

this code almost works, it maps VWare VMDK to windows drives. The code is not mine.
Among other info it will return "DD-SERV-01_15.vmdk for VM MAIN is Drive letter G:"
The script will prompt for credentials and proceeds to map however something goes wrong and only the last VM / Drive is is saved as output - I was hoping someone could take a look and update / fix the code so that it saves all output please?
Thanks.
#Get VMware Disk Usage
# Created by Hugo Peeters
# http://www.peetersonline.nl
# VARIABLES
$Decimals = 1
$VCServer = "SERVERNAME"
# SCRIPT
# Connect to VC
Write-Progress "Gathering Information" "Connecting to Virtual Center" -Id 0
$VC = Connect-VIServer $VCServer
# Create Output Collection
$myCol = #()
# List Datastores (Datastore Name)
Write-Progress "Gathering Information" "Listing Datastores" -Id 0
$Datastores = Get-Datastore | Sort Name
# List vms
Write-Progress "Gathering Information" "Listing VMs and Disk Files" -Id 0
$VMSummaries = #()
ForEach ($vm in (Get-VM))
{
$VMView = $VM | Get-View
ForEach ($VirtualSCSIController in ($VMView.Config.Hardware.Device | Where {$_.DeviceInfo.Label -match "SCSI Controller"}))
{
ForEach ($VirtualDiskDevice in ($VMView.Config.Hardware.Device | Where {$_.ControllerKey -eq $VirtualSCSIController.Key}))
{
$VMSummary = "" | Select VM, HostName, PowerState, DiskFile, DiskName, DiskSize, SCSIController, SCSITarget
$VMSummary.VM = $VM.Name
$VMSummary.HostName = $VMView.Guest.HostName
$VMSummary.PowerState = $VM.PowerState
$VMSummary.DiskFile = $VirtualDiskDevice.Backing.FileName
$VMSummary.DiskName = $VirtualDiskDevice.DeviceInfo.Label
$VMSummary.DiskSize = $VirtualDiskDevice.CapacityInKB * 1KB
$VMSummary.SCSIController = $VirtualSCSIController.BusNumber
$VMSummary.SCSITarget = $VirtualDiskDevice.UnitNumber
$VMSummaries += $VMSummary
}
}
Clear-Variable VMView -ErrorAction SilentlyContinue
}
# Loop through Datastores
ForEach ($Datastore in $Datastores)
{
# List vmdk files in datastore (vmdk Name)
Write-Progress "Gathering Information" ("Processing Datastore {0}" -f $Datastore.Name) -Id 0
$DSView = $Datastore | Get-View
$fileQueryFlags = New-Object VMware.Vim.FileQueryFlags
$fileQueryFlags.FileSize = $true
$fileQueryFlags.FileType = $true
$fileQueryFlags.Modification = $true
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
$searchSpec.details = $fileQueryFlags
$searchSpec.sortFoldersFirst = $true
$dsBrowser = Get-View $DSView.browser
$rootPath = "["+$DSView.summary.Name+"]"
$searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
ForEach ($result in $searchResult)
{
ForEach ($vmdk in ($result.File | ?{$_.Path -like "*.vmdk"} | Sort Path))
{
Write-Progress "Gathering Information" ("Processing VMDK {0}" -f $vmdk.Path) -Id 1
Write-Host "=============================================================================="
# Find vm using the vmdk (VM Name)
$VMRef = ($VMSummaries | ?{$_.DiskFile -match $Datastore.Name -and $_.DiskFile -match $vmdk.Path})
"VMDK {0} belongs to VM {1}" -f $vmdk.Path, $VMRef.VM
If ($VMRef.Powerstate -eq "PoweredOn")
{
Write-Host "VM is powered on" -ForegroundColor "yellow"
$Partitions = Get-WmiObject -Class Win32_DiskPartition -ComputerName $VMRef.HostName
If ($?)
{
$Disks = Get-WmiObject -Class Win32_DiskDrive -ComputerName $VMRef.HostName
$LogicalDisks = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $VMRef.HostName
$DiskToPartition = Get-WmiObject -Class Win32_DiskDriveToDiskPartition -ComputerName $VMRef.HostName
$LogicalDiskToPartition = Get-WmiObject -Class Win32_LogicalDiskToPartition -ComputerName $VMRef.HostName
Write-Host "Read partition and disk information" -ForegroundColor "yellow"
# Match disk based on SCSI ID's
$DiskMatch = $Disks | ?{($_.SCSIPort - 1) -eq $VMRef.SCSIController -and $_.SCSITargetID -eq $VMRef.SCSITarget}
If ($DiskMatch -eq $null){Write-Warning "NO MATCHES!"}
Else
{
Write-Host "Found match:" -ForegroundColor "yellow"
$DiskMatch
# Find the Partition(s) on this disk
$PartitionsOnDisk = ($DiskToPartition | ?{$_.Antecedent -eq $DiskMatch.__PATH})
If ($PartitionsOnDisk -eq $null){Write-Warning "NO PARTITIONS!"}
Else
{
ForEach ($PartitionOnDisk in $PartitionsOnDisk)
{
Write-Host "Disk contains partition" -ForegroundColor "yellow"
$PartitionOnDisk.Dependent
$PartitionMatches = $Partitions | ?{$_.__PATH -eq $PartitionOnDisk.Dependent}
ForEach ($PartitionMatch in $PartitionMatches)
{
$LogicalDiskRefs = $LogicalDiskToPartition | ?{$_.Antecedent -eq $PartitionMatch.__PATH}
If ($LogicalDiskRefs -eq $null)
{
Write-Warning "NO LOGICAL DISKS!"
}
Else
{
ForEach ($LogicalDiskRef in $LogicalDiskRefs)
{
$LogicalDiskMatches = $LogicalDisks | ?{$_.__PATH -eq $LogicalDiskRef.Dependent}
ForEach ($LogicalDiskMatch in $LogicalDiskMatches)
{
Write-Host "Matching Logical Disk:" -ForegroundColor "yellow"
$LogicalDiskMatch
# Create Output Object
$myObj = "" | Select Datastore, DSSizeGB, DSFreeGB, DSPercentFree, DiskFile, VM, HardDisk, DriveLetter, DiskSizeGB, DiskFreeGB, PercFree
# List datastore name
$myObj.Datastore = $Datastore.Name
# Determine datastore size in GB
$myObj.DSSizeGB = [Math]::Round(($Datastore.CapacityMB * 1MB / 1GB),$Decimals)
$myObj.DSFreeGB = [Math]::Round(($Datastore.FreeSpaceMB * 1MB / 1GB),$Decimals)
# Determine datastore free space (DS%Free)
$myObj.DSPercentFree = [Math]::Round((100*($Datastore.FreeSpaceMB/$Datastore.CapacityMB)),$Decimals)
# List disk file name
$myObj.DiskFile = $vmdk.Path
# List VM Name
$myObj.VM = $VMRef.VM
# Determine virtual hard disk / logical drive
$myObj.HardDisk = $VMRef.DiskName
# Report driveletter
$myObj.DriveLetter = $LogicalDiskMatch.DeviceID
# Report Size
$myObj.DiskSizeGB = [Math]::Round(($LogicalDiskMatch.Size / 1GB),$Decimals)
# Report Free Space
$myObj.DiskFreeGB = [Math]::Round(($LogicalDiskMatch.FreeSpace / 1GB),$Decimals)
# Calculate Percentage free space
$myObj.PercFree = [Math]::Round((100 * ([int]($LogicalDiskMatch.FreeSpace / 1MB) / [int]($LogicalDiskMatch.Size / 1MB))),$Decimals)
Write-Host "RESULT:" -ForegroundColor "yellow"
$myObj
# Add output object to output collection
$myCol += $myObj
}
Clear-Variable LogicalDiskMatches -ErrorAction SilentlyContinue
}
}
Clear-Variable LogicalDiskRefs -ErrorAction SilentlyContinue
}
Clear-Variable PartitionMatches -ErrorAction SilentlyContinue
}
}
Clear-Variable PartitionsOnDisk -ErrorAction SilentlyContinue
}
Clear-Variable DiskMatch -ErrorAction SilentlyContinue
Clear-Variable Disks -ErrorAction SilentlyContinue
Clear-Variable LogicalDisks -ErrorAction SilentlyContinue
Clear-Variable DiskToPartition -ErrorAction SilentlyContinue
Clear-Variable LogicalDiskToPartition -ErrorAction SilentlyContinue
}
Clear-Variable Partitions -ErrorAction SilentlyContinue
}
Else
{
Write-Host "VM is powered off" -ForegroundColor "yellow"
}
Clear-Variable VMRef -ErrorAction SilentlyContinue
Write-Progress "Gathering Information" ("Processing VMDK {0}" -f $vmdk.Path) -Id 1 -Completed
}
}
}
# Disconnect from VC
Disconnect-VIServer -Confirm:$False
# OUTPUT
Write-Host "==================================================="
Write-Host "==================================================="
$TotalDSFree = ($myCol | Select Datastore, DSFreeGB -Unique | Measure-Object DSFreeGB -Sum).Sum
$TotalDSSize = ($myCol | Select Datastore, DSSizeGB -Unique | Measure-Object DSSizeGB -Sum).Sum
$AverageDSFree = [Math]::Round(100 * ($TotalDSFree / $TotalDSSize),$Decimals)
$AverageDiskFree = [Math]::Round(100 * (($myCol | Measure-Object DiskFreeGB -Sum).Sum / ($myCol | Measure-Object DiskSizeGB -Sum).Sum),$Decimals)
Write-Host "Total DS Free: $TotalDSFree"
Write-Host "Total DS Size: $TotalDSSize"
Write-Host "Average DS Free Percentage: $AverageDSFree"
Write-Host "Average Disk Free Percentage: $AverageDiskFree"
$myCol | Export-Csv -NoTypeInformation 'C:\TEMP\VMwareDiskUsage.csv'

Try adding the -Append parameter to your export-csv, so it does not overwrite the last entry:
$myCol | Export-Csv -Append -NoTypeInformation 'C:\TEMP\VMwareDiskUsage.csv'

Related

How to collect system monitor information remotly using powershell?

I have code to collect system information remotely and create a csv file ,below,
Param(
[Parameter(Mandatory=$true, position=0)][string]$infile,
[Parameter(Mandatory=$true, position=1)][string]$outfile
)
#Column header in input CSV file that contains the host name
$ColumnHeader = "ComputerName"
$HostList = import-csv $infile | select-object $ColumnHeader
$out = #()
foreach($object in $HostList) {
$os = Get-WmiObject -computername $object.("ComputerName") -class win32_operatingsystem
$vol = Get-WmiObject -computername $object.("ComputerName") -class Win32_Volume
$net = Get-WmiObject -computername $object.("ComputerName") -class Win32_NetworkAdapterConfiguration | where-object { $_.IPAddress -ne $null }
$DeviceInfo= #{}
$DeviceInfo.add("Operating System", $os.name.split("|")[0])
$DeviceInfo.add("Version", $os.Version)
$DeviceInfo.add("Architecture", $os.OSArchitecture)
$DeviceInfo.add("Serial Number", $os.SerialNumber)
$DeviceInfo.add("Organization", $os.Organization)
$DeviceInfo.add("Disk Capacity", "$([math]::floor($vol.Capacity/ (1024 * 1024 * 1024 )) )" + " GB" )
$DeviceInfo.add("Free Capacity", "$([math]::floor($vol.FreeSpace/ (1024 * 1024 * 1024 )))" + " GB" )
$DeviceInfo.add("System Name", $vol.SystemName)
$DeviceInfo.add("File System", $vol.FileSystem)
$DeviceInfo.add("IP Address", ($net.IPAddress -join (", ")))
$DeviceInfo.add("Subnet", ($net.IPSubnet -join (", ")))
$DeviceInfo.add("MAC Address", $net.MACAddress )
$out += New-Object PSObject -Property $DeviceInfo | Select-Object `
"System Name", "Organization", "Serial Number","Operating System", `
"Version","Architecture","File System","Disk Capacity", `
"Free Capacity","MAC Address","IP Address","Subnet"
Write-Verbose ($out | Out-String) -Verbose
$out | Export-CSV $outfile -NoTypeInformation
}
and i have a script to get monitor information
function Decode {
If ($args[0] -is [System.Array]) {
[System.Text.Encoding]::ASCII.GetString($args[0])
}
Else {
"Not Found"
}
}
ForEach ($Monitor in Get-WmiObject WmiMonitorID -Namespace root\wmi) {
$Manufacturer = Decode $Monitor.ManufacturerName -notmatch 0
$Name = Decode $Monitor.UserFriendlyName -notmatch 0
$Serial = Decode $Monitor.SerialNumberID -notmatch 0
$ManufactureWeek = (Get-WmiObject WmiMonitorID -Namespace root\wmi).WeekofManufacture
$ManufactureYear = (Get-WmiObject WmiMonitorID -Namespace root\wmi).YearOfManufacture
echo "Manufacturer: $Manufacturer`nName: $Name`nSerial Number: $Serial"
echo "Week of Manufacture: $ManufactureWeek"
echo "Year of Manufacture: $ManufactureYear"
}
how can i combine these codes to get monitor information remotly,
how can i get monitor information remotely???????????
You may also update your monitor script. With more than one monitor, it will not work correctly.
function Decode {
If ($args[0] -is [System.Array]) {
[System.Text.Encoding]::ASCII.GetString($args[0])
}
Else {
"Not Found"
}
}
ForEach ($Monitor in Get-WmiObject WmiMonitorID -Namespace root\wmi) {
$Manufacturer = Decode $Monitor.ManufacturerName -notmatch 0
$Name = Decode $Monitor.UserFriendlyName -notmatch 0
$Serial = Decode $Monitor.SerialNumberID -notmatch 0
$ManufactureWeek = $Monitor.WeekofManufacture
$ManufactureYear = $Monitor.YearOfManufacture
echo "Manufacturer: $Manufacturer`nName: $Name`nSerial Number: $Serial"
echo "Week of Manufacture: $ManufactureWeek"
echo "Year of Manufacture: $ManufactureYear"
}

Export a list of BitLocker Devices on AD

Im trying extract a report from AD of a list of devices that have BitLocker enabled.
We have a Win 2008 r2 Domain Controller and most of our devices are Win 10 with a few Win 8.1 in the mix.
I'm no expert in power shell but have used it in the past on an amateur level. I found the following command online and tried it but when viewing the .CSV all fields are populated except for the "BitlockerPasswordSet" field.
Does anyone have any ideas on how to fix this or better yet a solution they have used that works?
Thanks in advance!
Param (
[string]$SearchBase = "OU=Office-UK,DC=MyDomainName,DC=local"
)
Try { Import-Module ActiveDirectory -ErrorAction Stop }
Catch { Write-Warning "Unable to load Active Directory module because $($Error[0])"; Exit }
Write-Verbose "Getting Workstations..." -Verbose
$Computers = Get-ADComputer -Filter * -SearchBase $SearchBase -Properties LastLogonDate
$Count = 1
$Results = ForEach ($Computer in $Computers)
{
Write-Progress -Id 0 -Activity "Searching Computers for BitLocker" -Status "$Count of $($Computers.Count)" -PercentComplete (($Count / $Computers.Count) * 100)
New-Object PSObject -Property #{
ComputerName = $Computer.Name
LastLogonDate = $Computer.LastLogonDate
BitLockerPasswordSet = Get-ADObject -Filter "objectClass -eq 'msFVE-RecoveryInformation'" -SearchBase $Computer.distinguishedName -Properties msFVE-RecoveryPassword,whenCreated | Sort whenCreated -Descending | Select -First 1 | Select -ExpandProperty whenCreated
}
$Count ++
}
Write-Progress -Id 0 -Activity " " -Status " " -Completed
$ReportPath = "C:\temp\BitLockerComputerReport.csv"
Write-Verbose "Building the report..." -Verbose
$Results | Select ComputerName,LastLogonDate,BitLockerPasswordSet | Sort ComputerName | Export-Csv $ReportPath -NoTypeInformation
Write-Verbose "Report saved at: $ReportPath" -Verbose

Delete domain profiles older than x days

i need some help in order to complete the script, i have already script which deleting specific profiles on the remote server, but i want o add time frame. Example: if the profile was not used for more than 120 days delete it.
Function Get-OldProfiles {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)] [string]$computerName
)
PROCESS {
foreach ($computer in $computerName) {
Write-host -ForegroundColor Yellow "Housekeeping on $computer"
Write-host -ForegroundColor Yellow "Mapping drive \\$computer\c$"
$drive = New-PSDrive -Name $computer.replace(".","-") -PSProvider FileSystem -Root \\$computer\C$
Write-host -ForegroundColor Yellow "Checking windows version"
#Cheking windows version
$version = (Get-WmiObject -ComputerName $computer -Class Win32_OperatingSystem).version
Write-host -ForegroundColor Yellow "Windows version $version"
#Profile Deleting area.
if ($version -ge 6) {
Write-host -ForegroundColor Yellow "Getting profiles from WMI"
$profiles = Get-WmiObject -ComputerName $computer Win32_UserProfile -filter "LocalPath Like 'C:\\Users\\%'" | Where-object localpath -Match 'B.{5}R$'| Select-Object {$_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-2)}
if ($profiles -ne $null) {
$profiles | foreach {
Write-host -ForegroundColor Red ("Deleting profile: " + $_.LocalPath)
#$_.Delete()
}
}
}
}
}
}
I've tried this:
$profiles= Get-WmiObject -ComputerName $computer -class Win32_UserProfile -filter "Special = False -and LocalPath Like 'C:\\Users\\%'" | Where-object localpath -Match 'B.{5}R$' | Where {$_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-120)}
But it returns an error
Get-WmiObject : Invalid query "select * from Win32_UserProfile where Special = False -and LocalPath Like 'C:\\Users\\%'"
At line:22 char:24 + $profiles= Get-WmiObject -ComputerName $computer -class Win32_UserPr ...
You can't use -and in a WQL-filter, it's just AND. Try:
$profiles = Get-WmiObject -ComputerName $computer -Class Win32_UserProfile -filter "Special = False AND LocalPath Like 'C:\\Users\\%'" |
Where-Object { ($_.localpath -Match 'B.{5}R$') -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-120)) }

Get Size, location and name of each shared folder

I have this code that generates a list of all the shares and the size however can not generate a txt with this information or the shared location
$servers = #("servername")
$sizes = #()
foreach($server in $servers) {
write-host "Server: $server"
(gwmi -class Win32_Share -ComputerName $server -filter "Type = 0" |
% {
write-host " share: $($_.Name)"
$s = gci \\$server\$($_.Name) -recurse -force | Measure-Object -Property length -Sum
New-Object PSObject -property #{Name=$_.Name; Server=$server; TotalSize=$s.Sum }
})
}
And this not only shows me the size and generates txt size and can generate txt
Get-WmiObject Win32_share -computer server01 | FT "server01", path, name > ServerShares.txt
Get-WmiObject Win32_share -computer server02 | FT "server02", path, name >> ServerShares.txt
Someone could help me to create only one that does everything
In your New-Object you just need to add additional properties to get the information you want:
If you're not running PowerShell v3, remove [Ordered]
$servers = #("servername")
$sizes = #()
foreach($server in $servers)
{
write-host "Server: $server"
# Get all shares
$shares = Get-WmiObject -class Win32_Share -ComputerName $server -filter "Type = 0"
# go through each share
foreach($share in $shares)
{
write-host " share: $($share.Name)"
# Get size of share
$size = Get-ChildItem -Path "\\$server\$($_.Name)" -recurse -force | Measure-Object -Property length -Sum
# Create a new object to store information
New-Object PSObject -property ([ordered]#{
# Name of share
Name = $share.Name
# Share path
Path = $share.path
# What server share is on
Server = $server
# Total size of share
TotalSize = $size.Sum
# Change this path to where you want the file to be saved to
}) | Export-Csv -Path C:\ShareDetails.csv -NoTypeInformation -Append
}
}
I made a small revision to #Bluecakes response in order to use COM instead of .NET to capture the size information. This overcomes the path-length issues.
# Get size of share
# $size = Get-ChildItem -Path "$($share.Name)" -recurse -force | Measure-Object -Property length -Sum
$objFSO = New-Object -com Scripting.FileSystemObject
$size = "{0:N2}" -f (($objFSO.GetFolder("$($share.Name)").Size) / 1MB)
Then you also need to remove ".sum"
# Total size of share
TotalSize = $size

Powershell determine the remote computer OS

I wrote a script to copy files to the "All Users" desktop or "Public Desktop"
However we have a mixed environment. Some people are using Windows XP and other people are using Windows 7.
$SOURCE = "I:\Path\To\Folder\*"
$DESTINATION7 = "c$\Users\Public\Desktop"
$DESTINATIONXP = "c$\Documents and Settings\All Users\Desktop"
$computerlist = Get-Content I:\Path\To\File\computer-list.csv
$results = #()
$filenotthere = #()
$filesremoved = #()
foreach ($computer in $computerlist) {
if((Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{
Write-Host "\\$computer\$DESTINATION\"
Copy-Item $SOURCE "\\$computer\$DESTINATION\" -Recurse -force
} else {
$details = #{
Date = get-date
ComputerName = $Computer
Destination = $Destination
}
$results += New-Object PSObject -Property $details
$results | export-csv -Path I:\Path\To\logs\offline.txt -NoTypeInformation -Append
}
}
DESTINATION is empty. Expanding on Keith's suggestion:
foreach ($computer in $computerlist) {
if((Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{
$OS = Get-WmiObject -Computer $computer -Class Win32_OperatingSystem
if($OS.caption -like '*Windows 7*'){
$DESTINATION = $DESTINATION7
}
if($OS.caption -like '*Windows XP*'){
$DESTINATION = $DESTINATIONXP
}
}
}
This could avoid the error you're getting also. empty $DESTINATION.
In your foreach loop through $computerlist you can grab the OS Caption for each computer by using WMI:
$OS = Get-WmiObject -Computer $computer -Class Win32_OperatingSystem
Ant then check the $OS
if($OS.caption -like '*Windows 7*'){
#Code here for Windows 7
}
#....
I had a slightly different goal...But thanks for the basics.
del C:\scripts\OS.csv
$computerlist = Get-Content c:\scripts\computerlist.csv
foreach ($computer in $computerlist) {
if((Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{
Get-WMIObject Win32_OperatingSystem -ComputerName $computer |
select-object CSName, Caption, CSDVersion, OSType, LastBootUpTime, ProductType| export-csv -Path C:\Scripts\OS.csv -NoTypeInformation -Append
}
}

Resources