Combining Powershell cmdlets issue - powershell

I've pieced together a script (sorry can't remember the source), that returns multiple attributes using two cmdlets, (Get-user & Get-mailboxstatistics). The code works as expected if I specify an individual user but when using a wildcard to return all users it only returns the attributes from Get-user, and I don't know why.
Any help in resolving this is appreciated.
$outputCollection = #()
$users = Get-User -identity *
$mailboxes = Get-Mailboxstatistics -identity *
$users | Foreach-Object {
#Associate objects
$userObject = $_
$mailboxObject = $mailboxes
$emailObject = $mail
#Make a combined object
$outputObject = "" | Select FirstName,Lastname,sAMAccountName,windowsemailaddress,ItemCount,Totalitemsize,TotalDeletedItemSize,DatabaseName,ServerName,LastLogonTime,LastLogoffTime
$outputObject.FirstName = $userObject.FirstName
$outputObject.Lastname = $userObject.Lastname
$outputObject.sAMAccountName = $userObject.sAMAccountName
$outputObject.windowsemailaddress = $userObject.windowsemailaddress
$outputObject.itemcount = $mailboxObject.itemcount
$outputObject.Totalitemsize = $MailboxObject.Totalitemsize
$outputObject.TotalDeletedItemSize = $MailboxObject.TotalDeletedItemSize
$outputObject.DatabaseNAme = $mailboxObject.DatabaseName
$outputObject.ServerName = $mailboxObject.ServerName
$outputObject.lastlogontime = $mailboxObject.lastlogontime
$outputObject.lastlogofftime = $mailboxObject.lastlogofftime
#Add the object to the collection
$outputCollection += $outputObject
}
$outputCollection

To help you understand I changed as little as possible. For starters I would remove the $mailboxes = Get-Mailboxstatistics -identity * line. Then, for simplicity sake, update the line below
$mailboxObject = Get-Mailboxstatistics -identity $userObject.UserPrincipalName
You need to get the statistics for the one mailbox in each loop pass. Changing how you populate the $mailboxObject should accomplish that without the need to change anything else.
You can possibly remove $emailObject = $mail since you don't appear to be using it anywhere.
FYI This is not tested but should work. This also assumes that the user actually has an exchange mailbox. If not there will be null values in the output.
About efficiency
I wanted to try and make this simpler for you to understand. However note that what Bacon Bits was trying to tell you about not running Get-Mailboxstatistics every time is true. My solution should still work though.

The basic problem is that there's no correlation between $users and $mailboxes.
For example: Why would $mailboxObject = $mailboxes automatically pick the right user's mailbox? The answer is that it wouldn't. It would return everything that was in $mailboxes. So $mailboxObject.FirstName doesn't mean anything, because $mailboxObject is an array, just like $mailboxes was. You could say ``$mailboxObject[0].FirstName`, but that wouldn't correlate correctly.
You need a key field that exists in both objects that you can lookup with a $mailboxObject = $mailboxes | Where-Object { $_.SomeKeyField = $userObject.SomeKeyField } statement.
Alternately, you can wait to run Get-MailboxStatistics until you're inside your loop, but that will significantly increase the amount of traffic to your Exchange system.
Also, bear in mind that all the above assumes that a User exists for every MailboxStatistic. If that's not the case, you'll have to do even more work if you want all of both in the results.

You could try this!
As mentioned by Bacon, either $users or $mailboxes should be filtered with a relationship between the two; Just like a Foreign Key.
**Always use Hash-Table when adding Key-Value pairs to have more options to manipulate and easy understanding
# DECLARE HASHTABLE
$outputCollection = #{}
$users = Get-User -identity *
$mailboxes = Get-Mailboxstatistics -identity $userObject.UserPrincipalName
$users | Foreach-Object {
#Associate objects
$userCollection = $_
$mailboxCollection = $mailboxes | Where-Object { $_.SomeKeyField = $userCollection.SomeKeyField }
# ADD ELEMENTS TO HASH TABLE
# Add elements from userCollection
$outputCollection.add("FirstName",$userCollection.FirstName);
$outputCollection.add("Lastname",$userCollection.Lastname);
$outputCollection.add("sAMAccountName",$userCollection.sAMAccountName);
$outputCollection.add("windowsemailaddress",$userCollection.windowsemailaddress);
# Add elements from mailboxCollection
$outputCollection.add("itemcount",$mailboxCollection.itemcount);
$outputCollection.add("Totalitemsize",$mailboxCollection.Totalitemsize);
$outputCollection.add("TotalDeletedItemSize",$mailboxCollection.TotalDeletedItemSize);
$outputCollection.add("DatabaseNAme",$mailboxCollection.DatabaseNAme);
$outputCollection.add("ServerName",$mailboxCollection.ServerName);
$outputCollection.add("lastlogontime",$mailboxCollection.lastlogontime);
$outputCollection.add("lastlogofftime",$mailboxCollection.lastlogofftime);
}
$outputCollection
By using Hash Table you could also be using different name for the Keys, Example you could do this,
outputCollection.add("Last Logoff Time",$mailboxCollection.lastlogofftime);
#Instead of
outputCollection.add("lastlogofftime",$mailboxCollection.lastlogofftime);
Hope this helps!!

