BitTitan PowerShell Tips: How to connect the BitTitan SDK to the German instance

As many of you might know, the BitTitan SaaS has multiple instances across the globe, such as:

If you’re using the BitTitan SDK, to automate most, if not all of your project tasks, keep in mind the following:

  • By default, the BitTitan SDK connects to the global instance
  • There is no specific version of the SDK to connect to a different instance
  • There are 2 cmdlets that allow you to connect to the German instance:
    • Set-BT_Environment -Environment Germany
    • Set-MW_Environment -Environment Germany
  • You need to run the cmdlets above before you create the BitTitan PowerShell ticket

The BitTitan SDK interacts with both our MSPC platform – for tasks such as managing customers, endpoints, DeploymentPro, etc – and with our MigrationWiz platform, to manage your migration projects among many other things. Depending on the platform you want to interact with, you’ll need to run one of the commands above, or both. The “BT” commands applies to MSPC and the “MW” command applies to MigrationWiz.

See below the sample code screenshot.

BTSDK1

On a side note, if you’re wondering what a BitTitan PowerShell ticket is (mentioned multiple times in this blog post), all of the BitTitan cmdlets are backed by a ticket, that determines which environment we’re connecting into (MSPC or MigrationWiz), which workgroup/account and has the credentials to access it.

For much more information on how to code with the BitTitan SDK, go here or ping me an email via the blog contact form.

Advertisements

Exchange Public Folders: Export item count, per item type, of your public folder structure

Just recently, I was asked to help investigate which Exchange cmdlets would help a partner the I work with, do an item count in an on premises Exchange Public folder structure. Their specific ask was to get, per folder, the number of contact items.

So starting with the best command to do this, it’s easy to get to the conclusion that it will be the Get-PublicFolderItemStatistics, and the first thing that you need to know about that cmdlet is that it’s only available in Exchange 2010+.

The second thing you need to focus on is, in which folders do you want to run the count on? All of them? And if not all, do you want to run the count based on folder type? i.e do you want to just count calendar items on folders of type calendar? How can we achieve this?

Lets break this down:

  • To be able to select the folders you want to count the items for, you need of course to start with the Get-PublicFolder cmdlet
  • If you want to filter just one or multiple folder type (i.e Calendar, Contacts, etc) you need to do it using the “FolderClass” attribute.

Note: The “FolderClass” attribute doesn’t exist in all versions of Exchange. I haven’t checked in detail but at least apparently in Exchange 2010 you won’t be able to leverage this attribute to filter just the folders you want. Worst case scenario you can always run a count against all folders. Also note that as you can see below, not all folders have a “FolderClass”.

PFCount1

And finally the code to grab all the folders you want.

With the FolderClass attribute filtering:

#Get all folders
$folders = get-publicfolder \ -recurse -resultsize unlimited | ? {$_.FolderClass -like “IPF.Contact”}
And without:
#Get all folders
$folders = get-publicfolder \ -recurse -resultsize unlimited

 

Note: The Where-Object filtering (? sign in the command above) in PowerShell caches all its results into memory, so if you have a very large public folder structure you might want to have that in mind and run the commands in a machine with enough resources.

Now that we know how to grab all the folders we need, lets look at how to do the folder count:

  • The command used to do the folder count is, as mentioned above in this post, the Get-PublicFolderItemStatistics
  • Because all you want to do is count items of a certain type, you will leverage the “ItemType” attribute in your filtering
  • Don’t forget that the Get-PublicFolderItemStatistics is an Exchange 2010+ cmdlet

Below see the output of an item count of a specific folder.

PFCount2

Now, finally, the entire script (in bold the item count):

PFCount4

(and the copy/paste version)
#Get all folders
$folders = get-publicfolder \ -recurse -resultsize unlimited | ? {$_.FolderClass -like “IPF.Contact”}
#Process All folders
Foreach ($folder in $folders){
$ContactCount = 0
$Contacts = get-publicfolderitemstatistics $Folder.Identity|? {$_.ItemType -like “IPM.Contact”}
If($Contacts -eq $null){
Write-Host”The folder ‘$($Folder.Identity)’ has 0 Contacts”
}
Else{
foreach($Contact in $Contacts){
$ContactCount++
}
Write-Host”The folder ‘$($Folder.Identity)’ has $($ContactCount) Contacts”
}
}
Lets break down the script above:
  • we start by getting all folders of class contact. Again you can do this filtering or not, depending on the Exchange version and what you need exactly.
  • we then enter a loop where, for each folder, we will grab all items of type contact and count them
  • once that is done we write the output into the console

