How to determine storage type (SAN/NAS/local disk) remotely, using PowerShell? - powershell

I have to collect the attached storage types of each server in our environment: Several hundreds of W2K3/W2K8 servers.
A script would be very useful to determine if the attached storage is SAN / SAN mirrored / NAS / local or combination of these. The problem is that I haven't really found any good solution.
I was thinking about a script, and the best I could figure out would do something like the following:
If the server uses SAN, Veritas Storage Foundation is always installed, so I would search for it with gwmi win32_product. This is really slow, and this doesn't provide the information if the storage is SAN, or SAN mirrored.
If the attached storage is NAS, there must be an ISCSI target ip, and I would search for that somehow.
I really don't think these methods are acceptable though. Could you please help me find a better way to determine the attached storage types somehow?
Thank you very much

I found an article about accessing the VDS service in powershell. Getting More Information About You Cluster LUN’s
Massaged the code a bit to get type. Works even on 2003.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Storage.Vds") | Out-Null
$oVdsServiceLoader = New-Object Microsoft.Storage.Vds.ServiceLoader
$oVdsService = $oVdsServiceLoader.LoadService($null)
$oVdsService.WaitForServiceReady()
$oVdsService.Reenumerate()
$cDisks = ($oVdsService.Providers |% {$_.Packs}) |% {$_.Disks}
$cPacks = $oVdsService.Providers |% {$_.Packs}
foreach($oPack in $cPacks)
{
If($oPack.Status -eq "Online")
{
foreach($oDisk in $oPack.Disks)
{
Write-Host "$($oDisk.FriendlyName) ( $($oDisk.BusType) )"
}
foreach($oVolume in $oPack.Volumes)
{
Write-Host "`t$($oVolume.AccessPaths) ( $($oVolume.Label) )"
}
}
}

You could probably find the info in one of the following WMI classes:
Win32_LogicalDisk
http://msdn.microsoft.com/en-us/library/windows/desktop/aa394173(v=vs.85).aspx
Win32_Volume
http://msdn.microsoft.com/en-us/library/windows/desktop/aa394515(v=vs.85).aspx
Win32_DiskDrive
http://msdn.microsoft.com/en-us/library/windows/desktop/aa394132(v=vs.85).aspx
Then... do something like:
Get-AdComputer Server* | Foreach-Object { Get-WmiObject -Class Win32_DiskDrive -ComputerName $_.Name }

Related

How do you Stop / Start Services on 140+ servers quickly?

We have over 140+ servers and when we need to perform an upgrade we need to stop 10 different services on each server before we can perform an upgrade. What is the fastest way to turn off all the services we require on all the servers ?
Thanks
I was just searching for something similar and if this help you out man, that's great!
$Servers = Get-Content c:\serverlist.txt
Invoke-Command -ComputerName $Servers -ScriptBlock {
if ((Get-Service -Name BITS).Status -e "Running") {
Stop-Service -Name SERVICENAME
}
}
This would need a file name serverlist.txt with the names of the servers on it listed only one on each line and it being located in C:\, you can also name whatever you want and place it wherever, just change the path.

Looking for a Powershell Script to check if Volume Shadow Copy is enabled

Sorry for asking, new in PowerShell. Looking for a Powershell Script to check if Volume Shadow Copy is enabled.
Couldn't find any useful and functional script.
Where did you look?
There are a number samples of these all over the web.
For example, using the script downloadable from here:
Get Shadow Copy Statistics
If you use Shadow Copies of Shared Folders (Previous Versions), this
script may help you keep on eye on how much history you have, the
average snapshot size, whether you are hitting storage area limits or
the 64 shadow copies per volume limit.
https://gallery.technet.microsoft.com/scriptcenter/Get-Shadow-Copy-Statistics-79e05a57
You can use it's example to get stats of such items. Meaning, if you get any results, then of course it's enabled.
#Query the local machine
.\Get-ShadowCopyStats.ps1 -ServerName .
#Query a remote machine
.\Get-ShadowCopyStats.ps1 -ServerName FS01
#Query multiple remote machines by passing an array
.\Get-ShadowCopyStats.ps1 -ServerName FS01,FS02
#Since it's the first parameter, you don't have to include -ServerName in the command:
.\Get-ShadowCopyStats.ps1 FS01,FS02
#Query multiple remote machines by passing them to the script down the pipeline
"FS01","FS02" | .\Get-ShadowCopyStats.ps1
Get-ADComputer -Filter * -SearchBase "OU=Servers,DC=company,DC=tld" | .\Get-ShadowCopyStats.ps1 -ShowAllVolumes | Tee-Object -Variable ShadowCopyStats
$ShadowCopyStats | Select * | Export-Csv -NoTypeInformation .\ShadowCopyStats.csv
Or this one...
Get Remote Shadow Volume Information With Powershell
Gather the remote shadow volume information for one or more systems
using wmi, alternate credentials, and multiple runspaces. Function
supports custom timeout parameters in case of wmi problems and returns
shadow volume information, shadow copies, their providers, and
settings.
https://gallery.technet.microsoft.com/scriptcenter/Get-Remote-Shadow-Volume-e5a72619
RemoteShadowCopyInformation -ComputerName 'Server2' -Credential $cred).ShadowCopyVolumes
when the shadowcopy is enabled, there will be a scheduled task created
$allTasks = Get-ScheduledTask
foreach ($task in $allTasks) {
if ($task.TaskName.Contains("ShadowCopyVolume")) {
#get volumeid & drive letter which shadowcopy is enabled
$allVolumes = Get-Volume
foreach ($volume in $allVolumes) {
if ($volume.ObjectId.Contains(($task.TaskName.Split("{")[1]).Split("}")[0])) {
write-host ($volume.driveletter + ":\ is enabled")
}
}
}
}

