Exchange 2010 TotalItemSize.Value.ToBytes() always empty - powershell

I'm trying to create simple reports on Exchange 2010 mailbox size.
While this works:
Get-MailboxStatistics -server <serverfqdn> |ft displayname, TotalItemSize
this doesn't (second column stays empty):
Get-MailboxStatistics -server <serverfqdn> |ft displayname, {$_.TotalItemSize.Value.ToBytes()}
The problem is that I need the size as an integer, so the first line of code doesn't serve my purpose.
According to several websites the second line of code should work but unfortunately doesn't on my machine.
I know I could parse the value after using the first line but that would be unnecessarily inefficient, wouldn't it? :-)
Hope anyone can help.
Regards,
Kevin

If performing from a imported PS session the methods .ToMB() is lost as the type becomes a custom object.
The $variable.TotalItemSize.Value becomes a two element array [0] in KB,MB or GB and [1] always in bytes.
So to use this we can play with strings to achieve what we want.. in long hand for clarity
$mailboxes = Get-Mailbox -Filter{(RecipientType -eq "UserMailbox") -and (CustomAttribute12 -eq "whatever")}
foreach ($mailbox in $mailboxes)
{
$size1 = Get-MailboxStatistics $mailbox.id
[string]$bob = $size1.TotalItemSize.Value
[int]$bill = $bob.TrimEnd(" bytes)").Split("(")[1] # The bytes part of the array.
$bill = $bill/1mb # Convert into MB's
if ($bill -le 1500) {do something} Else {"Too Big " + $bill} # note -le 1500 NOT 1500MB
}
I hope this helps

this worked for me
$a = get-mailbox -id user | Get-MailboxStatistics
$a.TotalItemSize.Value.ToMB()
$a.TotalItemSize.Value.ToKB()

I have the same issue. I'm not sure if you resolved this.
I have this, which is quite ugly - but works:
$a = get-mailbox USER | get-mailboxstatistics
$intTotalItemSize = [int]$a.TotalItemSize.SubString($a.TotalItemSize.indexof("(")+1, $a.TotalItemSize.indexof(" b")-$a.TotalItemSize.indexof("("))

Try this for your size expression:
#{expression={$_.TotalItemSize.Value.ToMB()};label="Mailbox Size(MB)"}
I believe there is also a ToKB() method.

