pipelined Powershell cmdlet showing partial result? - powershell

I am writing some C# program that executes PowerShell script.
I have the following line
Get-Mailbox -ResultSize:unlimited |
Get-MailboxPermission |
Where {($_.IsInherited -eq $false) -and !($_.user -like "S-1*") -and !($_.user -like "NT A*") } |
select identity,user,#{n="objectid";e={(get-recipient -identity $_.user).ExternalDirectoryObjectId}}
basically it finds all mailbox permissions and retrieves corresponding ExternalDirectoryObjectId (which is same as Azure ObjectID)
The issue here is that the result returned is different from different machines. I would get all identity, user values, but for expression values that are in bold above, will only start to show up half way through the execution.
for example on computer x
Identity|User|objectid
user1 |userA|
user2 |userA|
user2 |userB|
... |... |
user10|userC|
user11|userC|<objectID1>
user11|userD|<objectID2>
I noticed that on fast computer the objectIDs start showing up late, on slower computers it starts showing up early, however execution times are different.
How do I modify this so that objectGuid is retrieved for all entries? Why is pipelining not waiting until the calculated property objectID is properly retrieved?
If I write a short PowerShell script and use for loops for each mboxpermissions and retrieve them one by one, all of those objectGuids are retrievable. But it's slow.
Thanks for help and Please give me any suggestions!

Related

Export more than 1000 rows in Powershell

I am trying to access some data about Unified Groups using PowerShell.
Get-UnifiedGroup | ? {$_.AccessType -eq "Public"}
This is the command I am using, however I am also trying to export this data to CSV.
So the command becomes
Get-UnifiedGroup | ? {$_.AccessType -eq "Public"} | Export-Csv c:\temp\azureadusers.csv
But it only displays first 1000 results in the csv file and I am trying to get all of the data. I am new to PowerShell so I am still learning this.
How can I achieve this?
you may want too look at the -Filter Parameter too. It's always a good thing to filter as far left as possible. Mostly because it's free Performance gain.
-Filter {AccessType -eq "Public"}

Optimize Get-ADUser filter

