Powershell Export-CSV - powershell

I'm just having problems trying to export the following to a CSV, I've tried putting the Export-CSV within the Foreach-Object loop to no avail.
I want to put the server name $server in the first column and the description $description in the second.
In an earlier version, I was able to make a text file using Out-File -append .\outa.txt -InputObject $_.Name,$description but the formatting did not work well.
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
$server | ForEach-Object {
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$_.Name)
$RegKey = $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
$description = $RegKey.GetValue("srvcomment")
} | Export-Csv .\out1.csv

A ForEach as oppose to ForEach-Object:
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
foreach($s in $server){
# replaced $_ with $s
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$s.Name)
$RegKey= $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
$description = $RegKey.GetValue("srvcomment")
# casting as array is required to use +=
# disclaimer: the array is destroyed and rebuilt on each run.
[array]$myData += New-Object psobject -Property #{
Server = $s.Name
Description = $description
}
}
# If you want a CSV without the top info line, use the notypeinfo switch
# Select-Object gives you the column order you want.
$myData | Select-Object Server,Description | Export-Csv .\out1.csv -NoTypeInformation
Edit - comment answer
It is possible to do it without creating an array and from inside the loop, but an object is required by Export-Csv AFAIK.
ForEach-Object:
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
$server | ForEach-Object {
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$_.Name)
$RegKey = $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
New-object psobject -Property #{Server=$_.Name;Description=$RegKey.GetValue("srvcomment")} | Export-Csv .\out1.csv -NoTypeInformation
}
ForEach:
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
foreach($s in $server){
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$s.Name)
$RegKey= $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
New-object psobject -Property #{Server=$s.Name;Description=$RegKey.GetValue("srvcomment")} | Export-Csv .\out1.csv -NoTypeInformation
}
If you have problems with the values, brackets/subexpressions can help:
New-object psobject -Property #{Server=($_.Name);Description=($RegKey.GetValue("srvcomment"))} | Export-Csv .\out1.csv -NoTypeInformation
New-object psobject -Property #{Server=$($_.Name);Description=$($RegKey.GetValue("srvcomment"))} | Export-Csv .\out1.csv -NoTypeInformation

Your ForEach-Object only contains statements (variable assignments). It's not returning anything. You need to return something, otherwise you're passing an empty pipeline to Export-CSV.
I'm guessing that, instead of setting variables called $Reg, $RegKey, and $description (and then never using their values), what you actually want to do is create columns in the CSV called Reg, RegKey, and description. In that case, you want to yield a [PSCustomObject] (with those properties added to it) each time through the loop.
$server = Get-ADComputer -Filter {OperatingSystem -Like "*Server*" -and Name -Notlike "*DOM*"}
$server | ForEach-Object {
[PSCustomObject] #{
Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$_.Name)
RegKey = $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters")
description = $RegKey.GetValue("srvcomment")
}
} | Export-Csv .\out1.csv

Related

powershell better way to test if both directories exists or one only on all pc's from domain and output to csv