This script is very simple and doesn’t have error handling, logging and output to CSV. If you want those features feel free to contact me via the blog and I can build you a very complete version of the script.

Running the simple version of the script in a large environment can make the results difficult or impossible to analyse, however, with the code above gives you an insight in how to filter and count Public folders, by type and class.

As always I hope this is helpful.

Office 365: Run a script connected to 2 Exchange online sessions

Have you ever wondered how you can connect to 2 Exchange Online sessions, in the same PowerShell window?

For example, if you want to run a script that connects to 2 tenants, exports all mailbox permissions from one tenant and imports them into the other. Same thing applies to Distribution groups and memberships.

With the Microsoft Tenant 2 Tenant Migrations in high demand, and because there are so much that you might want to bring from one Exchange Online to the other, I thought I should write a quick blog article on how to connect and manage 2 Exchange Online tenants in one PowerShell window, ideal for scripting.

Before you look at the code below, let me outline two key parameters, of the Import-PSSession cmdlet to achieve your goal:

  • Prefix – Specifies a prefix to the nouns in the names of imported commands.
    Use this parameter to avoid name conflicts that might occur when different commands in the session have the same name.
    For instance, if you specify the prefix Remote and then import a Get-Date cmdlet, the cmdlet is known in the session as Get-RemoteDate, and it is not confused with the original Get-Date cmdlet.
  • AllowClobber – Indicates that this cmdlet imports the specified commands, even if they have the same names as commands in the current session.
    If you import a command with the same name as a command in the current session, the imported command hides or replaces the original commands. For more information, see about_Command_Precedence.
    By default, Import-PSSession does not import commands that have the same name as commands in the current session.

Note: Both the definitions above were taken from the Import-PSSession cmdlet official Microsoft article, that you can see by clicking here.

So how does this work actually? Have a look at the code below:

<#
.NOTES
 Author: antonio.vargas@myexchangeltd.co,uk

Date: October 4th 2017
 Version: 1

.SYNOPSIS
 This lines of code will connect 2 PowerShell Exchange Online sessions to 2 different tenants. 
.DESCRIPTION
 By opening 2 PowerShell sessions, using the PREFIX parameter for each one of those sessions it will allow you to manage both tenants at the same time (ideal for tasks where you want to migrate configurations from one tenant to the other)
#>

### Input source and destination credentials

$SourceCred = Get-credential -message "Please Enter your SOURCE tenant credentials"

$DestCred = Get-credential -message "Please Enter your DESTINATION tenant credentials"

### Create Source EXO Session

$SourceSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -AllowRedirection -Authentication Basic -Credential $SourceCred

$result = Import-PSSession $SourceSession -prefix SRC -AllowClobber

### Create Destination EXO Session

$DestSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -AllowRedirection -Authentication Basic -Credential $DestCred

$result = Import-PSSession $DestSession -prefix DST -AllowClobber

### Run a get-mailbox to validate connection to both tenants

Write-Host "Listing mailboxes in the source tenant" -ForegroundColor Green

Start-Sleep -s 5

Get-SRCMailbox -resultsize unlimited |ft alias, *smtp*

Write-Host "Listing mailboxes in the destination tenant" -ForegroundColor Green

Start-Sleep -s 5

Get-DSTMailbox -resultsize unlimited |ft alias, *smtp*

### Showing a count of mailboxes in source and destination

Write-Host "Counting mailboxes in the source tenant" -ForegroundColor Green

Start-Sleep -s 5

(Get-SRCMailbox -resultsize unlimited).count

Write-Host "Counting mailboxes in the destination tenant" -ForegroundColor Green

Start-Sleep -s 5

(Get-DSTMailbox -resultsize unlimited).count

### LISTING PS SESSIONS

Write-Host "Your list of active PS Sessions" -ForegroundColor Green