Thanks for all the help. I finally managed to sort this using the following code.
Get-MailboxServer | Get-Mailbox -resultsize 1 | foreach-object {$email = $_.primarysmtpaddress; $_ | Get-MailboxStatistics | Sort #{expression= "totalitemsize";descending=$true}}| select DisplayName, #{expression={$_.totalitemsize.value.ToMB()};label=”Size(MB)”}, #{expression={$_.TotalDeletedItemSize.value.ToMB()};label=”Deleted Size(MB)”}, ServerName, DatabaseName, itemcount, lastlogontime, lastloggedonuseraccount, #{Name="EmailAddress";expression={$email}}| Export-csv c:\Report.csv -notypeinformation

Related

displaying information from two objects in powershell

I'd like to display information from skype and from AD next to eachother. (I'm asking this as a specific question, but I've stumbled against this more often).
the simplified version would be this:
get-csuser | ForEach-Object { $_.displayname,(Get-ADUser -Identity $_.samaccountname -Properties officephone).officephone } | ft
This would show me the displayname from skype and the phone number from AD. But it will show it as new rows, while I would like to have it in one handy table.
What's the best way to achieve this?
Thank you
I think you are looking for "calculated properties" in Select-Object. name is your label and expression is a script-block. Both are wrapped in a hash-table.
#{ Name = ''; Expression = {}}
So, for your code, it can be like this(didn't check for syntax) :
get-csuser | ForEach-Object { $_.displayname, #{ n= 'officePhone'; e = { (Get-ADUser -Identity $_.samaccountname -Properties officephone).officephone } }
Note : we can use short-hands for name and expression.
Link : Further Examples
Maybe this helps you.
$list = foreach ($process in (Get-Process)) {
[PSCustomObject]#{
ProcessID = $process.Id
Name = $process.Name
}
}
$list | Format-Table
Of course, you adjust the loop for the objects you enter the information you want to get, but generally this may help.

Remove #{ColumnName from Powershell results

I'm relatively new to PowerShell scripting and have mainly been cobbling together different scripts and cmdlets from Googling what I'm trying to do. One problem that I'm unable to Google, or search for on StackExchange, because of the special characters is having all my results come out as #{ColumnName=ColumnData}.
Here's an example script I found somewhere for pulling all the members of an AD group.
$Groups = Get-ADGroup -Filter {Name -like "!GroupName"}
$path = $groups
$myCol = #()
ForEach ($Group in $Groups)
{
$Members = #(Get-ADGroupMember "$Group")
ForEach ($Member in $Members){
try{
$user = get-aduser -identity $member -properties displayname
$MyObject = New-Object PSObject -Property #{
Displayname = $user.Displayname
}
$mycol += $MyObject}
catch {}}
}
Write-Host $MyCol | FL
I'm pretty sure there are better ways to get the members of an AD group but that's not the issue at the moment. The problem is all the data comes out like #{Displayname=Lawrence, Kimberly} and this happens with many of the scripts I've thrown together.
Any ideas on how to write scripts properly so I can just get DisplayName = Lawrence, Kimberly?
I agree your code is complex and it does not need to be. A couple of things that are important to mention is why your output looks the way it does.
You are creating an array of PSCustomObjects which function similar to hash-tables. In the end of your processing you have an array of objects with the property DisplayName. Since you are using Write-Host to display the the contents of $myCol it needs to cast it a string array in order to display it. You would see similar output if you just typed [string[]]$myCol. I should hope that you are doing something else in the processing as like the other answers show you have a very complicated way of getting what you are looking for.
Without beating the horse about those other solutions I will suggest some minor changes to yours as it stands. Your title and last sentence contradict what you are looking for since you want to remove the ColumnName in the title and the last sentence you are looking for output like DisplayName = Name.
Changing the last line will address both of these. If you just want the displaynames on there own
$myCols | Select-Object -ExpandProperty DisplayName
Lawrence, Kimberly
and if you actually want the other format you could do this
$myCols | ForEach-Object{
"DisplayName = $($_.DisplayName)"
}
Again, like the other answers, I stress that your code could use simple overhaul. If you needed to understand why it was working the way it way I hope I helped a little.
This actually has nothing to do with Active Directory; You are creating custom objects, then outputting them directly, and by default that is how PowerShell will format the output if you use Write-Host (which is intended to customize output). If you would like to be more specific about what your output should look like, we can help, but here is an example that outputs the results as just a list of strings, just using Write-Output:
$myCol = #()
$MyObject = New-Object PSObject -Property #{ DisplayName = "Lawrence, Kimberly" }
$myCol += $MyObject
$MyObject = New-Object PSObject -Property #{ DisplayName = "Hello, World" }
$myCol += $MyObject
# The default will show at-symbols, etc: Write-Host $MyCol | FL
Write-Output $myCol
The output will be:
DisplayName
-----------
Lawrence, Kimberly
Hello, World
Try this:
Get-ADGroup -Filter "Name -like '!GroupName'" |
Get-ADGroupMember |
Get-ADUser -Properties DisplayName |
Select-Object DisplayName
When I ran your code, I also got the hashes, even without special characters. Your code is way too unnecessarily complex.

How to use the "-notcontains" conditional parameter when querying a BlockedSendersAndDomains list in PowerShell?

I am trying to make a list of all mailboxes who DO NOT have "conostco.com" domain on their BlockedSendersAndDomains list.
by using
$test= Get-MailboxJunkemailconfiguration -Identity * | fl Displayname, BlockedSendersAndDomains
I can get a list of all the names and the Blocked domains/email addresses associated to those names but for my purpose I want to get a list of all of the names of the mailboxes that DO NOT contain the domain "conostco.com". Is there such way to do it?
Based on the sample data you have given I am "assuming" BlockSendersAndDomains is an array.
So maybe you could do something like this then
$test= Get-MailboxJunkemailconfiguration -Identity * | % { $domains = $_.BlockedSendersAndDomains.GetEnumerator() | ? {$_ -notmatch 'conostco.com'}; $name = $_.displayname ; new-object PSObject -Property #{name=$displayname;domains=$domains} }
Again, there is no way for me to test, so see if that gives you a $test object collection which you can then re-use or just display, if you like.
Get-MailboxJunkEmailConfiguration -Identity * |
Where-Object {$_.BlockedSendersAndDomains -notlike '*conostco.com*' }

In PowerShell, how can I combine the results of two commands that have a 1-to-1 relashionship?

This particular example is Get-User and Get-Mailbox (Exchange 2010). Get-User returns some of the columns I need, and Get-Mailbox some others. I am having difficulty figuring out how I can combine the results of the two into a single table with the results from both.
Get-User -Filter "..." | Get-Mailbox -Filter "..."
How do I take the results of a command similar to the above and turn it into results similar to below?
FirstName LastName Alias CustomAttribute1
--------- -------- ------ ----------------
Bob Smith bsmith Example
Johnny NoMail
Adam Blye ablye Has a Mailbox
Note that FirstName and LastName are not returned by Get-Mailbox, and conversely Alias and CustomAttributes are not returned from Get-User. Not every user has a mailbox, so sometimes a portion of the columns would be null. But I'm having a devil of a time figuring out the best way to return a combined table like this.
Get the users, save each user in a variable, get the mailbox for each user and then create a new object with properties from both variables
Get-User -Filter ... | Foreach-Object{
$user = $_
$mbx = Get-Mailbox $user
New-Object -TypeName PSObject -Property #{
FirstName = $user.FirstName
LastName = $user.LastName
Alias = $mbx.Alias
CustomAttribute1 = $mbx.CustomAttribute1
}
}
I make a custom object, which may be overkill, but it's the simplest way I've found.
Here's some sample code to play with. Let me know if it generates any trouble or additional questions:
$outputCollection = #()
$users = Get-User -Filter "..."
$mailboxes = Get-Mailbox -Filter "..."
$users | Foreach-Object {
#Associate objects
$userObject = $_
$mailboxObject = $mailboxes | Where-Object {$_.Name -eq $userObject.Name}
#Make a combined object
$outputObject = "" | Select Name, UserAttribute, MailboxAttribute
$outputObject.Name = $userObject.Name
$outputObject.UserAttribute = $userObject.UserAttribute
$outputObject.MailboxAttribute = $mailboxObject.MailboxAttribute
#Add the object to the collection
$outputCollection += $outputObject
}
$outputCollection
Another option that should work is called calculated properties:
Get-User -Filter "..." | Select Name, UserAttribute, #{Name="OtherAttribute"; Expression={(Get-Mailbox $_.Name).MailboxAttribute}}
...note that this will run a new Get-Mailbox command for each entry, potentially increasing execution time
Thank you guys. I spent a hell of a lot of time trying to figure out my own issue and your code helped me get it right. I needed to find all the calendars were the default account was set to none. Below is what I needed up using.
Get-Mailbox | ForEach-Object{
$user = $_
$calPerms = Get-MailboxFolderPermission $user":\calendar" -User Default | Where-Object {$_.AccessRights -eq "none"}
New-Object -TypeName PSObject -Property #{
Name = $user
Permissions = $calPerms.AccessRights
Users = $calPerms.user
}
}
One liner to get FirstName, LastName, Alias and CustomAttribute1:
Get-User | Select Firstname, Lastname, #{Name="Alias"; Expression={(Get-Mailbox $_.Name).Alias}}, #{Name="CustomAttribute1"; Expression={(Get-Mailbox $_.Name).CustomAttribute1}}

