Using Powershell with large O365 Tenants - powershell

I have been tasked with creating a powershell script that will crawl through one of our clients Office365 enviroments daily and disable POP/IMAP Access to anyone not in a specific security group.
I have written the code and it works, up until it gets about 75% of the way through our ~5000+ mailboxes, then it starts to fail with "Starting a command on the remote server failed with the following error message : The I/O operation has been aborted because of either a thread exit or an application request. For more"
I imagine it is somehow timing out due to there being so many mailboxes, but not sure how to more efficiently write my script? Any ideas?
Connect-EXOPSSession
Connect-MSOLService
$PopGroup = Get-MSOLGroup -All | Where-Object {$_.DisplayName -eq "POP Exception"}
$ImapGroup = Get-MSOLGroup -All | Where-Object {$_.DisplayName -eq "IMAP Exception"}
$EnablePOP = Get-MSOLGroupMember -GroupObjectId $PopGroup.ObjectId -All | Select-Object -ExpandProperty DisplayName
$EnableImap = Get-MSOLGroupMember -GroupObjectId $ImapGroup.ObjectId -All | Select-Object -ExpandProperty DisplayName
$Mailboxes = $Mailboxes = Get-Mailbox -ResultSize Unlimited
ForEach ($Mailbox in $Mailboxes) {
If ($EnablePop -Contains $Mailbox) {
$Mailbox | Set-CASMailbox -PopEnabled $True }
Else {
$Mailbox | Set-CASMailbox -PopEnabled $False }
If ($EnableImap -Contains $Mailbox) {
$Mailbox | Set-CASMailbox -ImapEnabled $True }
Else {
$Mailbox | Set-CASMailbox -ImapEnabled $False }}

That error message appears to be a performance limitation or I/O timeout (see this blog entry from MS for example of this issue). I have a couple of ideas to try:
Your Get-MSOLGroup -ALL commands are pulling the entire list of AD groups and then filtering, it would be better to specify the UPN or SearchString to filter your request. See this link for examples and common performance tips for PS. Try $PopGroup = Get-MSOLGroup -SearchString "POP Exception"
Try using Get-CASMailbox instead of getting all mailboxes with all of their data:
Get-CASMailbox -ResultSize Unlimited | foreach-object {
$upn = "$_#YOURDOMAIN.COM"
if($_.POPEnabled -eq $true -and $(!($EnablePop.EmailAddress -Contains $upn))){
Set-CASMailbox -Identity $upn -PopEnabled $false
} elseif ($_.POPEnabled -eq $false -and $EnablePop -Contains $upn){
Set-CASMailbox -Identity $upn -PopEnabled $true
}
if($_.ImapEnabled -eq $true -and $(!($EnableImap -Contains $upn))){
Set-CASMailbox -Identity $upn -ImapEnabled $false
} elseif($_.ImapEnabled -eq $false -and $EnablePop -Contains $upn){
Set-CASMailbox -Identity $upn -ImapEnabled $true
}
}

Related

Get-ADUser is not returning anything when I use the UPN returned from Get-MailboxPermissions as a variable

I am trying to get the SamAccountName from Get-ADUser, but when I pass in the variable $member.User I get no results. $member.User when I print it out with Write-Host returns a variable, but used in the code below I get nothing. Also, if I copy/paste the $member.User value into that $add = Get-ADUser I get the SamAccountName. Why isn't Get-ADUser -Filter {EmailAddress -eq "$member.User" } returning anything? It is driving me nuts. Thank you in advance.
$Send = #()
$SendAs = #()
# Displaying FullAccess permissions for shared mailboxes
$Send = Get-MailboxPermission -Identity $MailboxUPN | Where-Object { -not ($_.User -like “NT AUTHORITY\SELF”) } | Select-Object Identity,User,AccessRights
$Send
# Displaying SendAs permissions for shared mailboxes
# $SendAs = Get-RecipientPermission -Identity $MailboxUPN | Where-Object {($_.IsInherited -eq $False) -and -not ($_.Trustee -like “NT AUTHORITY\SELF”) } | Select-Object Trustee, AccessRights
# $SendAs
forEach ($member in $Send){
Write-Host "In here"
Write-Host $member
$add = Get-ADUser -Filter {EmailAddress -eq "$member.User" }| Select-Object -ExpandProperty SamAccountName
Write-Host $add.SamAccountName
}

Exchange 2010 Powershell - Return Users With SendAs Permissions But Filter Disabled Users