could you please help me to correct this script (gathered by pieces from internet) or better change logic how it should work (not working currently). The goal is to get pc's where only one folder exist (oracle11) and not both (11+12) and export it to csv. Oracle is a real pain in the ....
Thank you in advance for your advice.
Import-Module ActiveDirectory
$computers = Get-ADComputer -Filter * -Properties * | Select -Property Name
$output = #()
#$computers = get-adcomputer -filter * | Select-Object -Expand Name | foreach-object {
Foreach ($Computer in $computers){
if ( (test-path "\\$Computer\C$\oracle\product\11.2.0\" ) -and !( test-path "\\$Computer\C$\oracle\product\12.2.0" )) {
$output += $Computer
}
}
$output | Export-Csv -NoTypeInformation -Path c:\temp\test.csv
The problem is that the path strings you construct inside the loop are not as you expect.
When you pipe the output from Get-ADComputer to Select-Object -Property Name, it creates a new object with a single property Name for each input object.
When you then implicitly convert one of these objects to a string, the resulting value is going to be "#{Name=Computer01}", instead of just "Computer01".
You can observe this yourself, by calling Write-Host instead of Test-Path:
Get-ADComputer -Filter * |Select-Object -Property Name |ForEach-Object {
Write-Host "\\$_\C$"
}
To extract just the value of the Name property from each ADComputer, use ForEach-Object -MemberName instead of Select-Object -Property:
$computerNames = Get-ADComputer -Filter * -Properties * | ForEach-Object -MemberName
$output = #()
foreach($ComputerName in $computerNames){
if ( (Test-Path "\\$ComputerName\C$\oracle\product\11.2.0\" ) -and !( Test-Path "\\$ComputerName\C$\oracle\product\12.2.0" )) {
$output += $ComputerName
}
}
$output | Export-Csv -NoTypeInformation -Path c:\temp\test.csv
Note that passing -Properties * to Get-ADComputer is unnecessary, the object name is always part of the default property set sent back by the Get-AD* cmdlets.

PowerShell list users in a group linked to folders on file share

I have a Specific OU with groups that we use to control access to a few specific folder on a file share, lets says fileshare1, fileshare2 and fileshar3.
OU=example2,OU=example1,OU=Groups,DC=domain1,DC=domain2,DC=example,DC=uk
I need to get a list of users that are members of the groups that are being used on fileshare1... does anyone have a method of being able to do this? whether its to get powershell to loop through the security section of each folder to get the groups being used...
For each group I need to list, the folder name and who has access.
I'm looking on google, but kinda drawing up blanks...
You could try something like the following:
$ou = "OU=example2,OU=example1,OU=Groups,DC=domain1,DC=domain2,DC=example,DC=uk"
$groups = Get-ADGroup -Filter * -SearchBase $ou
$fileshare = "\\server\share\"
$folders = Get-ChildItem $fileshare -Directory
foreach($folder in $folders){
$accessRights = Get-Acl -Path $folder
$groupsWithAccess = #()
foreach($group in $accessRights.Access){
$groupName = $group.identityReference.value.split("\")[-1]
$getGroup = $groups | Where-Object {($_.samAccountName -eq $groupName -or $_.name -eq $groupName)}
if($getGroup){
$groupsWithAccess += $getGroup
}
}
$memberObject = #()
foreach($groupWithAccess in $groupsWithAccess){
$members = Get-ADGroupMember -Identity $groupWithAccess.samAccountName | Where-Object {($_.objectClass -eq "user")}
<# Uncomment if you wish to add one layer of nested groups
$nestedGroups = Get-ADGroupMember -Identity $groupWithAccess.samAccountName | Where-Object {($_.objectClass -eq "group")}
foreach($nestedGroup in $nestedGroups){
$members = Get-ADGroupMember -Identity $groupWithAccess.samAccountName | Where-Object {($_.objectClass -eq "user")}
$memberObject += New-Object psobject -Property #{
fileshare = $accessRights.Path.Split("\")[-1]
group = $groupWithAccess.name
members = $members.name -join ";"
}
}
#>
$memberObject += New-Object psobject -Property #{
fileshare = $accessRights.Path.Split("\")[-1]
group = $groupWithAccess.name
members = $members.name -join ";"
}
}
}
$memberObject | Export-CSV -Path "C:\Temp\test.csv" -Delimiter "," -NoTypeInformation
The above will loop through the folders, get the access rights for the folder, match the groups from the ou with access, queries the members, queries the members of nested groups (commented out), than creates an object and outputs it to a csv in C:\Temp\test.csv.
You could also use this for a single folder by removing the Get-Childitem and losing the foreach loop through the folders.
$ou = "OU=example2,OU=example1,OU=Groups,DC=domain1,DC=domain2,DC=example,DC=uk"
$groups = Get-ADGroup -Filter * -SearchBase $ou
$fileshare = "\\server\fileshare1"
$accessRights = Get-Acl -Path $fileshare
$groupsWithAccess = #()
foreach($group in $accessRights.Access){
$groupName = $group.identityReference.value.split("\")[-1]
$getGroup = $groups | Where-Object {($_.samAccountName -eq $groupName -or $_.name -eq $groupName)}
if($getGroup){
$groupsWithAccess += $getGroup
}
}
$memberObject = #()
foreach($groupWithAccess in $groupsWithAccess){
$members = Get-ADGroupMember -Identity $groupWithAccess.samAccountName | Where-Object {($_.objectClass -eq "user")}
<# Uncomment if you wish to add one layer of nested groups
$nestedGroups = Get-ADGroupMember -Identity $groupWithAccess.samAccountName | Where-Object {($_.objectClass -eq "group")}
foreach($nestedGroup in $nestedGroups){
$members = Get-ADGroupMember -Identity $groupWithAccess.samAccountName | Where-Object {($_.objectClass -eq "user")}
$memberObject += New-Object psobject -Property #{
fileshare = $accessRights.Path.Split("\")[-1]
group = $groupWithAccess.name
members = $members.name -join ";"
}
}
#>
$memberObject += New-Object psobject -Property #{
fileshare = $accessRights.Path.Split("\")[-1]
group = $groupWithAccess.name
members = $members.name -join ";"
}
}
$memberObject | Export-CSV -Path "C:\Temp\test.csv" -Delimiter "," -NoTypeInformation
Please, since you have not showed any code you have tried yourself, make sure to read this code and try to fully understand what is happening. Then adjust it to your needs and finally try and test it out. Good luck!

PowerShell processing of large users very slow - Is there a better way?

I have been using for Office365 Licence Tracking. Actually it looks like good but but it takes too much time to complete the process. most of time is spent by Get-MsolUser it may be improved calculating them in parallel (while processing user 1 you're already fetching user 2's data and so on...) BTW we have about 3000+ cloud user How can I improve the speed of the script?
$T1 = #()
$O365Users = Get-MsolUser -All
ForEach ($O365User in $O365Users)
{
$ADuser = Get-ADUser -Filter { UserPrincipalName -eq $O365User.UserPrincipalName } -Properties whenCreated, Enabled, lastlogondate
$O365Stats = Get-MailboxStatistics $O365User.DisplayName -ErrorAction SilentlyContinue
$O365Smtp = Get-Recipient $O365User.DisplayName -ErrorAction SilentlyContinue
If ($O365Stats -and $O365Smtp) {
If (($ADUser.Enabled -eq $true) -and ($O365User.isLicensed -eq $true))
{
$T1 += New-Object psobject -Property #{
CollectDate = $(Get-Date);
ADUserUPN = $($ADUser.UserPrincipalName);
O365UserUPN = $($O365User.UserPrincipalName);
ADUserCreated = $($ADUser.whenCreated);
ADUserEnabled = $($ADUser.Enabled);
ADLastLogonDate = $($ADUser.LastLogonDate);
O365Licensed = $($O365User.isLicensed);
O365LastLogonTime = $($O365Stats.LastLogonTime);
O365SMTPAddress = $($O365Smtp.PrimarySMTPAddress)
}
}
}
}
$T1 = $T1 | Sort-Object -Property ADUserCreated
$T1 | Format-Table
$T1 | Export-Csv -Path $OutputFile -NoTypeInformation
Write-Host "Output to $OutputFile"
Using a pipeline, filtering early on, and avoiding appending to an array should already speed things up considerably:
Get-MsolUser -All | Where-Object {
$_.IsLicensed
} | ForEach-Object {
$upn = $_.UserPrincipalName
Get-ADUser -Filter "UserPrincipalName -eq '$upn'" -Properties whenCreated, Enabled, lastlogondate
} | Where-Object {
$_.Enabled
} | ForEach-Object {
$O365Stats = Get-MailboxStatistics $_.DisplayName -ErrorAction SilentlyContinue
$O365Smtp = Get-Recipient $_.DisplayName -ErrorAction SilentlyContinue
if ($O365Stats -and $O365Smtp) {
New-Object -Type PSObject -Property #{
'CollectDate' = Get-Date
'ADUserUPN' = $_.UserPrincipalName
'O365UserUPN' = $_.UserPrincipalName
'ADUserCreated' = $_.whenCreated
'ADUserEnabled' = $_.Enabled
'ADLastLogonDate' = $_.LastLogonDate
'O365Licensed' = $true
'O365LastLogonTime' = $O365Stats.LastLogonTime
'O365SMTPAddress' = $O365Smtp.PrimarySMTPAddress
}
}
} | Sort-Object -Property ADUserCreated | Export-Csv -Path $OutputFile -NoType
Also, why the heck is everybody so infatuated with subexpressions? Use them where you need them. Don't obfuscate your code with them when they're unnecessary.
To give you a set off with parallelism in Powershell.
I would like you to go through the PS Workflows.
We have -parallel in that which will help you in parallel call.
Apart from that, we have one function for Invoke-Parallel
This is the link for it : Invoke-Parallel Function
Note: Examples are mentioned inside the function itself . You can use get-help with that function also once compiled.

Error Handling Issue with Get-ADComputer in a foreach loop

I am running into an error handling issue and have tried a lot of different attempts without much luck. I have a data set and am trying to compare it against AD. A machine may exist in the data set, but not in AD. I would like to retain that information in my end array, but am running into a terminating error:
Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException
$data = Import-Csv -Path .\data.csv
$cdata = #()
foreach ($data in $data) {
foreach ($addata in (Get-ADComputer -Identity $data.Name -Properties LastLogonDate | Select-Object LastLogonDate)) {
$combine = #{
"Name" = $data.Name
"LastPolicyRequest" = $data.LastPolicyRequest
"LastLogonDate" = $addata.LastLogonDate
}
$cdata += New-Object psobject -Property $combine
}
}
Get-ADComputer (and other AD cmdlets) throw this exception when you try to get an object by identity that doesn't exist. Use the -Filter parameter to avoid this issue. Also, don't use the same variable for item and set ($data in $data).
$csv = Import-Csv -Path '.\data.csv'
foreach ($data in $csv) {
$name = $data.Name
foreach ($addata in (Get-ADComputer -Filter "SamAccountName -eq '$name'" -Properties LastLogonDate | Select-Object LastLogonDate)) {
...
}
}
If you just want to add the last logon date to those records that are present in AD you could do something like this:
$cdata = Import-Csv -Path '.\data.csv' |
Select-Object Name, LastPolicyRequest, #{n='LastLogon';e={
Get-ADComputer -Filter "SamAccountName -eq '$($_.Name)'" -Properties LastLogonDate |
Select-Object -Expand LastLogonDate
}}