Get-PSSession |fl

Some considerations of the code above:

  • There’s no logging or error handling in the code. The purpose of the code above is to provide you the insight on how to connect to 2 sessions with the same cmdlets.
  • The code is provided as is and you should test it before you run it in production.
  • The code includes blocks to perform the following:
    • Request input for source and destination credentials
    • Create the source Exchange Online session
    • Create the destination Exchange Online session
    • Code to demonstrate how to run cmdlets in the source and destination tenant (example with get-mailbox)
    • Code to list both PS Sessions created

Now lets see the output of the code:

2sessions

Simple, right? Again this can be very useful for tenant to tenant migrations.

Azure: “CurrentStorageAccountName is not accessible” error when creating a VM via the SDK

I just recently faced an error when doing an Azure lab, that I thought I should blog about, since the resolution is very simple.

Here’s what happened, I was creating a VM via the SDK using the Service Module (Classic – see note below) and the following cmdlet:

New-AzureQuickVM –Windows –ServiceName “AV-AutoSVC” –name “AV-AutoVM” –ImageName $image –Password $password -Location “East US” -InstanceSize “Basic_A0” -AdminUsername avargasadmin

Note: You can create virtual machines both with the service model (classic deployment) or with the Resource manager (new portal). Both methods are available via SDK but the way to connect is different. You use Add-AzureAccount to login to the service model and Add-AzureRMAccount to login to the resource manager. See differences here.

And I got the following error:

Azure01

New-AzureQuickVM: CurrentStorageAccountName is not accessible. Ensure the current storage account is accessible and in the same location or affinity group as the cloud service.

Now after digging a little bit more in my Azure tenant and what might be the cause of the problem I confirmed that I did had the storage account, and even with the -Location parameter in the cmdlet above forcing it to be “East US” (where my storage account is) I was still getting the same error.

Then I decided to run a the following cmdlet:

Get-Azuresubscription

azure02

I then realized that I have no valid storage account associated with my subscription, and the solution to my problem was to run:

Set-AzureSubscription –SubscriptionName <YourSubscriptionName> –CurrentStorageAccount <YourStorageAccountName>

If you don’t know your storage account name run:

Get-Azurestorageaccount |fl storageaccountname, location

Once you do this, re run your New-AzureQuickVM cmdlet (note: this error should happen also when you’re running the New-AzureVM cmdlet) and the error should be gone.

 

Understand and script the Get-MailboxFolderStatistics cmdlet

This blog post is going to give you some insight on how you can script the Get-MailboxFolderStatistics cmdlet, but also how to understand it’s output, as well as how is it important to plan a mailbox migration project.

Why are the mailbox folder statistics important to plan a migration project?

That is indeed the first thing you need to consider: Why do I need those statistics? To better answer that let me start by showing you an output of the command.

1

What you see above is the output of a mailbox, where I filtered the Folder Path, the number of items in the folder and sub folders, as well as the size of each folder. So let me bullet point why is this important for a migration project planning:

  • It will give you item counts per user and per item type (Mail, calendar, contacts). Those numbers are important, specially if extremely large, to estimate migration timelines when using the Bittitan MigrationWiz mailbox migration.
  • It will show you the size of every folder as well as the entire mailbox size. This is important to identify which folders are larger on a very big mailbox. I’ve seen examples of some of those folder being considered irrelevant for a migration (i.e deleted items, sent items) and with a flexible tool like the Bittitan MigrationWiz you can filter them out.
  • It will show you not only the folders visible to the end user, but also the recoverable deleted items folder. This might be very important in scenarios where you have in place hold active. It will give you the exact estimate of how many items are under in place hold on the recoverable items, that you will eventually need to move to the same folder on the destination mailbox.

So in summary, and giving a quick example, if you’re moving 1000 mailbox from Exchange Online tenantA to tenantB, you should use the Bittitan MigrationWiz tool, that will be not only fast but also flexible on what you can include and exclude in the migration, leveraging the Exchange Web Services API. To do that the insight of what’s in the mailbox is fundamental to plan the entire migration. You can plan the timelines and how long it will take, what type of licensing you will need at the destination tenant and many other important variables for the project.

