I am creating a menu and have a option that displays the computer name and os version and a option that displays the disk space. I need to have the options to be done on a local or a remote machine. It works on the local machine but I am not sure how to allow the options to be done on a remote machine
'1' {
cls
Write-Host -NoNewLine "OS Version: "
Get-CimInstance Win32_OperatingSystem | Select-Object Caption | ForEach{ $_.Caption }
Write-Host ""
Write-Host -NoNewLine "Computer Name: "
Get-CimInstance Win32_OperatingSystem | Select-Object CSName | ForEach{ $_.CSName }
Write-Host ""
} '2' {
cls
gwmi win32_logicaldisk | Format-Table DeviceId, MediaType, #{n="Size";e={[math]::Round($_.Size/1GB,2)}},#{n="FreeSpace";e={[math]::Round($_.FreeSpace/1GB,2)}}
Since you have already mentioned that it is working in your local, I am not touching that part. Just wrap that entire code as a Function with $ComputerName as Parameters.
If you are in a domain admin or if you do not have any permission issue, then with the same credentials you can fetch the information remotely.
Use like this:
function Get-DiskInfo($drive,$computername)
{
##-- Local system Validation
if((gwmi win32_computersystem).Name -eq $computername)
{
$diskReport = Get-WmiObject Win32_logicaldisk
$drive = $diskReport | Where-Object { $_.DeviceID -eq $drive}
$result = #{
Size = $drive.Size
FreeSpace = $drive.Freespace
}
return $result
}
else
{
$diskReport = Get-WmiObject Win32_logicaldisk -ComputerName $ComputerName
$drive = $diskReport | Where-Object { $_.DeviceID -eq $drive}
$result = #{
Size = $drive.Size
FreeSpace = $drive.Freespace
}
return $result
}
}
Call the function:
Get-DiskInfo C: localhost
Since you are using all WMI calls , you do not need to use Invoke-Command.
You can directly use the -ComputerName switch present in the get-wmiobject to fetch all the details remotely and you can pass the specific credentials also as -credential
Hope it helps you understand the logic.
Related
Cmdlet below works normally, but does nothing within a do & switch statement in code block at bottom? Debugging in ISE doesn't provide any help. Removing | Select-Object does make it function, but produces too much info. Removing -CimSession $hostname does make it function. So issue seems related to the remote PC and/or SELECT statement.
Get-CimInstance Win32_UserProfile -CimSession $hostname | Select-Object -Property LocalPath, LastUseTime
function Show-Menu {
Write-Host "
1)Option A
2)Option B
3)User Profiles of Remote PC
"}
DO {Show-Menu
$UserChoice = Read-Host "Enter # of tool you want to run"
$hostname=Read-Host "enter hostname"
switch ($UserChoice) {
1 {'You choose opt1'}
2 {'You choose opt2'}
3 {Get-CimInstance Win32_UserProfile -CimSession $hostname | Select-Object -Property LocalPath, LastUseTime}
}
} UNTIL ($hostname -eq '')
Same issue with this cmdlet: {Get-WMIObject Win32_UserProfile -ComputerName $hostname | Select-Object -Property LocalPath,LastUseTime}
Works, but is spaced funny: {Get-WMIObject Win32_UserProfile -ComputerName $hostname | Format-List LocalPath, LastUseTime}
Works, but is spaced funny & has weird runspaceID item: {Invoke-Command -ComputerName $hostname -HideComputerName -ScriptBlock {Get-WMIObject Win32_UserProfile | Select-Object LocalPath, LastUseTime}}
Your code has a syntax error and is missing a curly bracket to close the switch. Mixing text output with object output causes problems in powershell. For example, this works fine.
function Show-Menu {Write-Host "
1)Option A
2)Option B
3)User Profiles of Remote PC
"}
DO {
# Show-Menu
# $UserChoice = Read-Host "Enter # of tool you want to run"
# $hostname=Read-Host "enter hostname"
$hostname = 'comp001'
switch (3) {
1 {'You choose opt1'}
2 {'You choose opt2'}
3 {Get-CimInstance Win32_UserProfile -CimSession $hostname |
Select-Object -Property LocalPath, LastUseTime}
}
} UNTIL (1)
As I mentioned, theres no cimsession thats been established for you to point to. So, lets create it using New-CimSession and the computer name provided in $hostname.
function Show-Menu
{
Write-Host "
1)Option A
2)Option B
3)User Profiles of Remote PC
"
}
Do {
Show-Menu
$User_Choice = Read-Host -Prompt "Enter # of tool you want to run"
switch ($User_Choice) {
1 {'You choose opt1'}
2 {'You choose opt2'}
3 {
$hostname = Read-Host -Prompt "Enter Computer Name"
if ([string]::IsNullOrEmpty($hostname) -eq $true) {
"No Computer Name was specified";
Break
}
try {
$CIMSession = New-CimSession -ComputerName $hostname -ErrorAction stop
Get-CimInstance -ClassName Win32_UserProfile -CimSession $CIMSession | Select-Object -Property LocalPath, LastUseTime
}
Catch [Microsoft.Management.Infrastructure.CimException] {
$Error[0].Message.Split('.')[1].Trim()
}
Finally {
if (Get-CimSession) {
Get-CimSession | Remove-CimSession
}
}
}
}
} Until ($User_Choice -notcontains '')
Besides some minor syntax issues, you should have the $hostname prompt inside your #3 selection. Unless, you'd like to use that variable for the other selections as well. And of course, you want some error handling in case an error occurs connecting to the machine which we can do with a try{} and catch{} block; added a finally{} block for cimsessions clean up.
I was trying to take advantage of CIM's built-in parallel processing to get information about all the installed printers against a given subnet of computers. The script works faster than my WMI variation, but doesn't return the same information and doesn't always return as much as the Get-WmiObject call.
EDIT: The information the script drops is information about entire computers.
Here's the CIM version:
$Computer = Get-Content -Path c:\Scripts\input.txt
$Objects = foreach ($ComputerName in $Computer) {
# New CIM Instance
Write-Host Collecting information on $ComputerName
$Cim = New-CimSession -ComputerName $ComputerName
# Collect Printer Info
Get-CimInstance -CimSession $Cim -Class Win32_printer -Property deviceid, drivername, portname, systemName
# Define Hashtable properties
$ObjectProperties = #{
SystemName = $Cim.systemName
DeviceID = $Cim.deviceid
DriverName = $Cim.drivername
PortName = $Cim.portname
}
# Create new object
New-Object PSObject -Property $ObjectProperties
}
# Export Results
$Objects | Select DeviceID, DriverName, PortName, SystemName |
Export-Csv - NoTypeInformation -Path c:\Scripts\output.csv
Here's the WMI version:
$results = #()
$Computer = Get-Content -Path c:\Scripts\input.txt
# Check each computer in the list
foreach ($ComputerName in $Computer) {
$results += Get-WmiObject -Class Win32_printer -cn $ComputerName |
Select deviceid, drivername, portname, systemName
Start-Sleep -Milliseconds 500
}
# Export to CSV file
$Results | Select DeviceID, DriverName, PortName, SystemName |
Export-Csv -NoTypeInformation -Path c:\Scripts\output.csv
We sometimes need to run this script against multiple subnets. I moved to the CIM sessions because it reduced the total run of the script to consistently under 5 minutes, but if it's not going to return all of the information, it might be better to wait.
Does anyone have any idea on how to prevent CIM from dropping information?
It should be noted that WinRM is not enabled by default on these machines and the script has to force enable CIMs with the following command.
& "c:\Scripts\SnIPT\psexec.exe" \\$ComputerName -s -d -n 5 winrm.cmd quickconfig -q -force
The same WMI-class should return the same data (however CIM-cmdlets convert dates ++). Since you haven't explained what's different I'd guess it's missing output for certain computers. Usually this is because the target computer is missing Windows Management Framework 3.0 or later (think PS 3.0+) which is required for CIM. If that's the case, it should generate an error that you can catch and use to use DCOM (same as WMI) as a fallback. Ex:
$Computer = Get-Content -Path c:\Scripts\input.txt
$DCOM = New-CimSessionOption -Protocol Dcom
$Objects = ForEach($ComputerName in $Computer)
{
#New Cim Instance with fallback to DCOM
Write-Host Collecting information on $ComputerName
$Cim = $null
try {
$Cim = New-CimSession -ComputerName $ComputerName -ErrorAction Stop
} catch [Microsoft.Management.Infrastructure.CimException] {
#CIM not available on target (requires WMF 3.0+). Using DCOM (used by WMI)
try { $Cim = New-CimSession -ComputerName $ComputerName -SessionOption $DCOM -ErrorAction Stop }
catch { Write-Host $_.Exception.Message }
}
#Collect Printer Info
Get-CimInstance -CimSession $Cim -Class Win32_printer -Property DeviceID, DriverName, PortName, SystemName
#Best practice to store the original object.
#No need to create a new one with a few properties when you do it during export anyways.
#If you really need it, add "| Select-Object -Property DeviceID, DriverName, PortName, SystemName" to the previous line
}
#Export Results
$Objects | Select-Object -Property DeviceID, DriverName, PortName, SystemName | Export-Csv - NoTypeInformation -Path c:\Scripts\output.csv
I am using Invoke-WMIMethod to identify all SIDS beginning with S-1-5-21, like so (thanks to Mathias R. Jessen):
$Keys = Invoke-WmiMethod -Path $ClassPath -Name EnumKey -ArgumentList 2147483651,''
| Select-Object -ExpandProperty sNames | Where-Object {$_ -match 'S-1-5-21-[\d\-]+$'}
I want to convert these SIDs from the remote system to usernames on the remote system using WMI. Is this possible through WMI or Invoke-WmiMethod?
You can use the Win32_SID class to obtain the account name:
foreach($Key in $Keys)
{
$SID = [wmi]"\\$RemoteComputer\root\cimv2:Win32_SID.SID=$Key"
New-Object psobject -Property #{
SID = $Key
Username = $SID.ReferencedDomainName,$SID.AccountName -join '\'
}
}
Rather than grabbing from the registry you could get the same information from the Win32_UserProfile provider. Then if folder name is good enough, consider something like this:
$Computer = "ExampleComputer"
Get-WMIObject Win32_UserProfile -Filter "SID like 'S-1-5-21-*'" -ComputerName $Computer |
select SID,#{name=LocalPath;Expression={Split-Path -leaf $_.LocalPath}}
Otherwise Win32_UserAccount exists but can be really slow with a large domain.
$Computer = "ExampleComputer"
$SIDs = Get-WMIObject Win32_UserProfile -Filter "SID like 'S-1-5-21-*'" -ComputerName $Computer | select -ExpandProperty SID
$UserAccounts = Get-WMIObject Win32_UserAccount -ComputerName $Computer
foreach ($SID in $SIDs) {
foreach ($Account in $UserAccounts) {
If ($SID -eq $Account.SID) {
$Account
}
}
}
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
}
}
}
}
I'm trying to modify a script that is supposed to list out all the computers within a domain, and if there is someone logged in on said computer it should show the username for the account.
The script works fine, but i got a little esthetic problem. Is there a way to turn the return data (for those computers and servers thats online) into another color?
Here is my current script:
function Check-Domain {
Get-ADComputer -Filter * |
Select-Object -ExpandProperty Name |
ForEach-Object {
$computer = $_
$pingme = Test-Connection -ComputerName $computer -Quiet -Count 1
if ($pingme -eq $true) {
Invoke-Command -ComputerName $computer -ScriptBlock {
Get-WmiObject Win32_ComputerSystem |
Select-Object Username, Name }
} else {
Write-Host "$computer - OFF" -ForegroundColor Red
}
} | Format-Table
}
Sure, that's easy enough. Just wrap the command up in a sub-expression and use Write-Host just like you do with the offline servers.
{Write-Host $(Invoke-Command -ComputerName $computer -ScriptBlock { Get-WmiObject win32_computersystem | Select-Object username, name}) -ForegroundColor Green}
It'll execute the script inside the $() first, and then apply it's output to the Write-Host so that you can format it as desired. For output consistency, especially when it's just outputting text to the host, I personally like to use formatted strings. When used in combination with the -NoNewLine switch for Write-Host you can get some really sharp looking results.
You can edit this part - this stores the result of Invoke-Command in a variable, and outputs it with the color you like with Write-Host:
if ($pingme -eq $true) {
$result = Invoke-Command -ComputerName $computer -ScriptBlock {
Get-WmiObject Win32_ComputerSystem | Select-Object Username, Name
}
Write-Host $result -ForegroundColor Green
}