I am working on a PowerShell script that will output a list od system admins to a CSV file using the Export-Csv command. The portion of the script that gets the data is:
Foreach ($Computer in $Computers){
$Online = Test-Connection -ComputerName $Computer -Quiet
if ($Online -eq "True"){
$GroupName = Get-WmiObject win32_group -ComputerName $Computer | ? {$_.SID -eq 'S-1-5-32-544'} | Select-Object name -ExpandProperty name
$LocalGroup =[ADSI]"WinNT://$Computer/$GroupName"
$GroupMembers = #($LocalGroup.psbase.Invoke("Members"))
$Members = $GroupMembers | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
foreach ($Member in $Members){
$obj = New-Object System.Object
$obj | Add-Member -MemberType NoteProperty -Name "Computer" -Value $Computer
$obj | Add-Member -MemberType NoteProperty -Name "AdminGroupMembers" -Value $Member
$obj
}
}
}
}
Get-Admins | Export-Csv -NoTypeInformation c:\scripts\adm.csv -Encoding UTF8
The current output is formatted looks like this:
"Computer1", "Admin1"
"Computer1", "Admin2"
"Computer1", "Admin3"
"Computer1", "Admin4"
"Computer2", "Admin1"
"Computer2", "Admin2"
"Computer3", "Admin1"
I am trying to get the output to look like this:
"Computer1", "Admin1" , "Admin2" , "Admin3" , "Admin4"
"Computer2", "Admin1" , "Admin2"
"Computer3", "Admin1" , "Admin2" , "Admin3"
Any Ideas?
Your output format is not CSV, so Export-Csv is not a suitable tool for you. Try this instead:
Get-Admins | group { $_.Computer } | % {
'{0},{1}' -f #($_.Group.Computer)[0], ($_.Group.AdminGroupMembers -join ',')
} | Out-File 'output.csv'
For PowerShell v2 you'll need to manually expand the group properties:
Get-Admins | group { $_.Computer } | % {
$computer = #($_.Group | select -Expand Computer)[0]
$admins = ($_.Group | select -Expand AdminGroupMembers) -join ','
'{0},{1}' -f $computer, $admins
} | Out-File 'output.csv'
Related
Is it possible to adjust this code to export all lines outside foreach loop:
This works fine (inside loop):
$vms = Get-VM | Where { $_.State –eq ‘Running’ } | Select-Object -ExpandProperty Name
foreach($vm in $vms) {
# Get network interface details
$out = Get-VMNetworkAdapter -vmname $vm | select VMName, MacAddress, IPAddresses
$vm_name = $out.VMName | Get-Unique
$ip = ($out.IPAddresses | ForEach-Object {
$_ | ? {$_ -notmatch ':'}
}) -join " "
# If more than 1 MAC , put it in same row separated by space (00:15:5D:58:12:5E 00:15:5D:58:12:5F )
$mac = ($out.MacAddress | ForEach-Object {
$_.Insert(2,":").Insert(5,":").Insert(8,":").Insert(11,":").Insert(14,":")
}) -join ' '
$results = #()
$comp = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty name
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "VM NAME" -Value $vm_name
$obj | Add-Member -MemberType NoteProperty -Name "IP ADDRESS" -Value $ip
$obj | Add-Member -MemberType NoteProperty -Name "MAC ADDRESS" -Value $mac
$obj | Add-Member -MemberType NoteProperty -Name "HYPER-V HOST" -Value $comp
$results += $obj
Write-Output $results
$results| Export-Csv -Path "c:\1.csv" -NoTypeInformation -append
}
However, when i move $results| Export-Csv -Path "c:\1.csv" -NoTypeInformation -append outside loop,
only one (last) line is saved to CSV
Inside loop, $results variable contains all lines, when i move this variable outside loop write-host $results only one (last) line is printed
For what it's worth, your code can be condensed quite a bit. Many of your steps are not necessary:
$results = Get-VM | Where State –eq Running | Get-VMNetworkAdapter | ForEach-Object {
[pscustomobject]#{
'VM NAME' = $_.VMName
'IP ADDRESS' = ($_.IPAddresses -notmatch ':') -join ' '
'MAC ADDRESS' = ($_.MacAddress -replace '(..)(..)(..)(..)(..)','$1:$2:$3:$4:$5:') -join ' '
'HYPER-V HOST' = $env:COMPUTERNAME
}
}
$results | Export-Csv -Path "c:\1.csv" -NoTypeInformation
Notes:
You can pipe the VMs that Get-VM returns directly into Get-VMNetworkAdapter
If you filter on a single property you don't need a script block for Where-Object. Where State -eq Running is a bit easier to write and read than Where { $_.State -eq 'Running' }.
$_.IPAddresses -notmatch ':' Operators like -notmatch work on arrays. 'a','b','0','c' -notmatch '\d' will return 'a','b','c'.
The same goes for -replace. 'a0','b1','c2' -replace '\d','' will return return 'a','b','c'. No foreach loops necessary at all.
$env:COMPUTERNAME should be faster than using WMI to get the computer name
Any object you create in a script block (like the ForEach-Object {...} script block) that you do not assign to a variable will be in the script block's output. This is why $results = ... | ForEach-Object {...} works. There is no need to explicitly create arrays with #() and add values to them.
Casting a hash table to [pscustomobject] is much easier than using Add-Member.
Figured it out:
moved $results variable outside loop (make it "global")
$vms = Get-VM | Where { $_.State –eq ‘Running’ } | Select-Object -ExpandProperty Name
$results = #()
foreach($vm in $vms) {
# Get network interface details
$out = Get-VMNetworkAdapter -vmname $vm | select VMName, MacAddress, IPAddresses
# Remove duplicate VM names
$vm_name = $out.VMName | Get-Unique
# In case more than 1 IP, put it in same row separated by space (192.168.1.1, 192.168.1.2)
$ip = ($out.IPAddresses | ForEach-Object {
$_ | ? {$_ -notmatch ':'}
}) -join " "
# If more than 1 MAC , put it in same row separated by space (00:15:5D:58:12:5E 00:15:5D:58:12:5F )
$mac = ($out.MacAddress | ForEach-Object {
$_.Insert(2,":").Insert(5,":").Insert(8,":").Insert(11,":").Insert(14,":")
}) -join ' '
$comp = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty name
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "VM NAME" -Value $vm_name
$obj | Add-Member -MemberType NoteProperty -Name "IP ADDRESS" -Value $ip
$obj | Add-Member -MemberType NoteProperty -Name "MAC ADDRESS" -Value $mac
$obj | Add-Member -MemberType NoteProperty -Name "HYPER-V HOST" -Value $comp
$results += $obj
}
$results| Export-Csv -Path "c:\1.csv" -NoTypeInformation
I need to list users of a connected drive and it's serial # in an output file. I'll be connecting between 12-24 drives in arrays at a time. I would like to be able to put the assigned drive letters into a variable. And then have the entire script loop for each connected drive. dumping serial + linking it to the users of that drive in a CSV output file
How can I put the assigned drive letters into an array?
$(get-physicaldisk; get-childitem -path (array variable):\Users) | add-content C:\path\to\my\output.csv
almost gets the output I need when I try this on a single drive. But I'd really like to clean it up and only display the important info (PSChildName) excluding all default, public admin accounts to reduce duplicate un-needed info.
I wanted this to work
$(get-physicaldisk | select-object FriendlyName, SerialNumber)-$(get-childitem -path L:\Users| select-object PSChildName)
but it did not
I need it to grab the serial for each drive - and output the users associated with that drive … i'm struggling with making the output look the way I want.
For each - drive in array - output ((serial #) + (users on the drive)) amending my .csv
After much plugging and chugging i'm now here, thanks to everyone's help
function Get-UsersOnDrive([string[]]$DriveLetters){
if (!$DriveLetters){
$DriveLetters = Get-WmiObject Win32_Logicaldisk | %{$_.Name -replace ":", ""}
}
foreach($DriveLetter in $DriveLetters)
{
$SerialNumber = get-partition -DriveLetter $DriveLetter -ErrorAction Ignore | get-disk | select -ExpandProperty SerialNumber
$path = $DriveLetter + ":\Users"
$Users = get-childitem -path $path | select-object PSChildName
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
}
}
Get-UsersOnDrive -DriveLetters #("C") | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation
Ok so here is what i came up with and its rough
Get-WmiObject Win32_Logicaldisk | %{
$DriveLetter = $_.Name -replace ":", ""
$SerialNumber = get-partition -DriveLetter $DriveLetter | get-disk | select -ExpandProperty SerialNumber
$Users = Get-WmiObject Win32_UserProfile | select -ExpandProperty LocalPath | ?{$_ -like "$DriveLetter*"} | %{
$_ -replace '.*\\'
}
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
} | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation
A. Get WMI LogicalDisk (gets you the drive letters)
B. Pass the $DriveLetter into a get-partition and get the SerialNumber property value.
C. Get Users Profile path, then find the ones on the current drive and replace everything except for the last slash, which is the username
D. Foreach user on drive we create a Custom Object and add the properties SerialNumber and Username, then return output and export to CSV
Here is a function that you can call to get users on drive as well
function Get-UsersOnDrive([string[]]$DriveLetters){
if (!$DriveLetters){
$DriveLetters = Get-WmiObject Win32_Logicaldisk | %{$_.Name -replace ":", ""}
}
foreach($DriveLetter in $DriveLetters){
$SerialNumber = get-partition -DriveLetter $DriveLetter -ErrorAction Ignore | get-disk | select -ExpandProperty SerialNumber
$Users = Get-WmiObject Win32_UserProfile | select -ExpandProperty LocalPath | ?{$_ -like "$DriveLetter*"} | %{
$_ -replace '.*\\'
}
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
}
}
Get-UsersOnDrive -DriveLetters #("C","V","F") | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation
If you remove -DriveLetters parameter and the drives then it will check all drives
The following code gets the disk serial number. I am not sure why that is needed. Will this give you a start?
function Get-DiskSerialNumber {
param(
[Parameter(Mandatory = $true,Position=0)]
[string] $DriveLetter
)
Get-CimInstance -ClassName Win32_DiskDrive |
Get-CimAssociatedInstance -Association Win32_DiskDriveToDiskPartition |
Get-CimAssociatedInstance -Association Win32_LogicalDiskToPartition |
Where-Object DeviceId -eq $DriveLetter |
Get-CimAssociatedInstance -Association Win32_LogicalDiskToPartition |
Get-CimAssociatedInstance -Association Win32_DiskDriveToDiskPartition |
Select-Object -Property SerialNumber
}
& openfiles /query /fo csv |
Select-Object -Skip 5 |
ConvertFrom-Csv -Header #('ID','USER','TYPE','PATH') |
Select-Object -Property USER,#{name='DRIVE';expression={$_.PATH.Substring(0,2)}} |
Sort-Object -Property DRIVE,USER -Unique |
Select-Object -Property *,
#{name='SERIALNUMBER';expression={(Get-DiskSerialNumber -Drive $_.DRIVE).SerialNumber}}
I want to migrate from one server to another one and because of that it is needed to add some local groups in the new server. In these local groups the users added belong to the domain.
Ex.:
Server | Members
---------------------------|------------------
Server\Group1 | Domain\User1, Domain\User2
Server\Group2 | Domain\User2, Domain\User3
The following link https://www.petri.com/use-powershell-to-find-local-groups-and-members seems to resolve this, but I am getting an unexpected result
This is the PowerShell script
# set variables
$server = $env:COMPUTERNAME
$localgroup = "Administrators"
$Group= [ADSI]"WinNT://$Server/$LocalGroup,group"
# get users name
$members = $Group.psbase.Invoke("Members")
$members | ForEach-Object
{
$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
}
Get-WMIObject win32_group -filter "LocalAccount='True'" -computername $Server | Select PSComputername,Name,#{Name="Members";Expression={$_.GetRelated("Win32_UserAccount").Name -join ";"}}
The shown output is a two columns(although it should be 3, but PSComputerName is not being displayed where the Members column is empty)
Well, this is how I achieved the output and also exported it to a *.csv file
# set variables
$server = $env:COMPUTERNAME
$tableOutput = New-Object System.Collections.ArrayList
# get members
Function Get-Members($groupName){
$testgroup = [ADSI]"WinNT://$Server/$tmpGroupName,group"
$members = New-Object System.Collections.ArrayList
$testgroup.psbase.Invoke("Members") | ForEach-Object{
$searchFilter = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -replace "\."," "
$tmpUser = Get-ADUser -Filter {(Name -like $searchFilter) -or (SamAccountName -like $searchFilter)}
if($tmpUser){
[void]$members.Add($tmpUser.UserPrincipalName)
}
}
$members
}
Get-WMIObject win32_group -Filter { (LocalAccount='True') } -computername $Server | ForEach-Object{
$tmpGroup = $_
# get data
$tmpGroupName = $tmpGroup.Name
$members = Get-Members($tmpGroupName)
$tmpGroupDescription = $tmpGroup.Description
# save into object
$groupObject = New-Object -TypeName PSObject
$groupObject | Add-Member -MemberType NoteProperty -Name GroupName -Value $tmpGroupName
$groupObject | Add-Member -MemberType NoteProperty -Name GroupDescription -Value $tmpGroupDescription
$groupObject | Add-Member -MemberType NoteProperty -Name UsersList -Value $members
[void]$tableOutput.Add($groupObject)
}
$tableOutput | Select GroupName, GroupDescription, #{Name='Users';Expression={$_.UsersList -join ','}} | Export-CSV -Path 'C:\test\users.csv' -Delimiter ';' -NoTypeInformation
Any correction would be appreciated.
I am very close to what I am looking for, I have all of the correct data and it is just not formatting correctly. Currently it is all in one column, and what I would like is for the headers to be in a row, and then each entry to be in its own row as well. Here is my current code:
$servers = Get-ADComputer -Filter "Name -like 'f****p*'" | Sort-Object | Select -ExpandProperty Name
$servers | foreach {
$server = $_
$serverIP = Test-Connection -ComputerName $server -Count 1 | Select -ExpandProperty IPV4Address
$serverIPString = $serverIP.IPAddresstoString
$scope = Get-DhcpServerv4Scope -ComputerName $server
$options = Get-DhcpServerv4OptionValue -ComputerName $server
New-Object -TypeName psobject -Property #{
ServerName = $server
ServerIP = $serverIPString
ScopeName = $scope.Name | Out-String
StartIP = $scope.StartRange.IPAddressToString | Out-String
EndIP = $scope.EndRange.IPAddressToString | Out-String
SubnetMask = $scope.SubnetMask.IPAddressToString | Out-String
Duration = $scope.LeaseDuration.Days | Out-String
OptionName = $options.Name | Out-String
OptionID = $options.OptionID | Out-String
OptionValue = $options.Value | Out-String
}
} | Select-Object ServerName, ServerIP, ScopeName, StartIP, EndIP, SubnetMask, Duration, OptionName, OptionID, OptionValue | Out-File C:\temp\FPScopes.csv
Currently the output looks like this example (using Duration and Options as an example, as it has the most generic data to share):
What I am looking for would be to have each object type as a column header, and have the entries in a separate row underneath the header:
Etc....
Any assistance would be greatly appreciated.
Thank you! :)
Ry
My final code, after help from this forum:
Add-Content -Value "ServerName,ServerIP,ScopeName,StartRange,EndRange,SubnetMask,Duration,OptionName,OptionID,Value" -Path c:\temp\POSScopes.csv
Get-ADComputer -Filter {Name -like 'p****p01' -or Name -like 'p****p02' -or Name -like 'q****001' -or Name -like 'q****002'} | Sort-Object | Select -ExpandProperty Name | Out-File C:\Temp\POSServers.txt
$servers = Get-Content C:\Temp\POSServers.txt
$servers | foreach {
$server = $_
$serverIP = Test-Connection -ComputerName $server -Count 1 | Select -ExpandProperty IPV4Address
$serverIPString = $serverIP.IPAddresstoString
$scope = Get-DhcpServerv4Scope -ComputerName $server
$options = Get-DhcpServerv4OptionValue -ComputerName $server
New-Object -TypeName psobject -Property #{
ServerName = $server
ServerIP = $serverIPString
ScopeName = $scope.Name | Out-String
StartIP = $scope.StartRange.IPAddressToString | Out-String
EndIP = $scope.EndRange.IPAddressToString | Out-String
SubnetMask = $scope.SubnetMask.IPAddressToString | Out-String
Duration = $scope.LeaseDuration.Days | Out-String
OptionName = $options.Name | Out-String
OptionID = $options.OptionID | Out-String
OptionValue = $options.Value | Out-String
}
for ($i = ($Options.Count -1); $i -gt -1; $i--) {
Add-Content -Value "$($server),$($serverIPString),$($scope.Name),$($scope.StartRange.IPAddressToString),$($scope.EndRange.IPAddressToString),$($scope.SubnetMask.IPAddressToString),$($scope.LeaseDuration.Days),$($options[$i].Name),$($options[$i].OptionID),$($Options[$i].Value)" -Path C:\temp\POSScopes.csv
}
}
Add-Content -Value "ServerName,ServerIP,ScopeName,StartRange,EndRange,SubnetMask,Duration,OptionName,OptionID,Value" -Path c:\temp\POSScopes.csv
Get-ADComputer -Filter {Name -like 'p****p01' -or Name -like 'p****p02' -or Name -like 'q****001' -or Name -like 'q****002'} | Sort-Object | Select -ExpandProperty Name | Out-File C:\Temp\POSServers.txt
$servers = Get-Content C:\Temp\POSServers.txt
$servers | foreach {
$server = $_
$serverIP = Test-Connection -ComputerName $server -Count 1 | Select -ExpandProperty IPV4Address
$serverIPString = $serverIP.IPAddresstoString
$scope = Get-DhcpServerv4Scope -ComputerName $server
$options = Get-DhcpServerv4OptionValue -ComputerName $server
New-Object -TypeName psobject -Property #{
ServerName = $server
ServerIP = $serverIPString
ScopeName = $scope.Name | Out-String
StartIP = $scope.StartRange.IPAddressToString | Out-String
EndIP = $scope.EndRange.IPAddressToString | Out-String
SubnetMask = $scope.SubnetMask.IPAddressToString | Out-String
Duration = $scope.LeaseDuration.Days | Out-String
OptionName = $options.Name | Out-String
OptionID = $options.OptionID | Out-String
OptionValue = $options.Value | Out-String
}
for ($i = ($Options.Count -1); $i -gt -1; $i--) {
Add-Content -Value "$($server),$($serverIPString),$($scope.Name),$($scope.StartRange.IPAddressToString),$($scope.EndRange.IPAddressToString),$($scope.SubnetMask.IPAddressToString),$($scope.LeaseDuration.Days),$($options[$i].Name),$($options[$i].OptionID),$($Options[$i].Value)" -Path C:\temp\POSScopes.csv
}
}
Hello I am trying to if/else and write two separate files, if PST exists then do the following. Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-OpenPSTs-$Date.csv
Else Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-NOPSTs-$Date.csv
Could anyone please suggest.
$Date = Get-Date -format d-M-yyyy
$UserName = $env:USERNAME
$ComputerName = $env:COMPUTERNAME
$Outlook = New-Object -comObject Outlook.Application
$object = $Outlook.Session.Stores | Where {$_.FilePath -like "*.PST"} | Select `
#{Expression={$_.DisplayName}; Label="PST Name in Outlook"},`
#{Expression={$_.FilePath}; Label="PST Location/FileName"},`
#{Expression={$_.IsOpen}; Label="PST Open in Outlook"},`
#{Expression={(Get-Item $_.FilePath).Length / 1KB}; Label="PST File Size (KB)"}
$object | Add-Member -MemberType NoteProperty -Name 'ComputerName' -Value $ComputerName
$object | Add-Member -MemberType NoteProperty -Name 'UserName' -Value $UserName
$object | Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-OpenPSTs-$Date.csv
Start-Sleep 5
Get-Process | Where {$_.Name -like "Outlook*"} | Stop-Process
You could replace the Where-Object filter with a ForEach-Object loop and a nested conditional:
$Outlook.Session.Stores | % {
if ($_.FilePath -like '*.pst') {
$_ | select ... | Export-Csv 'OpenPST.csv' -NoType -Append
} else {
$_ | select ... | Export-Csv 'NoPST.csv' -NoType -Append
}
}
That might not perform too well, though, because it repeatedly appends to the output files. It might be better to just run 2 pipelines with complementary filters:
$stores = $Outlook.Session.Stores
$stores | ? { $_.FilePath -like '*.pst' } | select ... |
Export-Csv 'OpenPST.csv' -NoType
$stores | ? { $_.FilePath -notlike '*.pst' } | select ... |
Export-Csv 'NoPST.csv' -NoType