How can I filter the output per item type?

When running the cmdlet you need to use the “-FolderScope” parameter to filter the output per item type. That parameter allows you to enter many different folder scope types, the most common being:

  • All
  • Calendar
  • Contacts
  • RSSSubscriptions
  • RecoverableItems

See the entire list in the official Technet article of the Get-MailboxFolderStatistics cmdlet.

Some cmdlet examples

See below some examples on how to obtain specific data:

Get total number of items in the mailbox (does not include recoverable deleted items):

Get-MailboxFolderStatistics user@domain.com | Where-Object {$_.foldertype -eq ‘root’} |ft folderpath, itemsinfolderandsubfolders, folderandsubfoldersize

2

Get total number of contacts in the mailbox:

Get-MailboxFolderStatistics user@domain.com -FolderScope Contacts |ft folderpath, items*, folderandsubfoldersize

3

Note: the above will work for calendar items if you change the folder scope from “Contacts” to “Calendar”

Get total number of items in the Recoverable deleted items folders:

Get-MailboxFolderStatistics user@domain.com| where {$_.FolderType -eq ‘RecoverableItemsRoot’}|ft folderpath, itemsinfolderandsubfolders, folderandsubfoldersize

4

And finally… the script that exports all of this to a csv:

Now that we discussed how important it is to get the statistics, and I gave you same examples on how to run some one line commands to get some insight on the mailbox, I will share with you a script that was done by me and my colleague and friend Alberto Nunes.

Below are the details of what the script does:

  • The script will run the statistics against a list of users that needs to be specified on a file called users.csv (see csv format below). The file needs to be on the same folder as the script.
  • The script runs Exchange PowerShell commands, so make sure you’re connected to the Exchange PowerShell (online or on premises)
  • The script will export the results to a file statistics.csv
  • The script will ask you for a source or destination parameter. The statistics for the recoverable deleted items folders will only be included if you select Destination.
  • This script is provided as is and there’s no guarantee that it will work in your environment.

The Users.csv file:

5

Note: Make sure you name the column A “EmailAddress”

The Script:

Copy the text below (in red) to a notepad. Save it as .ps1 and run it from a PowerShell session connected to Exchange.

