gscales / Exch-Rest

Office365 Outlook and Exchange 2016 REST PowerShell library
MIT License
52 stars 14 forks source link

Performing admin activities with single token #24

Open SeriesOfTubez opened 4 years ago

SeriesOfTubez commented 4 years ago

I'm sure I'm just missing something, but I've written a script to grab all users and pull their mailbox rules. The rule works fine against my account, but when I try to run against other mailboxes it tells me I don't have an access token.

I have the app registered, granted app permissions, created a self-signed cert, updated the manifest and used the cert and cert pw to connect to my mailbox.

Here's part of my script so you can see what I'm trying to do:


Connect-EXRMailbox -MailboxName 'mymailbox@domain.com' -certificateFileName C:\temp\cert.pfx -clientId xxxx-xxxx-xxxx-xxxx -certificateFilePassword $password -ResourceURL graph.microsoft.com

#get enabled users
$users = get-exrusers -filter 'userType eq ''member'' and accountEnabled eq true'

$forwardAbuseRules = @()

#loop through users
foreach ($user in $users){
   #get mailbox rules
    $mbxRules = Get-EXRInboxRule -MailboxName $user.userPrincipalName
    #loop through rules and add to RuleObj if "forwardTo" is a property of actions
    foreach ($rule in $mbxRules){
        If ($rule.actions | get-member forwardTo){
            $RuleObj = [PSCustomObject]@{
                UserName = $user.displayName
                UserUPN = $user.userPrincipalName
                RuleName = $rule.displayName
                RuleID = $rule.id
                RuleEnabled = $rule.isEnabled
                RuleAction = 'forwardTo'
                RuleConditions = $rule.conditions
                ForwardAddress = $rule.actions.forwardTo.emailAddress.address
                Domain = $rule.actions.forwardTo.emailAddress.address.split("@")[1]
                }

            $forwardAbuseRules += $ruleObj
            $ruleObj = $null
        }```

Here's what I get when I try to run against other mailboxes:
```Get-EXRAccessToken : No Access Token for user@domain.com
At line:49 char:20
+ ...           $AccessToken = Get-EXRAccessToken -MailboxName $MailboxName
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-EXRAccessToken

Get-EndPoint : Cannot bind argument to parameter 'AccessToken' because it is null.
At line:57 char:48
+         $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment  ...
+                                                ~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Get-EndPoint], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Get-EndPoint

App Token has expired
At line:124 char:25
+                         throw "App Token has expired"
+                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (App Token has expired:String) [], RuntimeException
    + FullyQualifiedErrorId : App Token has expired```
SeriesOfTubez commented 4 years ago

Figured out that I needed to use Get-ERAppOnlyToken to make the requests. Now I can't get my token to refresh when it expires. I put logic in my loop to check the expires_on property of the token and to get a new token if it's less than the current epoch time.

How are others handling token refreshes for long running scripts? I have 60k mailboxes to work with.

mhokanson commented 4 years ago

I only have about 30 mailboxes I'm dealing with at any given time, but I grab a new token for each mailbox. My script only run for about 15 minutes per mailbox (at most).

SeriesOfTubez commented 4 years ago

In my case, adding even 1 second to each loop iteration would add about 16 hours to my run time. Hopefully there is a more elegant solution.

mhokanson commented 4 years ago

Do you run the script in a single thread then? Have you tried multiple jobs running in parallel to reduce the overall runtime?

gscales commented 4 years ago

Sorry I've been on holidays and just catching up. Yes you need your own code to manage the certificate expiry for App only certs. Checking the epoch after each mailbox is this easy solution it shouldn't be that costly in terms of time unless your making external calls to work out the epoch's. A background task https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/start-job?view=powershell-6 would be the best approach but complex to handle and code well.

The other thing that may help speed wise is that this code is still using the beta endpoint so if you open the file (Get-EXRInboxRule in Exch-Rest\functions\mailrules) and modify line 53 from

$EndPoint = Get-EndPoint -AccessToken $AccessToken -Segment "users" -beta

to

$EndPoint = Get-EndPoint -AccessToken $AccessToken -Segment "users"

You should get better performance