Append CSV output coding issue

Hi i created the below script to audit local admin groups on remote hosts. It works fine, but since it only outputs the data after it has went through all the hosts, i am afraid the array will run out of buffer space before it has a chance to export to csv, so i have been trying to have it create and append the output from each host as it goes through the list except i cannot get the headers to display on the first line and append additonal lines below it. Below is the output i get when i try to append. The italicized words should be the headers and the other info should be listed in the next row. what am i doing wrong?
#{Server=pc1; Members=Administrator;DistinguishedName=DC=Domain,DC=com
This is how it should look. It looks this way if i dont append and i let it create the csv after it has finished going through the list of hosts
Server Members DistinguishedName
host1 Administrator;Admin2 DC=DOMAIN,DC=COM
$servers= get-content "C:\Scripts\AD Audits\Local Admin\workstations.txt"
$output = "c:\temp\local admin audit $CurrentDate.csv"
$results = #()
$disconnected = "Did not respond"
foreach($server in $servers)
{
$connected = Test-Connection $server -count 1 -quiet
if ($connected) {
"$server responded" | Out-File -append "c:\temp\LocalAdmin goodhosts $CurrentDate.txt"}
else {
"$server did not respond" | Out-File -append "c:\temp\LocalAdmin badhosts $CurrentDate.txt"}
$group =[ADSI]"WinNT://$server/Administrators"
$members = $group.Members() | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) }
$results += New-Object PsObject -Property #{
DistinguishedName = (get-adcomputer ($server) -properties * | select distinguishedname).distinguishedname
Server = $server
Members = $members -join ";"
}
$results | Export-Csv $Output -NoTypeInformation
}`
if($connected -eq $True) {
New-Object PSObject -Property #{
DistinguishedName = (Get-ADComputer $_).DistinguishedName
Server = $_
Members = $members -join ";"
}} else {write-host ""}
My suggestion is to use the pipeline rather than a foreach statement, so each object is written to the file as soon as it's processed.
$servers | ForEach-Object{
$connected = Test-Connection $_ -Count 1 -Quiet -ErrorAction SilentlyContinue
$state = if($connected) {"$_ responded"} else {"$_ did not respond"}
$state | Out-File -Append "c:\temp\LocalAdmin goodhosts $CurrentDate.txt"
$group =[ADSI]"WinNT://$_/Administrators,group"
$members = $group.Members() | ForEach-Object {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) }
if($connected)
{
New-Object PSObject -Property #{
DistinguishedName = (Get-ADComputer $_).DistinguishedName
Server = $_
Members = $members -join ";"
}
}
} | Export-Csv $Output -NoTypeInformation