#Start Script
Param
(
[Parameter(Position=0,Mandatory = $true)][ValidateSet(‘Source’,’Destination’,’MigrationWiz’)][String]$Location,
[Parameter(Mandatory = $false)][String]$Folder = “.”,
[Parameter(Mandatory = $false)][String]$UsersCSV = “Users.csv”,
[Parameter(Mandatory = $false)][String]$OutputCSV = “Statistics.csv”
)
Get-Date
$ErrorActionPreference = “SilentlyContinue”
$SourceFile = $Folder + “\” + $UsersCSV
$OutputFile = $Folder + “\” + $OutputCSV
$mailboxes = Import-Csv -Path “$SourceFile”
$CSV = @()
Foreach ($mailbox in $mailboxes)
{
Write-Host “Working on $($mailbox.EmailAddress)”

$AllStats = Get-MailboxFolderStatistics $($mailbox.EmailAddress)| Where-Object {$_.foldertype -eq ‘root’}

$ContactStats = Get-MailboxFolderStatistics $($mailbox.EmailAddress) -FolderScope Contacts
$TotalContactsItems = ($ContactStats | select -expand ItemsInFolder |Measure-Object -Sum).Sum

$CalendarStats = Get-MailboxFolderStatistics $($mailbox.EmailAddress) -FolderScope Calendar
$TotalCalendarItems = ($CalendarStats | select -expand ItemsInFolder |Measure-Object -Sum).Sum

$RSSFeeds = Get-MailboxFolderStatistics $($mailbox.EmailAddress) -FolderScope RssSubscriptions -ErrorAction SilentlyContinue
If ($RSSFeeds)
{
$TotalRSSFeeds = ($RSSFeeds | select -expand ItemsInFolder | Measure-Object -Sum).Sum
}
Else
{
$TotalRSSFeeds = 0
}

# In the source, we don’t need to know the Size of the RecovableItems
If ($Location -eq “Destination”)
{
$recoverableItems = Get-MailboxFolderStatistics $($mailbox.EmailAddress)| where {$_.FolderType -eq ‘RecoverableItemsRoot’}
[double]$recoverableSize = $recoverableItems.FolderAndSubfolderSize.TrimEnd(” bytes)”).Split(“(“)[1].replace(‘,’,”) |%{“{0:N2}” -f ($_ /1mb)}
}

$TotalMailItems = $AllStats.ItemsInFolderAndSubfolders – $TotalContactsItems – $TotalCalendarItems – $TotalRSSFeeds

$ISSize = $AllStats.FolderAndSubfolderSize.tostring()
[double]$ISSize =$ISSize.TrimEnd(” bytes)”).Split(“(“)[1].replace(‘,’,”) |%{“{0:N2}” -f ($_ /1mb)}

$Output = New-Object PSObject
$Output | Add-Member -MemberType NoteProperty -Name “User” -Value $mailbox.EmailAddress
$Output | Add-Member -MemberType NoteProperty -Name “Location” -Value $Location
$Output | Add-Member -MemberType NoteProperty -Name “TotalItems” -Value $AllStats.ItemsInFolderAndSubfolders
$Output | Add-Member -MemberType NoteProperty -Name “MailboxSize MB” -Value $ISSize
$Output | Add-Member -MemberType NoteProperty -Name “TotalContactItems” -Value $TotalContactsItems
$Output | Add-Member -MemberType NoteProperty -Name “TotalCalendarItems” -Value $TotalCalendarItems
$Output | Add-Member -MemberType NoteProperty -Name “TotalMailItems” -Value $TotalMailItems
$Output | Add-Member -MemberType NoteProperty -Name “TotalRSSFeeds” -Value $TotalRSSFeeds
If ($Location -eq “Destination”)
{
$Output | Add-Member -MemberType NoteProperty -Name “RecovableSize in MB” -Value $recoverableSize
$Output | Add-Member -MemberType NoteProperty -Name “RecovableItems” -Value $recoverableItems.ItemsInFolderAndSubfolders
$Output | Add-Member -MemberType NoteProperty -Name “Total Mailbox Size including Recovable” -Value $($ISSize + $recoverableSize)
$Output | Add-Member -MemberType NoteProperty -Name “Total Mailbox Count including Recovable” -Value $($AllStats.ItemsInFolderAndSubfolders + $recoverableItems.ItemsInFolderAndSubfolders)
}
$CSV += $Output
Write-Host “Completed $($mailbox.EmailAddress)” -ForegroundColor Green
}

$CSV | export-CSV “$OutputCSV” -NoTypeInformation
#End Script

To run the script you need to specify the location parameter:

.\MailboxFolderStats.ps1 -Location Source

.\MailboxFolderStats.ps1 -Location Destination (includes recoverable deleted items statistics)

As always I hope the above is helpful. Thanks for reading.

Office 365 tip: Enable Directory Sync via PowerShell

There’s no easy way to say this. The new Office 365 admin Portal comes with a new way to enable Directory Sync, a wizard that tries to guide you through with a series of questions and suggestions. The end result is bad, to say the least.

 

ADSyncWiz1

The wizard is confusing and the end result is sometimes not the expected. It happened to me several times finishing the wizard just to find out that Directory Sync was still disabled.

In my opinion things like finishing the wizard if you select “1-10” in the number of users of your organization, assuming of course that you don’t want Directory Sync, are not welcome. I understand the “dummy proof” idea behind it, but let’s face it not everything needs to be designed in such way.

ADSyncWiz2

As you can see above once you run the wizard you’ll get a summary report of the results, and links to download  AD Connect and the IdFix tool.

But the purpose of this blog post is to explain you how to get away from the wizard and on a simple command activate Directory Synchronization. Here it goes:

  1. Download and install the Windows Azure Active Directory Module for Windows PowerShell
  2. Connect to Office 365 and run the cmdlets below

ADSyncWiz3

Enable Directory Sync:

Set-MsolDirSyncEnabled -EnableDirSync $true

Verify Directory Sync state:

(Get-MsolCompanyInformation).DirectorySynchronizationEnabled

I hope the above is helpful. As always, any questions please let me know.