MVP Shay Levy has delved into this on his blog (http://blogs.microsoft.co.il/blogs/scriptfanatic/archive/2011/08/22/get-full-control-over-your-exchange-remote-powershell-session.aspx).
Basically, you have to modify a setting in the PowerShell virtual directory on the server that you are remoting to.
This is great news for those who are remoting to Exchange servers that they have this kind of control over, but is not helpful for those of us who use hosted Exchange solutions and cannot change these settings. I suppose we will just have to abandon some of the uber-coolness of PowerShell and go back to parsing the string to get the bytes and convert from there.
--EDIT--
This is how I tackled outputting a file of all of my users' mailbox sizes. It could be compressed a bit further, but is a little more readable this way.
$allMailboxes = Get-Mailbox -ResultSize Unlimited
ForEach ( $mailbox in $allMailboxes ) {
$itemSizeString = ( Get-MailboxStatistics $mailbox.Identity ).TotalItemSize.Value.ToString()
$posOpenParen = $itemSizeString.IndexOf("(") + 1
$numCharsInSize = $itemSizeString.IndexOf(" bytes") - $posOpenParen
$mailboxSizeInBytes = $itemSizeString.SubString($posOpenParen,$numCharsInSize).Replace(",","")
Write-Output "$($mailbox.alias),$($mailboxSizeInBytes)"
}

Please, see this article: http://blogs.technet.com/b/gary/archive/2010/02/20/the-get-mailboxstatistics-cmdlet-the-totalitemsize-property-and-that-pesky-little-b.aspx
Get-Mailbox | Get-MailboxStatistics | Add-Member -MemberType ScriptProperty -Name TotalItemSizeinMB -Value {$this.totalitemsize.value.ToMB()} -PassThru | Format-Table DisplayName,TotalItem*

I needed to have this work outside of a remoting session, so I simplified the answer from Greybear to this:
$a = get-mailbox USER | get-mailboxstatistics
$intTotalItemSize = [int64]($a.TotalItemSize -split '[\( ]')[3]
Or in the format of the original question::
Get-MailboxStatistics -Server <serverfqdn> | Select-Object -Property DisplayName,#{label="TotalItemSize";expression={[int64]($_.TotalItemSize -split '[\( ]')[3]}} | ft
Realized that [int] would fail for mailboxes over 4GB, so changed to [int64]. Alternately, display the mailboxes in MB:
Get-MailboxStatistics -Server <serverfqdn> | Select-Object -Property DisplayName,#{label="TotalItemSize";expression={[int64](([int64]($_.TotalItemSize -split '[\( ]')[3])/1048576)}} | ft

The name needs to go before the expression. This will work.
Get-MailboxStatistics -Identity [name] | select #{label=”User”;expression={$_.DisplayName}},lastlogontime,#{label=”Total Size (MB)”;expression={$_.TotalItemSize.Value.ToMB()}}

This works for me
#{Name="TotalSize (MB)"; Expression={((($_.TotalItemSize) -split " ")[0])}}

You might try:
get-mailbox -resultsize unlimited | Get-MailboxStatistics | ft displayname,#{label="Total Size (MB)";expression={$_.TotalItemSize.Value.ToMB()}}

Related

PowerShell referencing object in a pipeline

I'm struggling with passing objects in a pipeline.
I have been going round the problem converting them to strings, but that cannot be the most efficient way of doing things.
$mapi = (Get-CASMailbox -Identity $user | fl mapiEnabled | Out-String ).Split(':')[-1]
if ($mapi -match "True") {
Set-CASMailbox -Identity $User -MAPIEnabled $false
}
I really want to directly access the bool returned instead of converting it to string
Similarly, I have been using below to do a for loop:
$groups = (Get-DistributionGroup | fl name | Out-String -Stream ).Replace("Name : ", "")
foreach ($group in $groups) {
echo $group
}
Both examples are from Exchange Online, below one more universal:
if (((Get-NetIPInterface -InterfaceAlias $adapters -AddressFamily Ipv4 | fl dhcp | Out-String -Stream ).Trim() -ne "").Replace("Dhcp : ","") -match "Disabled") {
echo disabled
}
I just wanted to take a second to see if I can help you understand what is happening in the pipeline and why #mathiasR.Jessen and #AdminOfThings comments will help you.
$mapi = (Get-CASMailbox -Identity $user | fl mapiEnabled | Out-String ).Split(':')[-1]
Breaking down that this line of code does:
Get-CASMailbox is going to return an object with multiple properties. Format-List (fl) is still going to return an object, but now it has been formatted so it's less malleable. Out-String is going to transform that formatted list into a single string. Putting those commands in parentheses runs them and allows you to execute a method on the resulting string object.
Using the same concept, we can use the parenthesis to execute the Get-CASMailbox command and get the singular property you are looking for:
$mapi = (Get-CASMailbox -Identity $user).mapiEnabled
Now we have set $mapi to the value of the mapiEnabled property returned by the command.
Hope this helps!

Combining Powershell cmdlets issue

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

how to get exact user name from get-mobiledevicestatistics in powershell

I have a small script to get all user mobile devices info from exchange 2013 server.
Get-Mailbox -ResultSize Unlimited |
ForEach {Get-MobileDeviceStatistics -Mailbox:$_.Identity} |
Select-Object #{label="User" ; expression={$_.Identity}},DeviceOS, lastsuccesssync
I just want to get an exact user name instead of a path in AD. How can I do it in expression={?}
Here is another script to do it, it gives me the user name, but all devices belongs to user are not in separated lines, they all in one line...
$EASMailboxes = Get-CASMailbox -Filter {HasActiveSyncDevicePartnership -eq $True -and DisplayName -notlike "CAS_{*"} | Get-Mailbox
$EASMailboxes | Select-Object DisplayName, PrimarySMTPAddress, #{Name="Mobile Devices";Expression={(Get-MobileDeviceStatistics -Mailbox $_.Identity).DeviceOS}} |
Out-GridView
I don't have the environment to test this but is this not what you are looking for ?
Get-Mailbox -ResultSize Unlimited | ForEach {
$user = $_.SamAccountName
Get-MobileDeviceStatistics -Mailbox:$_.Identity |
Select-Object #{label="User" ; expression={$user}},DeviceOS, lastsuccesssync
}
That should output the user for every device they own on its own line. You could then easily export this to Export-CSV or some such thing that way.
We save the $user so it is available later in the pipe. Could also have used Add-Member but the result would have been the same.
If you have the identity field, which looks like this
domain.com/Users/OU/UserName/ExchangeActiveSyncDevices/iPhone
Then to split on the / and get the third result, you simply request:
$_.Identity.Split("/")[3]
>UserName
PowerShell begins indexing with number zero, so to request the fourth entry in the list, we request index number 3.
Update
OP mentioned that the OU level might vary, meaning that he couldn't count on a fixed position to request the Index. In order to accomodte that scenario, try this method, which will look for the index of ExchangeActiveSyncDevices and then pick the index position before that.
$_.Identity.Split('/')[($_.Identity.Split('/').Indexof('ExchangeActiveSyncDevices')-1)]
Get-Mailbox -resultsize unlimited|foreach {Get-MobileDeviceStatistics -Mailbox:$_.identity} |Select-Object #{l="user";e={$_.Identity.parent.parent.name}}, lastsuccesssync
on e2k13

powershell how to remove `{}#` from output. Is there a special command to do it?

I entered gwmi win32_product | select -property name | select -first 1 and output to a file. My result was #{name=Google Talk Plugin}.
How can I get rid of #{}, and name. I only want it to show Google Talk Plugin?
#{} means your exporting an object with properties. Try the -ExpandProperty parameter in Select-Object. You could also combine both select-object commands, like:
gwmi win32_product | select -expandproperty name -first 1
I ran into a problem similar with
$drive = Get-WmiObject Win32_LogicalDisk -ComputerName $servername | Select-Object DeviceID
$drive comes up as #{DeviceID=C:}, #{DeviceID=D:}, ...
Here is my brute force hack at it.
The second Trim statement was because for some reason if I put it in the first Trim it starts to Trim the letters in the Drive =D: becomes :
enter code here
$Asdrive = #() #declared before to get rid of null pointer issue, also to tell PS this is an array not a string
#Write-Host "Trimming for Get-WmiObject"
for($i=0;$i -lt $drive.length; $i++) {
[string]$sdrive = $drive[$i]
[string]$sdrive1 = $sdrive.Trim("#","{","}","D","e","v","i","c","e","I","D")
[string]$sdrive2 = $sdrive1.Trim("=")
$Asdrive += $sdrive2
}
If you're running at least Version 3, you can also use the member enumeration feature and then array slicing to take the first one, instead of using select:
(gwmi win32_product).name[0]
I add some code as I found this question with google.
Frode F. solution is the best one.
If you write out something like:
Get-ADComputer -Filter * -SearchBase $OU | Select-Object Name
you get a proper List of all Computers in an OU. You can also pipe that to a CVS/HTML file and its still nice.
| Export-CSV "mylist.csv"
But if you store it into a variable (array) every name will be wrapped in #{}.
In my case I needed computer names in a variable. Here is the solution thanks to Frodo:
$computerList = Get-ADComputer -Filter * -SearchBase $OU | Select-Object -ExpandProperty Name
Hope it helps someone.
(would add it as comment under the right solution, but I don't have enough reputation to do so)

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.