so I have a script that works fine that I found online and changed to suit my needs. I have pasted this script below. However, in the output there is a lot of disabled users that have permissions to the mailboxes. E.g. I'd get an Output like "Mailbox Name^Mailbox#email.com^ActiveUser ActiveUser DisabledUser" So I am wondering if there is a way to make the script skip disabled users, same way how it leaves out self permissions.
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
. $env:ExchangeInstallPath\bin\RemoteExchange.ps1
Connect-ExchangeServer -auto
$OutFile = “C:\Send_As_Permissions.txt”
“DisplayName” + “^” + “Email Address” + “^” + “Send As” | Out-File $OutFile -Force
$Mailboxes = Get-Mailbox -resultsize unlimited | Select Identity, Alias, DisplayName, DistinguishedName, WindowsEmailAddress
ForEach ($Mailbox in $Mailboxes) {
$SendAs = Get-ADPermission $Mailbox.identity | where {($_.ExtendedRights -like “*Send-As*”) -and -not ($_.User -like “NT AUTHORITY\SELF”) -and -not ($_.User -like “s-1-5-21*”)} | % {$_.User}
$Mailbox.DisplayName + “^” + $Mailbox.WindowsEmailAddress + “^” + $SendAs | Out-File $OutFile -Append
}
If you want to report on enabled mailboxes only, filter the output from Get-Mailbox on the IsMailboxEnabled property:
$Mailboxes = Get-Mailbox -resultsize unlimited | Where-Object { $_.IsMailboxEnabled } | Select ...
If you want to report on individual rights assignments for enabled accounts only, you'll have to query AD based on the value of the User property you extract:
$SendAs = Get-ADPermission $Mailbox.identity | where {($_.ExtendedRights -like “*Send-As*”) -and -not ($_.User -like “NT AUTHORITY\SELF”) -and -not ($_.User -like “s-1-5-21*”)} | % {$_.User}
$domain,$username = $SendAs.Split('\')
$ADUser = Get-ADUser -Identity $username -Server $domain
if($ADUser.Enabled){
# output to report
}

Assign 0365 licensing to a list of users

Trying to get a script to assign O365 license to a list of users. What am I missing here? I get the list of users to work, made based on SamAccountName length and the date the user is created. How can I get it to look at each of the users inside that list and assign it an O365 license if it is false? Is an IF statement ideal to use here?
Import-Module ActiveDirectory
$date = (Get-Date).ToString()
$month = (Get-Date).AddDays(-4)
$NewUser = Get-ADuser -Filter * -Properties * | Where { ($_.samaccountname.length -eq 3 -and $_.whencreated -ge $month) } | Select-Object SamAccountName, UserPrincipalName, whencreated
Import-Module MSOnline
Connect-Module -Cred $User
foreach ($item in $NewUser) {Get-MsolUser -UserprincipalName $NewUser | where {$_.isLicensed -eq $false}
Set-MsolUserLicense -UserPrincipalName $NewUser.UserPrincipalName -AddLicenses $NewUser.licensetype
}
Use if statement is right. Please refer to the following:
foreach($user in $NewUser){
$AccountInfo = Get-MsolUser -UserPrincipalName $user.UserPrincipalName -ErrorAction Stop
$CurrentAccountSku = $AccountInfo.Licenses.AccountSkuId
if($CurrentAccountSku -ne $null)
{
Set-MsolUserLicense -UserPrincipalName $user.UserPrincipalName -AddLicenses 'contoso:ENTERPRISEPACK' -ErrorAction Stop
}
}
Here's a blog for adding different Skus to Office 365 user accounts.
http://blogs.technet.com/b/treycarlee/archive/2013/11/01/list-of-powershell-licensing-sku-s-for-office-365.aspx

bulk creation of forwarding addresses and contacts via powershell

I am trying to move mailboxes to a new Exchange server on a different O365 tenant. This script is a portion of this move which attempts to list all Exchange Online users who have licenses, create a contact for each user pointing to their new email address, and forwards their email to their new address as well as their mailbox
#requires -Version 3 -Modules MSOnline
$mailboxlist = Get-Mailbox | Select-Object -Property userprincipalname |
Get-MsolUser | Where-Object -Property islicensed -EQ -Value $true |
Select-Object -Property firstname, lastname, userprincipalname |
Where-Object -Property firstname -NE -Value $null |
Format-list -AutoSize
#Begin foreach loop
foreach ($item in $mailboxlist)
{
#Create the forwarding address
$forwardingaddress = $item.firstname + '.' + $item.lastname + '#newdomain.com'
#Check for a contact, add if not present
If (!(Get-MailContact $forwardingaddress -ErrorAction silentlycontinue)){
New-MailContact $forwardingaddress -ExternalEmailAddress $forwardingaddress
}
#assign forwarding address
set-mailbox -ForwardingAddress $forwardingaddress -DeliverToMailboxAndForward $true
}
The result of the above is a $mailboxlist which is populated with first and last names and the users UPN which is also an email address. The result of $forwardingaddress is .#newdomain.com.
I don't think I am creating the initial data correctly for the foreach loop. I know that if I replace the $item.firstname with $_.firstname I get the same empty result. I would much appreciate if someone knowledgeable on the topic could assist.
Thanks!
Edit: Final config with Kiran's suggestion
#requires -Version 3 -Modules MSOnline
$mailboxlist = Get-Mailbox | Get-MsolUser | Where-Object { ($_.islicensed -eq $true) -and ($_.firstname -ne $null) }
#Begin foreach loop
foreach ($item in $mailboxlist)
{
#Create the forwarding address
$forwardingaddress = $item.firstname + '.' + $item.lastname + '#newdomain.com'
#Remove any spaces from the email address
$forwardingaddress = ($forwardingaddress -replace " ","").trim()
#Check for a contact, add if not present
If (!(Get-MailContact $forwardingaddress -ErrorAction silentlycontinue))
{
New-MailContact $forwardingaddress -ExternalEmailAddress $forwardingaddress
}
#assign forwarding address
Set-Mailbox -ForwardingAddress $forwardingaddress -DeliverToMailboxAndForward $true
}
try this:
$mailboxlist = Get-Mailbox | Get-MsolUser |
Where-Object { ($_.islicensed -eq $true) -and ($_.firstname -ne $null) }
the UserPrincipalName of get-Msoluser accepts pipeline input by propertyname so you should just pipe the output of get-mailbox to get-msoluser
select-object should be preferred over format-* commands because of the type of objects that are created.
Use format* when you want to show data on the console.
The where-object clause allows you to create complex conditions so try to combine them where you can. This should be the flow of commands:
cmdlet | where-object { some condition(s)} | select properties

Powershell, filters and combining properties

I have an AD group that has other AD groups as members. Some of these groups may have "sub-groups" as well. I want to recursively descend through this group and find answers to several questions about the users in that group. For example:
Is User-X "enabled" in the overall group?
Does the account of User-X have values in ANY of the properties: AccountExpirationDate, accountExpires and Deleted?
I would like a displayed result that contains the properties: DisplayName, SamAccountName, AccountExpirationDate, accountExpires, Deleted and enabled (from the group object)
I have tried doing an "Add-Member" to insert the "enabled" value from get-ADgroupMember but I get the error:
Add-Member : Cannot add a member with the name "enabled" because a member with that name already exists. If you want to over
write the member anyway, use the Force parameter to overwrite it.
... but there is no such element as far as I can tell. I have renamed the member in the Add-Member to several very unique things but I still get the same error.
my current attempt is:
Import-Module ActiveDirectory
get-adgroupmember -Identity "My big AD group of groups" -recursive |
Where-Object -FilterScript {($_.ObjectClass -eq 'user')} |
ForEach-Object {
$enabled = $_.enebled
Get-ADUser `
-Filter {(name -eq $_.name)} `
-Properties DisplayName,SamAccountName,AccountExpirationDate,accountExpires,Deleted |
Add-Member -Name "myITGGroupEnabled" -Value $enabled -MemberType NoteProperty |
Where-Object `
-FilterScript {
($_.AccountExpirationDate -lt [datetime]::now) `
-OR ($_.accountExpires -eq $true) `
-OR ($_.Deleted -eq $true) `
-OR ($_.myITGGroupEnabled -eq $false)
}
Select-Object DisplayName,SamAccountName,AccountExpirationDate,accountExpires,Deleted,GroupEnabled
break
}
I am lost. Ideas?
I think all you need is a new PSObject. Like this:
...
Get-AdUser -Filter {name -eq $_.name} -Properties .... | % {
If ( ($_.AccountExpirationDate -lt [datetime]::now) `
-OR ($_.accountExpires -eq $true) `
-OR ($_.Deleted -eq $true) `
-OR ($_.myITGGroupEnabled -eq $false)) {
New-Object PSObject -Property #{MyITGGRoupEnabled=$enabled}
}
}