Net view - get Just ' Share Name'

I need to get all of the shares name in some storage's.
Im using Net view $StorageName and it's show The result in a Table format :
Share name Type Used as Comment
----------------------------------------------------------------------------
Backups Disk
CallRecordings Disk
Download Disk System default share
home Disk Home
homes Disk System default share
Installs Disk
Justin Disk Copy of files from Justin laptop
michael Disk
Multimedia Disk System default share
Network Recycle Bin 1 Disk [RAID5 Disk Volume: Drive 1 2 3 4]
Public Disk System default share
Qsync Disk Qsync
Recordings Disk System default share
Sales Disk Sales Documents
SalesMechanix Disk
Server2012 Disk Windows Server 2012 Install Media
Usb Disk System default share
VMWareTemplates Disk
Web Disk System default share
The command completed successfully.
Thats good, but I need Just the Share Name.
I need Help please.
Thanke You!
Here is one way you could do it with the net view output:
(net view $StorageName | Where-Object { $_ -match '\sDisk\s' }) -replace '\s\s+', ',' | ForEach-Object{ ($_ -split ',')[0] }
Basically that is saying find the lines that have Disk surrounded by whitespace just in case something else might have Disk in the name. Then replace multiple spaces with a comma. Then, for each of those lines, split again by the comma and take the first value which would be the share name.
If you are on a Windows 8/2012 or newer system (and attempting to enumerate shares on other Windows systems), you could use a CIM session along with Get-SmbShare instead of net view which would return the results as objects and allow you to select the fields you want in the native PowerShell way.
For example:
$cimSession = New-CimSession $StorageName
Get-SmbShare -CimSession $cimSession | Select Name
Name
----
Admin
ADMIN$
C$
IPC$
J$
print$
Public
Software
Another option for parsing the net view output that relies on positioning rather than regular expression matching. personally I feel it's a bit easier to read and just as reliable.
function get-shares {
param($server)
$rawShares = net view \\$server
$shares = $rawShares[7..($s.Count - 3)]
$shares | . {process{$_.substring(0,($_.indexof(" ")))}}
}

How to make a powershell program restart on user input