In AD, I'm trying to identify user accounts where the same EmployeeID value is populated in 2 or more records. Below is my piece of code (Credit: I'm using a Show-Progress function defined here) and the Get-ADUser command alone has taken more than 2 hours to fetch all the records. The other steps (2 to 5) have been pretty quick. While I've completed the work, I'm trying to know if this could've been done more efficiently with PowerShell.
Get-ADUser -LDAPFilter "(&(ObjectCategory=Person)(objectclass=user)(employeeid=*))" -Properties $properties -Server $server_AD_GC -ResultPageSize 1000 |
# *ISSUE HERE*
# The Get-ADUser extract process seems to work very slow.
# However, it is important to note that the above command will be retrieving more than 200K records
# NOTE: I've inferred that employeeid is an indexed attribute and is replicated to GlobalCatalogs and hence have used it in the filter
Show-Progress -Activity "(1/5) Getting AD Users ..." |
select $selectPropsList -OutVariable results_UsersBaseSet |
Group-Object EmployeeID |
Show-Progress -Activity "(2/5) Grouping on EmployeeID ..." |
? { $_.Count -gt 1 } |
Show-Progress -Activity "(3/5) Filtering only dup EmpID records ..." |
select -Exp Group |
Show-Progress -Activity "(4/5) UnGrouping ..." |
Export-Csv "C:\Users\me\op_GetADUser_w_EmpID_Dupes_EntireForest - $([datetime]::Now.ToString("MM-dd-yyyy_hhmmss")).csv" -NoTypeInformation |
Show-Progress -Activity "(5/5) Exporting ..." |
Out-Null
PS: I've also tried to first export all the user accounts to a csv file and then post-process with Excel but I had to frown because of the size of the dataset and it was both time and memory crunching.
Any suggestion is highly appreciated.
Since we don't know what is in $properties or $selectPropsList, your question is really only about finding out to which users the same EmployeeID has been issued, right?
By default, Get-ADUser already returns these properties:
DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname, UserPrincipalName
So all you need extra is the EmployeeID I guess. Trying to collect LOTS of properties does slow down, so keeping this to a bare minimum helps to speed things up.
Next, by using the Show-Progress script you have linked to, you will slow down the execution of the script considerably. Do you really need to have a progress bar?
Why not simply write the lines with activity steps directly to the console?
Also, piping everything together doesn't help in the speed department either..
$server_AD_GC = 'YourServer'
$selectPropsList = 'EmployeeID', 'Name', 'SamAccountName', 'Enabled'
$outFile = "C:\Users\me\op_GetADUser_w_EmpID_Dupes_EntireForest - $([datetime]::Now.ToString("MM-dd-yyyy_hhmmss")).csv"
Write-Host "Step (1/4) Getting AD Users ..."
$users = Get-ADUser -Filter "EmployeeID -like '*'" -Properties EmployeeID -Server $server_AD_GC -ResultPageSize 1000
Write-Host "Step (2/4) Grouping on EmployeeID ..."
$dupes = $users | Group-Object -Property EmployeeID | Where-Object { $_.Count -gt 1 }
Write-Host "Step (3/4) Collecting duplicates ..."
$result = foreach ($group in $dupes) {
$group.Group | Select-Object $selectPropsList
}
Write-Host "Step (4/4) Exporting ..."
$result | Export-Csv -Path $outFile -NoTypeInformation
Write-Host "All done" -ForegroundColor Green
P.S. Get-ADUser already returns user objects only, so there is no need for the LDAP filter (ObjectCategory=Person)(objectclass=user). Using -Filter "EmployeeID -like '*'" is probably faster
This answer complements Theo's helpful answer and focuses on showing progress during the operation:
The linked Show-Progress function, which is the latest as of this writing:
has an outright bug, in that it doesn't pass pipeline input through (the relevant line is accidentally commented out)
is conceptually flawed in that it doesn't use a process block, which means that all pipeline input is collected first, before it is processed - which defeats the idea of a progress bar.
Therefore, you Show-Progress calls won't show progress until the previous command in the pipeline has output all of its output. A simple alternative is to break the pipeline into separate commands and to simply emit one progress message before each command, announcing the next stage of processing (rather than per-object progress) as shown in Theo's answer.
Generally, there is no way to show the progress of command-internal processing, only the progress of a command's (multi-object) output.
The simplest way to do this via a ForEach-Object call in which you call
Write-Progress, but that comes with two challenges:
In order to show a percent-complete progress bar, you need to know how many objects there will be in total, which you must determine ahead of time, because a pipeline cannot know how many objects it will receive; your only option is to collect all output first (or find some other way to count it) and then use the collected output as pipeline input, using the count of objects as the basis for calculating the value to pass to Write-Progress -PerCentComplete.
Calling Write-Progress for each object received will result in a significant slowdown of overall processing; a compromise is to only call it for every N objects, as shown in this answer; the approach there could be wrapped in a properly implemented function a la Show-Progress that requires passing the total object count as an argument and performs proper streaming input-object processing (via a process block); that said, the mere act of using PowerShell code for passing input objects through is costly.
Conclusion:
Percent-complete progress displays have two inherent problems:
They require you to know the total number of objects to process beforehand (a pipeline has no way of knowing how many objects will pass through it):
Either: Collect all objects to process in memory, beforehand, if feasible; the count of elements in the collection can then serve as the basis for the percent-complete calculations. This may not be an option with very large input sets.
Or: Perform an extra processing step beforehand that merely counts all objects without actually retrieving them. This may not be practical in terms of the additional processing time added.
Object-by-object processing in PowerShell code - either via ForEach-Object or an advanced script/function - is inherently slow.
You can mitigate that somewhat by limiting Write-Progress calls to every N objects, as shown in this answer
Overall it's a tradeoff between processing speed and the ability to show percent-complete progress to the end user.

Get-ADComputer Lastlogondate / Lastlogon

