I'm attempting to gather data via WMI and then output that to a CSV once I've setup the tables using expressions, but I'm not getting the output I'd expect.
$Ips = "192.168.1.1", "192.168.1.2"
foreach ($ip in $Ips) {
$Profiles = Get-WmiObject -Class Win32_UserProfile -Computer $ip -ea 0
Write-Host `n$ip
foreach ($profile in $Profiles) {
#{Expression={$ip};Label="IPAddress"}, #{Expression={"\\"+$ip+"\"+$profile.LocalPath};Label="SharePath"} |
Export-Csv -Path "$HOME\Desktop\Found.csv" -Append -NoTypeInformation
}
}
The feature you're trying to use here is called calculated properties, and you need Select-Object to use it:
Get-WmiObject -Class Win32_UserProfile -Computer $ip -EA 0 |
Select-Object #{Expression={$ip};Label="IPAddress"},
#{Expression={'\\{0}\{1}' -f $ip, $profile.LocalPath};Label="SharePath"} |
Export-Csv -Path "$HOME\Desktop\Found.csv" -Append -NoTypeInformation
Related
I hope someone can help me with this. We want to see which computers have a HDD and SDD. I have an excel.csv of the computers. I import the computers. But when I export them I never see the csv or its incomplete. Can you tell what part of my script is incorrect. Thank you
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers) {
Write-Host "`nPulling Physical Drive(s) for $computer"
if((Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet)){
Invoke-Command -ComputerName $computer -ScriptBlock {
Get-WmiObject -Class MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage | Select-Object sort -Property PSComputerName, Model, SerialNumber, MediaType
Export-Csv C:\Temp\devices.csv
}
}
}
Update: 11/11/2021
Thank you everyone for you help
This script worked for me:
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\Computers.csv"
{} | Select "ComputerName", "Status", "Model", "SerialNumber", "MediaType" | Export-Csv $ExportTo
$data = Import-csv -path $ExportTo
foreach ($computer in $computers) {
$Online = Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer.computer -Quiet
if ($Online) {
Write-Host $computer.computer " is Online"
$OutputMessage = Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer.computer | Select-Object -Property PSComputerName,#{N='Status';E={'Online'}}, Model, SerialNumber, MediaType
$data.ComputerName = $computer.computer
$data.Status = $OutputMessage.Status
$data.Model = $OutputMessage.Model
$data.SerialNumber = $OutputMessage.SerialNumber
$data.MediaType = $OutputMessage.MediaType
$data | Export-Csv -Path $ExportTo -Append -NoTypeInformation
} else {
Write-Host $computer.computer " is Offline"
$data.ComputerName = $computer.computer
$data.Status = "Offline"
$data.Model = ""
$data.SerialNumber = ""
$data.MediaType = ""
$data | Export-Csv -Path $ExportTo -Append -NoTypeInformation
}
}
Continuing from my comment. . . as is, you would be exporting the results to the remote machine. That's if it was piped properly. You're currently missing a pipe (|) before Export-Csv.
Also, there's no need to invoke the command, as Get-WMIObject has a parameter for remote computers: -ComputerName. It's also a deprecated cmdlet that has been replaced by Get-CimInstance.
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers)
{
Write-Host "`nPulling Physical Drive(s) for $computer"
if (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet) {
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer |
Select-Object -Property PSComputerName, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation
}
}
Side Note: Get-CimInstance accepts an array of strings, meaning you can pass the entirety of $Computers to it. This should allow it to perform the the query in parallel, vs serial (one at a time):
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computers -ErrorAction SilentlyContinue |
Select-Object -Property PSComputerName, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation
Performing queries one at a time doesn't necessarily mean it's bad. You can actually have more control over the control of flow for your script.
EDIT:
Following up on your comment...you're no longer using your if statement to check if the computer is online before connecting. So given that you keep the if statement, and add an else condition, you can create a calculated property to add another property to export of Status. Then, you can pass it a value of Online, or Offline depending on if the machine is online or not:
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers)
{
if (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet) {
Write-Host -Object "`nPulling Physical Drive(s) for $computer"
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer |
Select-Object -Property PSComputerName,#{N='Status';E={'Online'}}, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation -Force
}
else {
Write-Host -Object "`n$Computer is Offline"
[PSCustomObject]#{PSComputerName=$Computer;Status='Offline'} | Export-Csv -Path $ExportTo -Append -Force
}
}
Also:
Always remember that even if you can ping a machine, it doesn't mean you can connect to it.
This can be mitigated by using a CIM Session, or PSSession depending on the type of commands you're running.
To specifically answer the question:
How do I correctly export a CSV file (use Export-Csv)?
You might want to read about PowerShell pipelines and PowerShell cmdlets.
Basically, a cmdlet is a single command that participates in the pipeline semantics of PowerShell. A well written cmdlet is implemented for the Middle of a Pipeline which means that it processes ("streams") each individual item received from the previous cmdlet and passes it immediately to the next cmdlet (similar to how items are processed in an assembly line where you can compare each assembly station as a cmdlet).
To better show this, I have created an easier minimal, complete and verifiable example (MVCE) and replaced your remote command (Invoke-Command ...) which just an fake [pscustomobject]#{ ... } object.
With that;
I have used Get-Content rather then Import-Csv as your example suggest that Computers.csv is actually a text file which list of computers and not a Csv file which would require a (e.g. Name) header and using this property accordingly (like $Computer.Name).
To enforce the pipeline advantage/understanding, I am also using the ForEach-Object cmdlet rather than the foreach statement which is usually considered faster but this is probably not the case here as for the foreach statement it is required to preload all $Computers into memory where a well written pipeline will immediately start processing each item (which in your case happens on a remote computer) while still retrieving the next computer name from the file.
Now, coming back on the question "How do I correctly export a CSV file" which a better understanding of the pipeline, you might place Export-Csv within the foreach loop::
Get-Content .\Computers.txt |ForEach-Object {
[pscustomobject]#{
PSComputerName = $_
Model = "Model"
SerialNumber = '{0:000000}' -f (Get-Random 999999)
MediaType = "MydiaType"
} |Export-Csv .\Devices.csv -Append
}
As commented by #lit, this would require the -Append switch which might not be desired as every time you rerun your script this would append the results to the .\Devices.csv file.
Instead you might actually want do this:
Get-Content .\Computers.txt |ForEach-Object {
[pscustomobject]#{
PSComputerName = $_
Model = "Model"
SerialNumber = '{0:000000}' -f (Get-Random 999999)
MediaType = "MydiaType"
}
} |Export-Csv .\Devices.csv
Note the differences: the Export-Csv is placed outside the loop and the -Append switch is removed.
Explanation
As with e.g. the ForEach-Object cmdlet, the Export-Csv cmdlet has internally Begin, Process and End blocks.
In the Begin block (which runs when the pipeline is started), the Export-Csv cmdlet prepares the csv file with a header row etc. and overwrites any existing file.
In the Process block (which runs for each item received from the pipeline) it appends each line (data record) to the file.
I'm trying to get the hostname and the MAC address from all PCs in the Active Directory. I know that MAC addresses are not in the Activce Directory. That's why I already used a small script from someone else. The point is that I have to make a list of hostnames, which I can do, but then the other script runs into a problem because some computers are not online.
Can anyone help me get a list with only the pc's that are online?
This is the part that searches the list I create with hostnames.
$Computers = Import-CSV C:\Users\admin_stagiair\Desktop\Computers.txt
$result = #()
foreach ($c in $Computers){
$nic = Invoke-Command {
Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"'
} -ComputerName $c.Name
$x = New-Object System.Object | select ComputerName, MAC
$x.Computername = $c.Name
$x.Mac = $Nic.MACAddress
$result += $x
}
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation
And this is the part that I tried to make search the list and filter out the online computers, which absolutely does not work and I can't figure out how to do it.
$Computers = Import-Csv C:\Users\admin_stagiair\Desktop\Computers.txt
foreach ($c in $Computers) {
$ping = Test-Connection -Quiet -Count 1
if ($ping) {
$c >> (Import-Csv -Delimiter "C:\Users\admin_stagiair\Desktop\online.txt")
} else {
"Offline"
}
}
Last bit, this is the part I use to create a list of all computers in the Active Directory.
Get-ADComputer -Filter {enabled -eq $true} -Properties * |
select Name > C:\Users\(user)\Desktop\Computers.txt
If you only want one property from Get-ADComputer don't fetch all
a computer could have more than one MAC, to avoid an array be returned join them.
$result += inefficiently rebuilds the array each time, use a PSCustomObject instead.
Try this (untested):
EDIT: first test connection, get MAC only when online
## Q:\Test\2018\09\18\SO_52381514.ps1
$Computers = (Get-ADComputer -Filter {enabled -eq $true} -Property Name).Name
$result = ForEach ($Computer in $Computers){
If (Test-Connection -Quiet -Count 1 -Computer $Computer){
[PSCustomPbject]#{
ComputerName = $Computer
MAC = (Invoke-Command {
(Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"').MACAddress -Join ', '
} -ComputerName $Computer)
Online = $True
DateTime = [DateTime]::Now
}
} Else {
[PSCustomPbject]#{
ComputerName = $Computer
MAC = ''
Online = $False
DateTime = [DateTime]::Now
}
}
}
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation
What about trying something like this:
# Get all computers in list that are online
$Computers = Import-Csv C:\Users\admin_stagiair\Desktop\Computers.txt |
Select-Object -ExpandProperty Name |
Where-Object {Test-Connection -ComputerName $_ -Count 1 -Quiet}
# Grab the ComputerName and MACAddress
$result = Get-WmiObject -ComputerName $computers -Class Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"' |
Select-Object -Property PSComputerName, MacAddress
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation
So having some good old fashion Powershell frustrations today. What I need to do is this:
Get a list of computers from a file
Query those computers for "CSName" and "InstallDate" from Win32_OperatingSystem
Convert InstallDate into a useable date format.
Export all that to a .Csv
I've tried so many different iterations of my script. I run into 2 major issues. One is that I can't export and append to .Csv even with Export-Csv -Append. It just takes the first value and does nothing with the rest. The 2nd is that I can't get the datetime converter to work when piping |.
Here's a few samples of what I've tried - none of which work.
This sample simply errors a lot. Doesn't seem to carry $_ over from the WMI query in the pipe. It looks like it is trying to use data from the first pipe, but I'm not sure.
Get-Content -Path .\Computernames.txt | Foreach-Object {
gwmi Win32_OperatingSystem -ComputerName $_) |
Select-Object $_.CSName, $_.ConvertToDateTime($OS.InstallDate).ToShortDateString()
} | Export-Csv -Path Filename -Force -Append -NoTypeInformation
}
This one simply exports the first value and gives up on the rest when exporting .Csv
$Computers = Get-Content -Path .\Computernames.txt
foreach ($Computer in $Computers) {
echo $Computer
$OS = gwmi Win32_OperatingSystem -ComputerName $Computer
$OS | Select-Object
$OS.CSName,$OS.ConvertToDateTime($OS.InstallDate).ToShortDateString() |
Export-Csv -Path $Log.FullName -Append
}
This one does get the data, but when I try to select anything, I get null values, but I can echo just fine.
$OS = gwmi Win32_OperatingSystem -ComputerName $Computers
$OS | Foreach-Object {
Select-Object $_.CSName,$_.ConvertToDateTime($OS.InstallDate).ToShortDateString() |
Export-Csv -Path $Log.FullName -Force -Append -NoTypeInformation
}
This feels like it should be ridiculously simple. I can do this in C# with almost no effort, but I just can't get PS to do what I want. Any help would be much appreciated!
Here you go,
$Array = #() ## Create Array to hold the Data
$Computers = Get-Content -Path .\Computernames.txt
foreach ($Computer in $Computers)
{
$Result = "" | Select CSName,InstallDate ## Create Object to hold the data
$OS = Get-WmiObject Win32_OperatingSystem -ComputerName $Computer
$Result.CSName = $OS.CSName ## Add CSName to line1
$Result.InstallDate = $OS.ConvertToDateTime($OS.InstallDate).ToShortDateString() ## Add InstallDate to line2
$Array += $Result ## Add the data to the array
}
$Array = Export-Csv c:\file.csv -NoTypeInformation
I'm using the following script and it creates the out file correctly, but it's empty. I'm not sure what it's missing exactly. This is my first attempt with using workflows.
workflow DisableIISParallel
{
$ScriptPath = "C:\Scripts\Server_Lists"
$OLD = "Legacy-Servers"
$OldList = Get-Content "$ScriptPath\$OLD.txt"
$objHost=$Records.Length
function DisableIIS ($appName) {
$objHostStr = [System.Net.Dns]::GetHostEntry([string]$objHost).HostName
Invoke-Command -ComputerName $objHostStr { iisreset /stop }
Set-Service -Name W3SVC -StartupType Disabled -Status Stopped -ComputerName $objHostStr
Get-WmiObject -Class win32_service -ComputerName $objHostStr |
where { $_.name -eq "W3SVC" } |
Format-Table -Property #{Expression={$_.PSComputerName};Label="Server";width=18},
#{Expression={$_.Name};Label="Service";width=45},
#{Expression={$_.StartMode};Label="Mode";width=10},
#{Expression={$_.State};Label="State";width=10},
#{Expression={$_.Status};Label="Status";width=10} |
Format-List | out-string -s | ? {$_} | Out-File C:\Scripts\Output\$appName.log -Append
Write-Output "" | Out-File C:\Scripts\Output\$appName.log -Append
}
foreach -parallel($objHost in $OldList)
{
$appName = $OLD
DisableIIS $appName
}
}
DisableIISParallel
This bit is not right:
Get-WmiObject -Class win32_service -ComputerName $objHostStr |
where { $_.name -eq "W3SVC" } |
Format-Table -Property #{Expression={$_.PSComputerName};Label="Server";width=18},
#{Expression={$_.Name};Label="Service";width=45},
#{Expression={$_.StartMode};Label="Mode";width=10},
#{Expression={$_.State};Label="State";width=10},
#{Expression={$_.Status};Label="Status";width=10} |
Format-List | out-string -s | ? {$_} | Out-File C:\Scripts\Output\$appName.log -Append
You should not run the output of one format command (Format-Table) into another format command (Format-List).
Try this (assuming you want tabular format - if not, change the Format-Table to Format-List):
Get-WmiObject -Class win32_service -ComputerName $objHostStr |
where { $_.name -eq "W3SVC" } |
Format-Table #{Expression={$_.PSComputerName};Label="Server";width=18},
#{Expression={$_.Name};Label="Service";width=45},
#{Expression={$_.StartMode};Label="Mode";width=10},
#{Expression={$_.State};Label="State";width=10},
#{Expression={$_.Status};Label="Status";width=10} >> C:\Scripts\Output\$appName.log
And pass in all the variables required to the function e.g.:
function DisableIIS ($appName, $objHost) {
...
}
foreach -parallel($objHost in $OldList)
{
$appName = $OLD
DisableIIS $appName $objHost
}
I can only get the command to return the services on the first computer in the text file.
Is there a better way than for-each for this task?
Get-Service *vault* -ComputerName (Get-Content c:\users\sean\desktop\js.txt) | select name,status,machinename | sort machinename | format-table -autosize
Try it without the get-content. Try this:
Get-Service *vault* -ComputerName c:\users\sean\desktop\js.txt | select name,status,machinename | sort machinename | format-table -autosize
If that doesn't work, then try:
$Computers = Get-Content c:\users\sean\desktop\js.txt
Get-Service *vault* -computername $Computers | Select name,status,machinename |sort machinename |format-table -autosize
If you are eager for a one-liner then try this:
Get-Content c:\users\sean\desktop\js.txt | Get-Service *vault* | Select name,status,machinename |sort machinename |format-table -autosize
I would try the top one first. I would test, but I don't have access to anything I can do a proper test right now.
$Computers = get-content .\desktop\test.txt
$Service = "Vault"
foreach ($computer in $computers) {
$computer
$Servicestatus = get-service -name $Service -ComputerName $computer
}
$Servicestatus | select-object Name,Status,MachineName | format-table -Autosize
This works for me, it gives me each of the computers in the text file, and it looks for the service.
This is what I use. I get the list of computers from an OU in AD.
Import-Module ActiveDirectory
$ou = "OU=Servers,DC=Domain,DC=com"
$servers = Get-ADComputer -Filter * -SearchBase $ou | select-object -expandproperty name
Foreach ($server in $servers){
$Data = Get-Service -ServiceName *IIS*,*TomCat*,*httpd* -ComputerName $server | select machinename,name | sort machinename | format-table -AutoSize
Write($Data) | Out-File .\WebServices.txt -Append
}
$servers = Get-Content .\servers.txt
Foreach ($server in $servers) {
"$server"
Get-Service -ComputerName $Server -name -like "*vault*"
"-------------------"
}
Following a memory limitation limit with older versions of PowerShell, I was required to refresh my code:
Old code:
gwmi win32_service -computer $allcomputers | Select-Object __SERVER,Name,state,startmode,StartName
New code:
`$servers = Get-Content "computers.txt"
Foreach ($server in $servers) {
Get-WmiObject -Class WIN32_service -ComputerName $server |
Select-Object __SERVER,Name,state,startmode,StartName |
Export-Csv -path "Report.CSV" -NoTypeInformation -Append
}`
This is how you can get list of all services in your AD domain:
Get-ADComputer -Filter {OperatingSystem -Like “Windows 10*”} | ForEach-Object {Get-WmiObject -Class Win32_Service -Computer $_.Name}
More useful examples on this (get list of services for all computer listed in a text file, etc.):
https://www.action1.com/kb/list_of_services_on_remote_computer.html
Get-Service -ComputerName ... has a bug in PowerShell 2.0 that only returns the first computer. This is fixed in newer versions so if you upgrade to PowerShell 3.0 or newer, your original code will work fine.
As a workaround, use a foreach-loop to run Get-Service once for each computer:
Get-Content c:\users\sean\desktop\js.txt |
ForEach-Object { Get-Service -Name *vault* -ComputerName $_ } |
Select-Object -Property Name, Status, MachineName |
Sort-Object -Property MachineName |
Format-Table -AutoSize
Nick's solution totally doesn't work for me. I ended up writing a quick and dirty one that works:
$servers = Get-Content .\servers.txt
Foreach ($server in $servers) {
"$server"
Get-Service *vault*
"-------------------"
}