Powershell pipeline - Retrieve outputs from first cmdlet?

I am trying a few things in Powershell and what I don't manage to achieve is the following (in Exchange):
Get-User | Get-MailboxStatistics
But in the output I would like some fields/outputs from the "Get-User" cmdlet and some fields/outputs from the "Get-MailboxStatistics" cmdlet.
If anyone has an answer, I have searched the web but with no success as I've had difficulties explaining it in a few words.
Thanks in advance for your help.
Start with the execution of one cmdlet, pipe the results to Foreach-Object and then save a reference to the current object ($user), now execute the second command and save it in a variable as well. Create new object with properties from both objects.
You also need to filter users that have mailboxes, use the RecipientTypeDetails parameter.
$users = Get-User -RecipientTypeDetails UserMailox
$users | Foreach-Object{
$user = $_
$stats = Get-MailboxStatistics $user
New-Object -TypeName PSObject -Property #{
FirstName = $user.FirstName
LastName = $user.LastName
MailboxSize = $stats.TotalItemSize
ItemCount = $stats.ItemCount
}
}
I don't know if it is the best or optimal solution, but you certainly do it by saving actual user to variable in foreach:
$users = Get-User
$users | % { $user = $_; Get-MailboxStatistics $_ | %
{
"User name:{0} - some mailbox statistics: {1}" -f $user.SomePropertyOfUser, $_.SomePropertyOfMailbox
}
}
The first step (saving users into separate variable) is required only when working with Exchange cmdlets - as mentioned here, you cannot nest Exchange cmdlets in foreach...
This error is caused when executing the Exchange cmdlets through PowerShell remoting, which do not support more than one pipeline running at the same time. You may see this error when you pipe the output from a cmdlet to foreach-object, which then runs another cmdlet within its scriptblock.
$users = Get-User -RecipientTypeDetails UserMailbox
$users | Foreach-Object{ $user = $_; $stats = Get-MailboxStatistics $user.DistinguishedName; New-Object -TypeName PSObject -Property #{FirstName = $user.FirstName; LastName = $user.LastName;MailboxSize = $stats.TotalItemSize;ItemCount = $stats.ItemCount }}
I've had to add a specific field in input of Get-MailboxStatistics because remotely, I was having:
The following Error happen when opening the remote Runspace: System.Management.Automation.RemoteException: Cannot process argument transformation on parameter 'Identity'. Cannot convert the "gsx-ms.com/Users/userName1" value of type "Deserialized.Microsoft.Exchange.Data.Directory.Management.User" to type "Microsoft.Exchange.Configuration.Tasks.GeneralMailboxOrMailUserIdParameter".
Anyway, thank you both #Jumbo and #Shay-levy
Get-ADUser -identity ADACCOUNT | Select-object #{Name="Identity";Expression={$_.SamAccountName}} | Get-MailboxStatistics
For some reason the Identity parameter doesn't take pipelne input by value, only by property name. So in order to get it to work you can change the name of piped in data to match the parameter name of Identity. Then Get-MailboxStatistics finally knows how to treat the data your feeding it via the pipeline.