I'm currently asking myself if it is possible to determine the last logon time of any user of a computer object which is connected to an active directory?
I need to find out when any user was logged onto a specific computer which is still online, communicating with the domain but was not in use in the last X days by any user.
I've already tried the following queries:
get-adcomputer $computername -Properties lastlogon | select
#{Name="lastLogon";Expression={[datetime]::FromFileTime($_.'lastLogon')}}
AND
get-adcomputer za31testvmrobin -Properties lastlogondate
I'm expecting the timestamp of the last logondate of a user on a computer object.
Hope you can help me.
I somehow figured it out with help from #boxdog . Thanks for that.
Here is the Powershell Code in one line:
Get-EventLog -LogName Security -InstanceId 4624 -ComputerName $computer |
`where {$_.Message -match "Kontoname: USERNAME" -and
`$_.Message -match "Anmeldetyp: 2" } | select -First 1)
Kontoname = Accountname
Anmeldetyp = Logontype (2 means interactive from console with keyboard & mouse)
The tabulator is needed. You can also use wildcards like an asterisk.
I could not find an easier way to get it working. Therefor I had to use the comparison operator "match" to find a string with which I could search within the Message property of the Eventlog.
Unfortunately searching takes some time. Via remote it takes up to 5 minutes each computer which is quiet unsatisfying.
Maybe someone has another solution which is faster or knows a way to work parallel, actually I don't really know how to do that, because I'm getting content with
get-content c:\data\input.txt
Thanks in advance

PowerShell Office 365 Script to get user and mailbox information together

