This is a powershell/AD/Exchange question....
I'm running a script against a number of Users to check some of their attributes however I'm having trouble getting this to output to CSV. The script runs well and does exactly what I need it to do, the output on screen is fine, I'm just having trouble getting it to directly export to csv.
The input is a comma seperated txt file of usernames (eg "username1,username2,username3")
I've experimented with creating custom ps objects, adding to them and then exporting those but its not working....
Any suggestions gratefully received..
Thanks
George
$array = Get-Content $InputPath
#split the comma delimited string into an array
$arrayb = $array.Split(",");
foreach ($User in $arrayb)
{
#find group memebership
Write-Host "AD group membership for $User"
Get-QADMemberOf $User
#Get Mailbox Info
Write-Host "Mailbox info for $User"
Get-Mailbox $User | select ServerName, Database, EmailAddresses, PrimarySmtpAddress, WindowsEmailAddress
#get profile details
Write-Host "Home drive info for $User"
Get-QADUser $User| select HomeDirectory,HomeDrive
#add space between users
Write-Host ""
Write-Host "******************************************************"
}
Write-Host "End Script"
EDITED....
Methods I have tried for exproting (showing only the for loop/export code)
Method1
$AllData = #()
foreach ($User in $arrayb)
{
#set title for this user
#Write-host "Details for $User"
#find out their group memebership
Write-Host "AD group membership for $User"
$AdMemberOf = Get-QADMemberOf $User
Write-Host "ad completed"
Write-Host ""
Write-Host ""
#Get Mailbox Info
Write-Host "Mailbox info for $User"
$ExInfo = Get-Mailbox $User | select ServerName, Database, EmailAddresses, PrimarySmtpAddress, WindowsEmailAddress
Write-Host "ex completed"
Write-Host ""
Write-Host ""
#get profile details
Write-Host "Home drive info for $User"
$HomeInfo = Get-QADUser $User| select HomeDirectory,HomeDrive
Write-Host "home drive completed"
#add space between users
Write-Host ""
Write-Host "******************************************************"
$ReturnedObj = New-Object PSObject
$ReturnedObj | Add-Member NoteProperty -Name "AD Group Membership for $User" -Value $AdMemberOf
$ReturnedObj | Add-Member NoteProperty -Name "Exchange details for $User" -Value $ExInfo
$ReturnedObj | Add-Member NoteProperty -Name "Home drive info for $User" -Value $HomeInfo
Write-Host $ReturnedObj
$AllData += $ReturnedObj
}
Write-Host "starting csv export"
Write-Output $AllData |export-csv -Path $OutputPath -notype -force
Write-Host "End Script"
Method2
$ExportInfo = #()
foreach ($User in $arrayb)
{
$UserInfo = New-Object System.Object
#set title for this user
$ExportInfo += "Details for $User"
#Get Mailbox Info
$ExportInfo += Get-Mailbox $User
#find out their group memebership
$ExportInfo += Get-QADMemberOf $User
#get profile details
$ExportInfo += Get-QADUser $User| select HomeDirectory,HomeDrive
#add space between users
$ExportInfo += ""
$ExportInfo += ""
}
$ExportInfo | Export-Csv -Path $OutputPath ;
EDITED:
Thanks to suggestion from Stej I now have the code below... Still doesn't work correctly. I have added a check to confirm the user exists in AD as well. First problem is with getting a users AD group membership - if I put a break point in and look at the value of a a specific users AD membership, the value in teh varialbe is error "Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value." No idea what's causing this, so I've just ignored it for now and commented out those lines for AD membership to concentrate on gettng the rest working.
With the lines commented out, the script runs fine and again, with a breakpoint at the bottom I can look at the value of $ExportInfo and they all look fine, ie they have all been saved into the variable correctly. However, it won't output. As you can see, I put a command to get $ExportInfo written to screen but that doesn't show anything. When it attempts to execute the Export line (ie "Export-Csv $ExportInfo -Path $OutputPath") it errors with:
"Cannot convert 'System.Object[]' to the type 'System.Char' required by parameter 'Delimiter'. Specified method is not supported.
At :line:80 char:10
+ Export-Csv <<<< $ExportInfo -Path $OutputPath"
I chnaged the line to "$ExportInfo | Export-Csv -Path $OutputPath" and it now exports to CSV... No idea why??? Two issues though... as noted above, AD groups aren't working and the field Email Addresses (which should return something like { SMTP:j.smith#domain.com, smtp: j.smith#domain.com.au, smtp: smithj#exchange.domain.com.au and SIP:j.smith#domain.com.au}) just shows up in the CSV as "Microsoft.Exchange.Data.ProxyAddressCollection". Again, checking in $ExportInfo, the addresses are there....
Thanks
$ExportInfo = #()
foreach ($User in $arrayb)
{
$CheckUser = Get-QADUser -Name $User
if (!$CheckUser)
{
$CountUser++
Write-Warning "############################################"
Write-Warning "$user not found in AD"
Write-Warning "############################################"
}
else
{
$CountUser++
$UserInfo = New-Object System.Object
#find out their group memebership
Write-Host "AD group membership for $User"
#$Temp = Get-QADMemberOf $User
#$UserInfo | Add-Member NoteProperty -Name "AD Group Membership" -Value $Temp.Name
#set title for this user
#Write-host "Details for $User"
#Get Mailbox Info
Write-Host "Mailbox info for $User"
$Temp = Get-Mailbox $User #| select ServerName, Database, EmailAddresses, PrimarySmtpAddress, WindowsEmailAddress
$UserInfo | Add-Member NoteProperty -Name "ServerName" -Value $Temp.ServerName
$UserInfo | Add-Member NoteProperty -Name "Database" -Value $Temp.Database
$UserInfo | Add-Member NoteProperty -Name "Email Addresses" -Value $Temp.EmailAddresses
$UserInfo | Add-Member NoteProperty -Name "Primary SMTP" -Value $Temp.PrimarySmtpAddress
$UserInfo | Add-Member NoteProperty -Name "Windows Email Address" -Value $Temp.WindowsEmailAddress
#$ReturnedObj | Add-Member NoteProperty -Name
#get profile details
Write-Host "Home drive info for $User"
$Temp = Get-QADUser $User #| select HomeDirectory,HomeDrive
$UserInfo | Add-Member NoteProperty -Name "Home Directory Location" -Value $Temp.HomeDirectory
$UserInfo | Add-Member NoteProperty -Name "Home Drive Mapped To" -Value $Temp.HomeDrive
#add space between users
Write-Host ""
Write-Host "******************************************************"
$ExportInfo += $UserInfo
}#end else
}
Write-Host "blah"
Write-Host $ExportInfo
Export-Csv $ExportInfo -Path - $OutputPath
Write-Host "Number of Users processed: $CountUser"
Is there any error message? Is there something in $error[0]? What does it mean that it is not working?
Edited:
In your second method you create $UserInfo object, but you don't use it. I guess it was intended to be added to $ExportInfo. Instead of this you add bare strings to $ExportInfo and that's why you gave such a strange content of your csv file.
How it should be done correctly:
$UserInfo = New-Object System.Object
$ReturnedObj | Add-Member NoteProperty -Name "User Name" -Value $user.Name
$ReturnedObj | Add-Member NoteProperty -Name "User otherValue" -Value $user.othValue
#Get Mailbox Info
$mailBox = Get-Mailbox $User
$ReturnedObj | Add-Member NoteProperty -Name "User Mail box size" -Value $mailBox.Size
$ReturnedObj | Add-Member NoteProperty -Name "User Mail box -count of messages" -Value $user.countOfMessages
... # and so on
$ExportInfo += $ReturnedObj
Note that the objects that you want to export to csv have to have properties of type strings, integers, bools etc. Not composed objects like $User or the ones returned by Get-Mailbox $User. The values in csv have to be primitive. That's the reason.
That applies for your first example where you add NoteProperty with value $AdMemberOf. That is object itself that can not be exported to csv. You have to create bunch of properties for every interesting property of $AdMemberOf.
Turned out I needed to use some foreach loops to get the info I needed.... So:
foreach($user in $array)
{
$temp = Get-QADUser -Name $User
if (!$temp)
{
$log += "????????????????" + "`n"
$log += "$user not found in AD" + "`n"
$log += "????????????????" + "`n"
}
else
{
#find out their group memebership
$log += "AD group membership for $User" + "`n"
$temp = Get-QADMemberOf $User
foreach ($drive in $temp)
{
$temp2 = $drive
$temp2 = $temp2.Substring(6)
$log += "`t" + $drive + "`n"
}#end foreach for drive loop
$temp = Get-Mailbox $User
if ($temp.RecipientType -like "UserMailbox")
{
$log += "Mailbox info for $User" + "`n"
#$log += Get-Mailbox $User | select ServerName, Database, EmailAddresses, PrimarySmtpAddress, WindowsEmailAddress
$log += "Email Server: " + $temp.ServerName + "`n"
$log += "Email Database: " + $temp.Database + "`n"
$log += "Primary SMTP: " + $temp.PrimarySmtpAddress + "`n"
$log += "Windows Email Address: "+ $temp.WindowsEmailAddress + "`n"
#$log += "`n"
foreach ($e in $temp.EmailAddresses)
{
$log += "`t" + "Email Addresses: " + $e + "`n"
}
}
else
{
$log += "########" + "`n"
$log += "$User is not a MailboxUser, IE no Exchange Mailbox" + "`n"
$log += "########" + "`n"
}
$log += "Home drive info for $User" + "`n"
$temp = Get-QADUser $User| select HomeDirectory,HomeDrive
$log += "Home Directory: " + $temp.HomeDirectory + "`n"
$log += "Home Drive Letter: " + $temp.HomeDrive + "`n"
$tempvar = [string] $temp.HomeDirectory
if ($tempvar -eq "")
{
$noHomedirectory += $User + "`n"
$countNoHOmeDirectory ++
}
}#end of the main if/else to determine if the AD account exists
$OutputPath = "C:\SomeFolder\"+"User_Report_"+([datetime]::Now).tostring("yyyyMMddhhmmss")+".txt"
$log | Out-File -FilePath $OutputPath
I then dump all the specifc logs (eg $noHomeDirectory etc) in the body of an email, attach the complete log as exported above and send it to myself.
Thanks for everyone's suggestions above and sorry for the delay in posting back the answer...
Related
I'm working on a script that allows me to fetch all of our Teams groups by their IDs and list the Id, Name, Owner(s), Member(s) and Guest(s).
The code works to a certain point, I get all the needed information, but it seems to be limiting it to 2 owners, 4 members and no guests...
When I run the code with adding it to a PSObject and simply do a write-host all the data is there, but I can't append it correctly to a CSV.
Code below, its either a limitation of the PSObject or I am doing something wrong/ missing something (hoping on the 2nd part ;) )
try
{
$host.Runspace.ThreadOptions = "ReuseThread"
# Get the credentials
Connect-AzureAD
# Connect to Microsoft Teams
Connect-MicrosoftTeams
# Get all the teams from tenant
[array]$teamColl = $null
[array]$ownerColl = $null
[array]$memberColl = $null
[array]$guestColl = $null
$teamColl=Get-Team
$date = Get-Date -Format "yyyy-MM-dd"
$OutputFile01 = "C:\temp\GetTeamsOwnersAndMembers-$date.csv"
# Clean file
Remove-Item $OutputFile01 -ErrorAction SilentlyContinue
$objectCollection=#()
$ownerCount = 0
$memberCount = 0
$guestCount = 0
# Loop through the teams
foreach($team in $teamColl)
{
$object = New-Object PSObject
# Get the Teams basic information
$object | Add-Member -type NoteProperty -Name ID -Value $team.GroupId
$object | Add-Member -type NoteProperty -Name TeamsName -Value $team.DisplayName
#$object | Add-Member -type NoteProperty -Name Description -Value $team.Description
# Get the Teams owners
$ownerColl = Get-TeamUser -GroupId $team.GroupId -Role Owner
$memberColl = Get-TeamUser -GroupId $team.GroupId -Role Member
$guestColl = Get-TeamUser -GroupId $team.GroupId -Role Guest
#Write-Host "$ownerColl"
#Write-Host "$memberColl"
#Write-Host "$guestColl"
# Loop through the owners
foreach($owner in $ownerColl)
{
$ownerCount++
$object | Add-Member -type NoteProperty -Name Owner_$ownerCount -Value $owner.User
}
# Loop through the members
foreach($member in $memberColl)
{
$memberCount++
$object | Add-Member -type NoteProperty -Name Member_$memberCount -Value $member.User
}
# Loop through the guests
foreach($guest in $guestColl)
{
$guestCount++
$object | Add-Member -type NoteProperty -Name Guest_$guestCount -Value $guest.User
}
# Reset counters
$ownerCount = 0
$memberCount = 0
$guestCount = 0
$objectCollection += $object
}
$objectCollection | Export-Csv $OutputFile01 -NoTypeInformation
}
catch [System.Exception]
{
Write-Host -ForegroundColor Red $_.Exception.ToString()
}
finally
{
Write-Host "Done"
}
Was able to solve it, I needed to use the -join to add the additional users :)
Working code:
try
{
$host.Runspace.ThreadOptions = "ReuseThread"
# Get the credentials
Connect-AzureAD
# Connect to Microsoft Teams
Connect-MicrosoftTeams
# Get all the teams from tenant
[array]$teamColl = $null
[array]$ownerColl = $null
[array]$memberColl = $null
[array]$guestColl = $null
$teamColl=Get-Team
$date = Get-Date -Format "yyyy-MM-dd"
$OutputFile01 = "C:\temp\GetTeamsOwnersAndMembers-$date.csv"
# Clean file
Remove-Item $OutputFile01 -ErrorAction SilentlyContinue
$GroupsCSV=#()
Write-Host -ForegroundColor Green "Processing Groups"
# Loop through the teams
foreach($team in $teamColl)
{
$ownerCount = 0
$memberCount = 0
$guestCount = 0
Write-Host -ForegroundColor Yellow -NoNewline "."
$ownerColl = Get-TeamUser -GroupId $team.GroupId -Role Owner
$ownerCollection=#()
# Loop through the owners
foreach($owner in $ownerColl)
{
$ownerCount++
$ownerCollection += $owner.User
}
$memberColl = Get-TeamUser -GroupId $team.GroupId -Role Member
$memberCollection=#()
# Loop through the members
foreach($member in $memberColl)
{
$memberCount++
$memberCollection += $member.User
}
$guestColl = Get-TeamUser -GroupId $team.GroupId -Role Guest
$guestCollection=#()
# Loop through the guests
foreach($guest in $guestColl)
{
$guestCount++
$guestCollection += $guest.User
}
# Create CSV file line
$GroupsRow = [pscustomobject]#{
GroupId = $team.GroupId
Name = $team.DisplayName
OwnerCount = $ownerCount
MemberCount = $memberCount
GuestCount = $guestCount
Owners = $ownerCollection -join " | "
Members = $memberCollection -join " | "
Guests = $guestCollection -join " | "
}
# Add to export array
$GroupsCSV+=$GroupsRow
}
# Export to CSV
Write-Host -ForegroundColor Green "`nCreating and exporting CSV file"
$GroupsCSV | Export-Csv -NoTypeInformation -Path $OutputFile01
}
catch [System.Exception]
{
Write-Host -ForegroundColor Red $_.Exception.ToString()
}
finally
{
Write-Host "Done"
}
I'm having trouble running this script. I am supposed to call using Strong authentication requirements which means users that enabled MFA using the MFA portal will be displayed if I am not mistaken.
Connect-MsolService
$role = getMsolRole -rolename "Company Administrator"
$rm = get-MsolRoleMember -RoleObjectId $role.ObjectId
foreach ($c in $rm)
{
Get-MsolUser -UserPrincipalName $c.EmailAddress | Select DisplayName, UserPrincipalName, #{N="MFA Status"; E={ if($_.StrongAuthenticationRequirements.Count -ne 0) { $_.StrongAuthenticationRequirements.State.toString() } else 'Disabled' }}
Error is
At line:9 char:225
+ ... { $_.StrongAuthenticationRequirements.State.toString() } else 'Disabl ...
+ ~
Missing statement block after 'else' keyword.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingStatementBlockAfterElse
Edited:
If even you can clarify the difference between calling MFA using StrongAuthenticationMethods and using StrongAuthenticationRequirements that will be good. So I can reproduce this code.
Problem with our MFA audit code is that it displays that system administrators are having MFA disabled even though they are claiming they are already enabled.
This is the audit code which returns admins with wildcard administrator are disabled.
Function Get-O365AdminMFAStatus{
$AdminData=#()
$objRole=#()
$Domain = $(get-addomain).dnsroot
$Log = "C:\temp\Audit\$Domain O365 Admin MFA Status $(get-date -f yyyy-MM-dd).csv"
try{
$Roles = Get-MsolRole | where {$_.name -LIKE "*Administrator*"}
$Roles = ($Roles).name
foreach ($Role in $Roles){
$Members = Get-MsolRoleMember -RoleObjectId (Get-MsolRole -RoleName $Role).ObjectId
foreach ($Member in $Members){
$MsUser = $Member | Get-MsolUser
if($MsUser.StrongAuthenticationMethods.Count -eq 0) {
$Enabled = "False"
write-host $Role - $Member.DisplayName "No MFA enabled" -foregroundcolor red
}
Else{
$Enabled = "True"
write-host $Role - $Member.DisplayName "MFA enabled" -foregroundcolor green
}
Try{
$Exist = [bool](Get-mailbox $MsUser.UserPrincipalName -erroraction SilentlyContinue)
if ($Exist){
$MBStats = Get-MailboxStatistics $MsUser.UserPrincipalName
$LastLogon = $MBstats.LastLogonTime
}
Else{
$LastLogon = "N/A"
}
}
Catch{
$LastLogon = "N/A"
}
$objRole = New-Object -TypeName PSObject
$objRole | Add-Member -MemberType NoteProperty -Name "Role Name" -Value $Role
$objRole | Add-Member -MemberType NoteProperty -Name "Display Name" -Value $Member.DisplayName
$objRole | Add-Member -MemberType NoteProperty -Name "UPN" -Value $Member.UserPrincipalName
$objRole | Add-Member -MemberType NoteProperty -Name "Licensed" -Value $Member.IsLicensed
$objRole | Add-Member -MemberType NoteProperty -Name "Last Logon" -Value $LastLogon
$objRole | Add-Member -MemberType NoteProperty -Name "MFA Enabled?" -Value $Enabled
$AdminData += $objRole
}
}
$AdminData | Export-Csv -NoTypeInformation $Log
write-host ""
write-host "CSV Export Complete to $Log" -foregroundcolor yellow
}
Catch{
Write-host "There was an error: $($_.Exception.Message)"
}
}
Get-O365AdminMFAStatus
anyways if you have clarifications with the question I will edit.
From the error you receive it's quite obvious, what you should correct:
Missing statement block after 'else' keyword
You're missing curly braces after else so it should be:
else {'Disabled'}
I checked your cmdlet against my accounts (I have 2FA enabled) and StrongAuthenticationRequirements is empty object for me (checked on many accounts - attempt to clarify below). I think that you should use StrongAuthenticationMethods property instead. It contains information about channels that were configured for 2FA.
Finally, your code would look like this:
foreach ($c in $rm) {
Get-MsolUser -UserPrincipalName $c.EmailAddress | Select DisplayName, UserPrincipalName,
#{N="MFA Status"; E={ if($_.StrongAuthenticationMethods.Count -ne 0) { "$($_.StrongAuthenticationMethods.Count) methods found" } else {'Disabled'} }}
}
But you might notice some entries erroring with such info:
Get-MsolUser : Cannot bind argument to parameter 'UserPrincipalName' because it is null.
To get rid of that, important thing is to filter out ServicePrincipal members from Get-MsolRoleMember (for example, I have RMS and PowerBI Information Service, you might have none or different ones):
foreach ($c in $rm | Where-Object {$_.rolemembertype -eq 'user'}) {
Get-MsolUser -UserPrincipalName $c.EmailAddress | Select DisplayName, UserPrincipalName,
#{N="MFA Status"; E={ if($_.StrongAuthenticationMethods.Count -ne 0) { "$($_.StrongAuthenticationMethods.Count) methods found" } else {'Disabled'} }}
}
Clarification about StrongAuthenticationMethods and StrongAuthenticationRequirements
From what I read here it seems like StrongAuthenticationRequirements applies for per-user MFA. If your tenant is using Conditional Access based MFA, that property might be empty (checked on my tenant). So I guess StrongAuthenticationMethods is more reliable.
NOTE: I also tested the part of long code you have posted and it works correctly for me. And you have a typo in getMsolRole - should be Get-MsolRole
I am trying to pull a list of all users in my O365 Tenant and if they are licensed, the list of sublicenses they have been granted. The following code works great to list out my sublicenses:
$userlicensetest = get-msoluser -userprincipalname "steve.dorr#merrillcorp.com"
$userlicensetest.licenses[0].servicestatus
ServicePlan :: ProvisioningStatus
----------- :: ------------------
INTUNE_O365 :: PendingActivation
YAMMER_ENTERPRISE :: PendingInput
OFFICESUBSCRIPTION :: Success
So I tried to modify code I found online to include the sublicense information. Here is what I have built so far:
$ReportPath = "c:\users\userlist.csv"
Add-Content -value ("UserPrincipalName"+","+"IsLicensed"+","+ "Licenses”"+","+ "SubLicenses") -Path $ReportPath
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
$UserPrincipalName = $User.UserPrincipalName
$IsLicensed = $User.IsLicensed
$Licenses = $User.Licenses.AccountSkuId
$SubLicenses = $User.Licenses[0].servicestatus
Add-Content -value ($UserPrincipalName+","+$IsLicensed+","+$Licenses+","+$SubLicenses) -Path $ReportPath
}
The problem is it is only pulling the header line from the sublicense query and not all the lines of detail. So the line for myself in the CSV looks like:
Steve.Dorr#MerrillCorp.com TRUE mymerrillcorp:ENTERPRISEPACK Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus
Which does not give me the detail lines I needed.
How do I pull all the lines that Licenses[0].servicestatus generates into the CSV file? I don't care whether it flattens it out and goes across more columns, or takes up multiple lines in Excel.
Thanks.
So since I posted this question I have worked a little on it. I do not have a perfect solution that puts this into a nice neat CSV file, but I do have a routine which now drops all this information into a text file. Below is my code.
$MyCredentials = Get-Credential -Message "Enter Office 365 Email & Password"
Connect-MsolService -Credential $MyCredentials
$ReportFile = "C:\temp\O365Data.txt"
" " | Out-File $ReportFile #erases the file if it exists
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
$UserPrincipalName = $User.UserPrincipalName
$IsLicensed = $User.IsLicensed
$Licenses = $User.Licenses.AccountSkuId
$SubLicenses = $User.Licenses[0].servicestatus
$OneLine = $UserPrincipalName + " " + $IsLicensed
$OneLine| Out-File $ReportFile -Append
if($User.Licenses[0].servicestatus) {$User.Licenses[0].servicestatus | Out-File $ReportFile -Append}
}
To create the report you'll need to create custom objects to hold the properties you are interested in.
The following will take into account all the different licenses that could be applied to a user and then generate a csv with one user listed per line.
# Connect to o365
$MyCredentials = Get-Credential -Message "Enter Office 365 Email & Password"
Connect-MsolService -Credential $MyCredentials
# Prepare result file
$ExportFile = ".\o365Output.csv"
Remove-Item $ExportFile
$Result = #()
# Query all Msol Users
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
# Generate a new object for each user
$ReturnObject = [pscustomobject]#{
UserPrincipalName = $User.UserPrincipalName
IsLicensed = $User.IsLicensed
Licenses = [string]$User.Licenses.AccountSkuId
}
# In the event multiple licenses are found append properties for each license
foreach ($License in $User.Licenses)
{
if($($License.ServiceStatus.ServicePlan.ServiceName).count -eq 1)
{
$ReturnObject | Add-Member -MemberType NoteProperty -Name $License.ServiceStatus.ServicePlan.ServiceName -Value $License.ServiceStatus.ProvisioningStatus
}
else
{
for($i = 0; $i -lt $($License.ServiceStatus.ServicePlan.ServiceName).count; $i++)
{
$ReturnObject | Add-Member -MemberType NoteProperty -Name $License.ServiceStatus.ServicePlan.ServiceName[$i] -Value $License.ServiceStatus.ProvisioningStatus[$i]
}
}
}
$Result += $ReturnObject
}
# Combine properties from all returned objects
$Properties = $Result | ForEach-Object { Get-Member -InputObject $_ -MemberType NoteProperty | Select-Object -ExpandProperty Name } | Select-Object -Unique | Sort-Object
$Headers = #("UserPrincipalName")
$Headers += $Properties -notlike "UserPrincipalName"
# Export to csv
$Result | Select-Object $Headers | Export-Csv -NoTypeInformation $ExportFile
# Open csv
Invoke-Item $ExportFile
I am trying to access the group information (group name and its members) who can access a Document Library in a SharePoint website.
Allow me to explain using an example:
For a site named "MyCompany" there are sub-sites as Finance, HR,etc. Each sub-site has a document library named "Shared Document", and in each Shared document has folders and sub-folders.
The access to these Shared Document is given to different groups like HR Managers are given access to Shared Document in HR sub-site.
I want to fetch the data of the groups that can access these Shared Documents as shown below:
Site : MyCompany (URL/Title)
Sub-site : HR (URL/Title)
Folder : Shared Document (Name/URL)
Group : HR Managers
Member : John
I am able to fetch group information up to sub-site level. But enable to do so at document library and sub-folder level.
My script so far:
Add-PSSnapin Microsoft.Sharepoint.Powershell
Start-SPAssignment -Global
$sysdate = Get-Date -UFormat "%m_%d_%y"
$sites = get-spsite http://MyCompany_Url
$sites
foreach($site in $sites)
{
[array]$users = $null
#$name = $site.RootWeb.Title
write-host -foregroundcolor green "Working on Site Collection: "$site.RootWeb.Title "..."
foreach ($web in $site.AllWebs)
{
write-host "--Working on web:" $web.Title "..." -foregroundcolor yellow
$webTitle = $web.Title
foreach($l in $web.Lists)
{
#Check for only document libraries
if($l.BaseType -eq "DocumentLibrary")
{
if($l.Title -eq "Shared Documents")
{
Write-Host $l.Title"(Web: "$web.Title")" -ForegroundColor DarkRed -BackgroundColor White
$grpinfo = $l.RoleAssignments
$groupinfo
<#foreach($item in $l.Items)
{
$ItemUrl = $item.Url
$ItemTitle = $item.Title
Write-Host $item.Url" : "$item.Title -ForegroundColor green
}#>
}
}
}
# # Getting group ifo at sub-site level
foreach ($group in $web.groups)
{
write-host "----Collecting users from group:"$group.Name "..." -ForegroundColor Magenta
#$GrpPermission = $roles.Type + ":"+ $roles.Name
foreach($user in $group.users)
{
$usrname = $user.DisplayName
$UserPerm = $user.Roles
$GroupPerm = $group.Roles
[string]$UPerm = #($UserPerm.Name) -join ","
[string]$GPerm = #($GroupPerm.Name) -join "," #-replace (" ", ",")
Write-Host "################"
$GPerm
Write-Host "----------------"
$UPerm
Write-Host "################"
write-host "-----"$user.DisplayName" : "$group.name" : "$web.Title" : "$site.RootWeb.Title " Group Permission : "$group.Roles " Member Permission : "$user.Roles -ForegroundColor Blue -BackgroundColor White
$users = new-object psobject
$users | Add-Member NoteProperty -name "Webpart Name" -value $web.Title
$users | add-member noteproperty -name "Groups" -value $group.name
$users | add-member noteproperty -name "Display Name" -value $user.DisplayName
$users | add-member noteproperty -name "Group Permission(s)" -value $GPerm
$users | add-member noteproperty -name "Webpart URL" -value $web.Url
#$users
$users | export-csv -path "E:\MyCompany_Groups_report_$sysdate.xls" -Append -NoTypeInformation
}
}
$web.Dispose()
}
$site.Dispose()
}
Stop-SPAssignment -Global
As part of my job i'm constantly auditing active directory for given properties of cross domain accounts.
I've constructed a powershell script to output information to a CSV based on properties given to the script. This is fine, the script works beautifully for a small list of people however i'm noticing that the script slows down considerably when i provide a big list of users to audit.
This is the script:
$inputfile = "C:\Powershell\input.txt"
$users = Get-Content $inputfile
$audit = Read-Host "Audit Name"
$csv = ".\output\Audit\$audit.csv"
$failed = #()
$serv = #("server1", "server2", "server3")
if((Test-Path $csv) -eq $true){Remove-Item $csv}
foreach($domain in $serv)
{
$count = $users.Count
for( $i=0; $i -le $count - 1; $i++ )
{
if (($users.Get($i)) -ne "")
{
try
{
Write-Host "Checking for $($users.get($i)) on" -NoNewline
switch($domain)
{ # with fancier text for which domain we're searching
"server1" {write-host "...Server1" -ForegroundColor Cyan -NoNewline; $domainCsv = "Server1"}
"server2" {Write-Host "...Server2" -ForegroundColor White -NoNewline; $domainCsv = "Server2"}
"server3" {Write-Host "...Server3" -ForegroundColor Magenta -NoNewline; $domainCsv = "Server3"}
}
$usr = Get-ADUser -Identity $users.get($i) -Properties $properties -Server $domain | ? { ($_.distinguishedname -notlike '*Suspended*')}
if ($usr -ne $null)
{
$usr = Get-ADUser -Identity $users.get($i) -Properties $properties -Server $domain | ? { ($_.distinguishedname -notlike '*Deletion*')}
if ($usr -ne $null)
{
Write-Host "...Found" -ForegroundColor Green
$userobj = New-Object PSObject
Add-Member -InputObject $userobj -MemberType NoteProperty -Name "User" -Value $($users.Get($i))
Add-Member -InputObject $userobj -MemberType NoteProperty -Name "Domain" -Value $domainCsv
foreach($prop in $properties) {$userobj | Add-Member -MemberType NoteProperty -Name $prop -Value "$($usr.$prop)"}
$userobj | Export-Csv $csv -Append -NoTypeInformation
}
else
{
Write-Host "...Pending Delete" -ForegroundColor Red
$failed += "$($users.Get($i)),Pending deletion on $domainCsv"
}
}
else
{
Write-Host "...Suspended" -ForegroundColor Red
$failed += "$($users.Get($i)),Suspended on $domainCsv"
}
}
catch [System.Exception]
{
Write-Host "...Not found" -ForegroundColor Red
$failed += "$($users.Get($i)),Could not find on $domainCsv"
} # </Try
} # </If user ""
} # </For users
} # </For Domains
Add-Content $csv ""
Add-Content $csv "Those who failed, (Not found or Suspended or Pending deletion)"
Add-Content $csv ""
Add-Content $csv "User,Domain"
foreach($fail in $failed) {Add-Content $csv $fail}
Write-Host " "
Write-Host " Audit saved to $csv" -ForegroundColor Green
What the script does
Gets input file full of users (one name per line) (mostly 20 or so lines but sometimes the input file has been over 200)
user1
user2
user3
user4
user5
goes through each domain
checks if they're in an OU for suspended accounts
checks if they're in an OU for accounts pending deletion
if not in either OU grabs the information and puts it into a PSObject for insertion into a CSV
after its done it lists the accounts it couldnt find or were in an OU that i dont need to worry about.
As i'm quite new to powershell i have no idea if theres a way i can condense parts of the code to be quicker, i have read a few pages on optimizing powershell but the only change i could see was to change
for( $i=0; $i -le $users.count - 1; $i++ )
to
$count = $users.count
for( $i=0; $i -le $count - 1; $i++ )
My question is: How can i improve my script to loop faster when given more users?
As far as I understand your script spent most of the time in Get-ADUser. You call it twice with the same parameters, using $usr you should call it just one time, your script execution time should bedevided by two.
Another thing, I can't find the defenition of $properties in your script, reducing this list can also reduce the network payload.
Test something like this.
$usr = Get-ADUser -Identity $users.get($i) -Properties $properties -Server $domain | ? { ($_.distinguishedname -notlike '*Suspended*')}
if ($usr.distinguishedname -notlike '*Suspended*')
{
if ($usr.distinguishedname -notlike '*Deletion*')
{