Invoke-Office365.ps1

function Invoke-Office365
{
    <#
    .Synopsis
        Invokes commands within Office365
    .Description
        Invokes PowerShell commands within Office365
    .Example
        Invoke-Office365 -ScriptBlock { Get-Mailbox -Identity james.brundage@start-automating.com }
    .LINK
        http://help.outlook.com/en-us/140/cc952755.aspx
    #>

    [CmdletBinding(DefaultParameterSetName='Office365')]
    [OutputType([PSObject])]
    param(        
    # The credential for the Office365 account
    [Parameter(Position=1,ParameterSetName='ExchangeServer', ValueFromPipelineByPropertyName=$true)]
    [Parameter(Position=1,ParameterSetName='Office365', ValueFromPipelineByPropertyName=$true)]
    [Management.Automation.PSCredential]
    $Account,  
    
    # A list of account settings to use.
    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [string[]]
    $AccountSetting  =  @("Office365UserName", "Office365Password"),
    
    # The exchange server name. Only required if you're not invoking against Office365
    [Parameter(Mandatory=$true,Position=2,ParameterSetName='ExchangeServer', ValueFromPipelineByPropertyName=$true)]    
    [string]
    $ServerName,        
   
    # The script block to run in Office365
    [Parameter(Position=0)]
    [string[]]
    $ScriptBlock,
    
    # Any arguments to the script
    [Parameter(ValueFromPipeline=$true, ValueFromRemainingArguments=$true)]
    [PSObject[]]
    $ArgumentList,
    
    # The name of the session. If omitted, the name will contain the email used to connect to Office365.
    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [string]$Name,
    

    # If set, will run the command in a background job
    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [Switch]
    $AsJob,

    # If set, will create a fresh connection and destroy the connection when the command is complete.
    # This is slower, but less likely to make the exchange server experience a session bottleneck.
    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [Switch]
    $FreshConnection
    )
    
    begin {
        if (-not $script:JobCounter) {
            $script:JobCounter = 1 
        }
    }

    process {
        #region Copy Credential for Office365
        if (-not $Account) {
            if ($AccountSetting -and $accountSetting.Count -eq 2) {
                $username = Get-SecureSetting $accountSetting[0] -ValueOnly 
                $password = Get-SecureSetting $accountSetting[1] -ValueOnly 
                if ($username -and $password) {
                    $account = New-Object Management.Automation.PSCredential $username,(ConvertTo-SecureString -AsPlainText -Force $password )
                    $psBoundParameters.Account = $account
                }
            }
        }

        if (-not $account) {
            Write-Error "Must provide an Account or AccountSetting to connect to Office365"
            return
        }
        #endregion Copy Credential for Office365

        

        #region Launch Background Job if Needed
        if ($AsJob) {
            $myDefinition = [ScriptBLock]::Create("function Invoke-Office365 {
$(Get-Command Invoke-Office365 | Select-Object -ExpandProperty Definition)
}
"
)
            $null = $psBoundParameters.Remove('AsJob')            
            
            $myJob= [ScriptBLock]::Create("" + {
                param([Hashtable]$parameter) 
                
            } + $myDefinition + {
                
                Invoke-Office365 @parameter
            }) 
            if (-not $name) { 
                $name = "Office365Job${script:JobCounter}"
                $script:JobCounter++ 
            } 
            Start-Job -Name "$name " -ScriptBlock $myJob -ArgumentList $psBoundParameters 
            return
        }
        #endregion Launch Background Job if Needed

        
                
        #region Prepare Session Parameters
        if ($psCmdlet.ParameterSetName -eq 'Office365') {
            if ($script:ExchangeWebService -and $script:CachedCredential.Username -eq $script:CachedCredential) {
                return
            }    
            $ExchangeServer = "https://ps.outlook.com/"
            Write-Progress "Connecting to Office365" "$exchangeServer"
            $script:CachedCredential = $Account
        
            $newSessionParameters = @{
                ConnectionUri='https://ps.outlook.com/powershell'
                ConfigurationName='Microsoft.Exchange'
                Authentication='Basic'           
                Credential=$Account
                AllowRedirection=$true
                WarningAction = "silentlycontinue"
                SessionOption=(New-Object Management.Automation.Remoting.PSSessionOption -Property @{OpenTimeout="00:30:00"})
                Name = "https://$($Account.UserName)@ps.outlook.com/powershell"
                
            }
            
            $ExchangeServer = "https://ps.outlook.com/"            
        } else { 
            $ExchangeServer = $ServerName
            $newSessionParameters = @{
                ConnectionUri="https://$ServerName/powershell"
                ConfigurationName='Microsoft.Exchange'
                Authentication='Basic'           
                Credential=$Account
                AllowRedirection=$true
                WarningAction = "silentlycontinue"
                Name = "https://$ServerName/powershell"
                SessionOption=(New-Object Management.Automation.Remoting.PSSessionOption -Property @{OpenTimeout="00:30:00"})
                
            }
        }

        if ($psBoundParameters.Name) {
            $newSessionParameters.Name = $psBoundParameters.Name
        }
        #endregion Prepare Session Parameters


        #region Find or Create Session
        $existingSession = (Get-PSSession -Name $newSessionParameters.Name -ErrorAction SilentlyContinue)
        if ($FreshConnection -or (-not $existingSession ) -or ($existingSession.State -ne 'Opened')) {
            if ($existingSession) {
                $existingSession | Remove-PSSession
            }
            if (-not $FreshConnection) {
                $Session = New-PSSession @newSessionParameters -WarningVariable warning 
            }
        } else {
            $Session = $existingSession
        }
        #endregion Find or Create Session
        
        
        #region Invoke on Office365
        if (-not $Session -and -not $FreshConnection) { return } 
                
        foreach ($s in $scriptBlock) {
            $realScriptBlock  =[ScriptBlock]::Create($s)
            if (-not $realScriptBlock) { continue } 
            
            if (-not $FreshConnection) {
                Invoke-Command -Session $session -ArgumentList $Arguments -ScriptBlock $realScriptBlock  
            } else {
                $null = $newSessionParameters.Remove("Name")
                Start-Job -ArgumentList $Account, $realScriptBlock,$Arguments -ScriptBlock {
                    param([Management.Automation.PSCredential]$account, $realScriptBlock, $Arguments) 

                    $realScriptBlock = [ScriptBlock]::Create($realScriptBlock)
                    $newSessionParameters = @{
                        ConnectionUri='https://ps.outlook.com/powershell'
                        ConfigurationName='Microsoft.Exchange'
                        Authentication='Basic'           
                        Credential=$Account
                        AllowRedirection=$true
                        WarningAction = "silentlycontinue"
                        SessionOption=(New-Object Management.Automation.Remoting.PSSessionOption -Property @{OpenTimeout="00:30:00"})
                        
                
                    }

                    Invoke-Command @newsessionParameters -ArgumentList $Arguments -ScriptBlock $realScriptBlock 
                } | Wait-Job | Receive-Job
                
            }
            
        }

        if ($session -and $FreshConnection) {
            Remove-PSSession -Session $session
        }   
        #endregion Invoke on Office365
    }
}