I am brand new to PowerShell (started this morning). I have successfully connected to my Office 365 and have been able to get lists of users from Office 365 and mailbox fields from the Exchange portion. What I can't figure out is how to combine them.
What I am looking for is the ability to export certain fields from the mailbox object but only for those mailboxes that belong to a non-blocked, licensed Office 365 users. We have a lot of users whose mailboxes have not been removed but they may no longer be licensed or they may be blocked.
Here are the two exports I have running now. They are complete exports. I tried to filter to the Office 265 users by isLicensed but I never got any results so I just downloaded everything and post processed them with Excel. But I need to run this on a regular basis...
Here's the code:
Get-Mailbox -ResultSize Unlimited | Select-Object DisplayName,Name,PrimarySMTPAddress,CustomAttribute2 | Export-CSV C:\temp\o365\mailboxes.csv
Get-MsolUser -all | Select-Object SignInName, DisplayName, Office, Department, Title, IsLicensed | export-csv c:\temp\o365\Users.csv
Any assistance would be appreciated.
Okay, so as I understand what you're trying to do... You want to get a list of all O365 users for whom the IsLicensed property is $true and the BlockCredential property is $false. Of those users, you then want to pull some data from their mailbox objects; DisplayName, Name, PrimarySMTPAddress, and CustomAttribute2.
There are a couple of ways that we can do this. The first is easier to throw together in the shell but takes longer to actually run. The second requires some set up but completes quickly.
First method
Since we know what our criteria is for what we want from Get-MsolUser, we'll use the pipeline to pull out what we want and toss it straight into Get-Mailbox.
Get-MsolUser -All | Where-Object {$_.IsLicensed -eq $true -and $_.BlockCredential -eq $false} |
Select-Object UserPrincipalName |
ForEach-Object {Get-Mailbox -Identity $_.UserPrincipalName | Select-Object DisplayName,Name,PrimarySMTPAddress,CustomAttribute2}
O365 PowerShell doesn't like giving us ways to filter our initial query, so we handle that in the second step, here...
Where-Object {$_.IsLicensed -eq $true -and $_.BlockCredential -eq $false}
This means, for each item passed in from Get-MsolUser -All, we only want those which have the properties Islicensed set to $true and BlockCredential set to $false.
Now, we only care about the results of Get-MsolUser in so far as determining what mailboxes to look up, so we'll grab a single property from each of the objects matching our prior filter.
Select-Object UserPrincipalName
If you only run everything up to this point, you'd get a list of UPNs in your shell for all of the accounts that we're now going to pipe into Get-Mailbox.
Moving on to our loop in this... If you haven't learned ForEach-Object yet, it's used to run a scriptblock (everything between the {}) against each item in the pipeline, one at a time.
Get-Mailbox -Identity $_.UserPrincipalName
Welcome to the pipeline operator ($_). Our previous Select-Object is feeding a collection of objects through the pipeline and this placeholder variable will hold each one as we work on them. Since these objects all have a UserPrincipalName property, we reference that for the value to pass to the Identity parameter of Get-Mailbox.
Sidebar
Here's a simple example of how this works..
PS> 1,2,3 | ForEach-Object {Write-Host $_}
1
2
3
Each item is passed along the pipeline, where we write them out one at a time. This is very similar to your standard foreach loop. You can learn more about their differences in this Scripting Guy post.
Moving on...
Select-Object DisplayName,Name,PrimarySMTPAddress,CustomAttribute2
We wrap it up with one last Select-Object for the information that you want. You can then look at the results in the shell or pipe into Export-Csv for working with in Excel.
Now... Since the pipeline works sequentially, there's some overhead to it. We're running a command, collecting the results, and then passing those results into the next command one at a time. When we get to our Get-Mailbox, we're actually running Get-Mailbox once for every UPN that we've collected. This takes about 2.5 minutes in my organization and we have less than 500 mailboxes. If you're working with larger numbers, the time to run this can grow very quickly.
Second method
Since a large amount of the processing overhead in the first method is with using the pipeline, we can eliminate most of it by handling our data collection as early and thoroughly as possible.
$Users = Get-MsolUser -All | Where-Object {$_.IsLicensed -eq $true -and $_.BlockCredential -eq $false} | Select-Object -ExpandProperty UserPrincipalName
$Mailboxes = Get-Mailbox | Select-Object UserPrincipalName,DisplayName,Name,PrimarySMTPAddress,CustomAttribute2
$Results = foreach ($User in $Users) {
$Mailboxes | Where-Object UserPrincipalName -eq $User
}
$Results | Export-Csv myFile.csv
The first 2 lines are pretty self-explanatory. We get all the user account info that we care about (just the UPNs) and then we grab all the mailbox properties that we care about.
foreach ($User in $Users)
Each entry in $Users will be stored in $User, where we'll then use it in the scriptblock that follows (in the {}).
$Mailboxes | Where-Object UserPrincipalName -eq $User
Each item in $Mailboxes is piped into Where-Object where we then check if the UserPrincipalName property is equal to the current value of $User. All of the matches are then stored in $Results, which can again be piped to Export-Csv for work in Excel.
While this method is harder to write out in the shell, and requires a little extra initial set up, it runs significantly faster; 22 seconds for my org, versus 2.5 minutes with the first method.
I should also point out that the use of UserPrincipalName with the mailbox dataset is simply to help ensure a solid match between those and the account dataset. If you don't want it in your final results, you can always pipe $Results into another Select-Object and specify only the properties that you care about.
Hope this helps!

Converting a Powershell ADUC query to VBS

So, I have a Powershell script that I use to see if usernames in an array are Smartcard Enabled. A lot of the scripts that are used to automate my company use VBS. Unfortunately my VBS is VERY rusty and I need to convert this powershell into VBS so my lead programmer can use it in a larger script. The script is below. I am leaving out the ADUC Hierarchy for my company's safety. It will be written in the code as "OU=,DC=" Thanks for the assist.
$Array="C:\UserNames.csv"
ForEach($Name in $Array)
{
Get-ADUser -SearchBase "OU=,DC=" -Filter * -Properties * | Where {$_.CN -like "*$Name*"} | Where {$_.SmartcardLogonRequired -eg %False} | Select SamAccountName,GivenName,Surname,SmartcardLogonRequired
}
Turns out he didn't actually want this translated. He needed the UserAccountControl Code for SMARTCARD_REQUIRED (262144). Well, I can scrap the last 3 days of work. Thanks for the comments.