Ok, so this is my first time posting on here and my first time writing PowerShell. My school computer maintenance and repair class requested from all the students that one of them create a PowerShell script that calculates a computers ram. The one I created calculates total physical ram, total ram usable by the user, and the modules in the computer and how much is on each module. However, after successfully coding it, I need a bit of advice as to tweaking the code so it can be use for my school.
The first part of my program opens up and talks about what each line means, follows by total physical ram, user accessible ram, and then the way the cards are set up. This leads right into a text that says to close the program. What I want to add in (I am a beginner at PowerShell by the way) is a way for the user to rerun the application if any of the variables from the program come up as zero (cause obviously the computer has ram of some sort if the computer is running). Right now its a Read-Host "Rerun memsrch ('y'/'n')?"
The other thing I want to add in is the ability for the user to select if the code is for the local computer or a distant machine. The user then could select the computer via IP or computer name. Below is the code I have now so everyone can see.
# Mesa Public Schools
$mps="Mesa Public Schools Information Technology Services"
$mps
# User Help
$print="The first section calculates your total physical memory,
the second line calculates the ram available to the user,
and the third line shows how the ram is divided up among
the ram cards.`n"
$print
#where I want to put a line of code to allow user to select if its local or remote
$ram = get-wmiobject win32_computersystem | select totalPhysicalMemory
Write-Host "Total usable RAM capacity"
$ramOutput = get-wmiobject win32_computersystem | select totalPhysicalMemory | foreach {$_.totalPhysicalMemory}
"RAM: " + "{0:N2}" -f ($ram.TotalPhysicalMemory/1GB) + "GB"
Get-WMIObject -class win32_physicalmemory | Format-Table devicelocator, capacity -a
Write-Host "Summary of System Memory"
Get-WmiObject -class Win32_PhysicalMemory | Measure-Object -Property Capacity -Sum
# Coded BY
$credits="Coded by Michael Meli"
$credits
#where I want to have the code reloop to the part of the code where
#you first select if the computer is local or remote.
Read-Host "Rerun memsrch (y/n)?"
I also have a bit of experience with HTML 4.01 and HTML 5 code, so I understand the basics of constructs and arguments, but aside from that a large part of powershell at the moment is above my head, so don't get to technical cause I don't want my brain to explode. :P Also note that the code if for computers running windows 8.1, but must be compatible with windows 7 as well. This also is not for a grade in my class either, it's extra credit.
If you wrap your code in a function, you will be able to call it again when you want. For instance, if the user input for the second question is y.
Store user input for the computer name or IP address, so you can use it in the WMI calls you make in the script, with the -ComputerName parameter
Example code:
function Show-MemoryReport {
#...
#where I want to put a line of code to allow user to select if its local or remote
#if computer name is null (first pass)
if($computerName -eq $null) {
#ask the user
$computerName = Read-Host "Enter computer name or IP, or leave blank for local"
#if the string is empty, use the local computer name
if($computerName -eq "") {
$computerName = $env:COMPUTERNAME
}
}
$ram = Get-WmiObject -ComputerName $computerName -Class Win32_Computersystem | Select-Object totalPhysicalMemory
#...
#where I want to have the code reloop to the part of the code where you first select if the computer is local or remote.
$rerun = Read-Host "Rerun report (y/n)?"
if($rerun -eq "y") { Show-MemoryReport }
}
#at first run, make sure computer name will be asked
$computerName = $null
#run report
Show-MemoryReport
After the first pass, $computerName will not be $null anymore.
Tip : you don't need to store a string in a variable to be able to output it. Just write it on a separate line like "print this on the screen" and it will be output.
For more information about PowerShell constructs and functions, you can read this and this

Powershell Remoting Speeding up a Foreach Loop Hosted Exchange

I have a CSV of email adddresses and Departments that I need to set on Live#edu. The command I currently have looks something like this:
Import-CSV departments.csv | ForEach-Object { Set-User $_.EmailAddress $_.Department }`
The problem is, this operation takes FOREVER.
My first thought is that it would be great to have the ForEach-Object command actually be forwarded over to the remote machine, so that it will only need to create the one pipeline between the two machines, but when I go into the PSSession, there doesn't seem to be any foreach-object available. For reference, How I Import the PSSession is:
Import-PSSession(New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri 'https://ps.outlook.com/powershell' `
-Credential (Get-Credential) `
-Authentication Basic -AllowRedirection)
Is there a better way that I can import the session to allow ForEach-Object to be remote, or to import an aliased version of the remote foreach-object, perhaps as ForEach-Object-Remote, or perhaps does anybody have something better to suggest to streamline this process?
UPDATE:
A Couple Things I've tried:
Using the -AsJob switch on the implicitly remoted command.
Import-CSV departments.csv | ForEach-Object { Set-User $_.EmailAddress $_.Department -AsJob }
This, unfortunately, doesn't work because there are throttling limits in place that don't allow the additional connections. Worse than that, I don't even know that anything went wrong until I check the results, and find that very few of them actually got changed.
Importing the ForEach-Object under a different name.
Turns out that adding a prefix is easy as putting -Prefix RS in the Import-PSSession Command to have things like the ForEach-Object from the Remote Session become ForEach-RSObject in the local session. Unfortunately, this won't work for me, because the server I'm connecting to does not does not have the Microsoft.Powershell ConfigurationName available to me.
UPDATE 2: The Set-User cmdlet seems to be Microsoft provided for Live#edu administration. Its purpose is to set User attributes. It is not a script or cmdlet that I am able to debug. It doesn't take pipeline input, unfortunately, so that would not be able to fix the issue.
As Far as I can tell, the problem is that it has to construct and tear down a pipeline to the remote machine every time this command runs, rather than being able to reuse it. The remote ForEach idea would have allowed me to offload that loop to avoid having to create all those remote pipelines, while the -asJob would have allowed them to all run in parallel. However, it also caused errors to fail silently, and only a few of the records actually get properly updated.
I suspect at this point that I will not be able to speed up this command, but will have to do whatever I can to limit the amount of data that needs to be changed in a particular run by keeping better track of what I have done before (keeping differential snapshots). Makes the job a bit harder.
EDIT: Start-Automate left a very useful help, unfortunately, neither of them work. It is my feeling at this point that I won't find a way to speed this up until my provider gives access to more powershell cmdlets, or the exchange cmdlets are modified to allow multiple pipelines, neither of which I expect to happen any time soon. I am marking his answer as correct, despite the ultimate result that nothing helps significantly. Thanks, Start-Automate.
You can speed up your script and also avoid trying to make two connections to the server by the use of the foreach statement, instead of Foreach-Object.
$departments = #(import-csv .\departments.csv)
foreach ($user in $departments) {
Set-User $user.EmailAddress $user.Department
}
If you need to batch, you could use the for statement, moving forward in each batch
for ($i =0; $i -lt $departments.Count; $i+=3) {
$jobs = #()
$jobs+= Invoke-Command { Set-User $departments[$i].EmailAddress $departments[$i].Department } -AsJob
$jobs+= Invoke-Command { Set-User $departments[$i + 1].EmailAddress $departments[$i + 1].Department } -AsJob
$jobs+= Invoke-Command { Set-User $departments[$i + 2].EmailAddress $departments[$i + 2].Department } -AsJob
$jobs | Wait-job | Receive-job
}
Hope this helps