Connect-O365.ps1

<#PSScriptInfo
 
.VERSION 1.5.4
 
.GUID a3515355-c4b6-4ab8-8fa4-2150bbb88c96
 
.AUTHOR Jos Verlinde [MSFT]
 
.COMPANYNAME Microsoft
 
.COPYRIGHT
 
.TAGS O365 RMS 'Exchange Online' 'SharePoint Online' 'Skype for Business' 'PnP-Powershell' 'Office 365'
   
.LICENSEURI
 
.PROJECTURI
 
.ICONURI https://onedrive.live.com/download?resid=5DF749BB977BF4D6!393423&authkey=!ADcrsZkNg64G36w&v=3&ithint=photo%2cpng
 
.EXTERNALMODULEDEPENDENCIES MSOnline, Microsoft.Online.SharePoint.PowerShell, AADRM, OfficeDevPnP.PowerShell.V16.Commands
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
v1.5 Add installation of dependent modules
v1.4 Corrected bug wrt compliance search, remove prior created remote powershell sessions
V1.3 Add dependend module information
V1.2 add try-catch for SPO PNP Powershell, as that is less common
V1.1 initial publication to scriptcenter
#>


<#
.Synopsis
   Connect to Office 365 and get ready to administer all services.
   Includes installation of PowerShell modules 1/4/2016
.DESCRIPTION
   Connect to Office 365 and most related services and get ready to administer all services.
   The commandlet supports saving your administrative credentials in a safe manner so that it can be used in unattended files
 
   Allows Powershell administration of : O365, Azure AD , Azure RMS, Exchange Online, SharePoint Online including PNP Powershell
       
.EXAMPLE
   connect-O365 -Account 'admin@contoso.com' -SharePoint
 
.EXAMPLE
   connect-O365 -Account 'admin@contoso.com' -SPO -EXO -Skype -Compliance -AADRM
 
.EXAMPLE
   #close PSremote sessions ( Exchnage , Skype
   connect-O365 -close
 
.EXAMPLE
 
   connect-O365 -Account 'admin@contoso.com' -Persist:$false
.EXAMPLE
 
   connect-O365 -Account 'admin@contoso.com'
   #retrieve credentials for use in other cmdlets
   $Creds = Get-myCreds 'admin@contoso.com'
 
.EXAMPLE
   connect-O365 -install
    
#>


[CmdletBinding()]
[Alias("COL")]
[OutputType([int])]
Param
(
    [CmdletBinding(DefaultParametersetName=”Admin")] 


    # Account to authenticate with
    [Parameter(ParameterSetName="Admin",Mandatory=$false,Position=0)]
    [string]$Account,
        
    # Save the account credentials for later use
    [Parameter(ParameterSetName="Admin",Mandatory=$false)]
    [switch]$Persist = $false, 

    #Azure AD
    [Parameter(ParameterSetName="Admin",Mandatory=$false)]
    [Alias("AzureAD")] 
    [switch]$AAD = $true, 

    #Exchange Online
    [Parameter(ParameterSetName="Admin",Mandatory=$false)]
    [Alias("EXO")] 
    [switch]$Exchange = $false, 

    #Skype Online
    [Parameter(ParameterSetName="Admin",Mandatory=$false)]
    [Alias("CSO")] 
    [switch]$Skype = $false, 
    #
    [Parameter(ParameterSetName="Admin",Mandatory=$false)]
    [Alias("SPO")] 
    [switch]$SharePoint = $false, 
        
    #Compliance center
    [Parameter(ParameterSetName="Admin",Mandatory=$false)]
    [switch]$Compliance = $false,

    #Azure Rights Management
    [Parameter(ParameterSetName="Admin",Mandatory=$false)]
    [Alias("AZRMS")] 
    [Alias("RMS")]
    [switch]$AADRM = $false,

    <# Add seperate parameterset #>

    #Install Modules
    [Parameter(ParameterSetName="Install",Mandatory=$true)]
    [switch]$Install,

    [Parameter(ParameterSetName="Install",Mandatory=$false)]
    [Alias("Lang")] 
    $Language = 'EN',
    [Parameter(ParameterSetName="Install",Mandatory=$false)]
    $LangCountry = 'EN-US',
    [Parameter(ParameterSetName="Install",Mandatory=$false)]
    $Folder = $null, #'C:\Users\Jos\Downloads',
    [Parameter(ParameterSetName="Install",Mandatory=$false)]
    $InstallPreview = $true,
    [Parameter(ParameterSetName="Install",Mandatory=$false)]
    [switch]$Force = $false

)

function global:Store-myCreds ($username){
    $Credential = Get-Credential -Credential $username
    MkDir "$env:USERPROFILE\Creds" -ea 0 | Out-Null
    $Credential.Password | ConvertFrom-SecureString | Set-Content $env:USERPROFILE\Creds\$USERNAME.txt
    return $Credential 
 }

function global:Get-myCreds ($UserName , [switch]$Persist){
    $Store = "$env:USERPROFILE\creds\$USERNAME.txt"
    if (Test-Path $store ) {            
        $Password = Get-Content $store | ConvertTo-SecureString
        $Credential = New-Object System.Management.Automation.PsCredential($UserName,$Password)
        return $Credential
    } else {
        if ($persist -and -not [string]::IsNullOrEmpty($UserName)) {
            $admincredentials  = Store-myCreds $UserName
            return $admincredentials
        } else {
            return Get-Credential -Credential $username
        }
    }
 }
 
if (-NOT $install) {

    $admincredentials = Get-myCreds $account -Persist:$Persist
    if ($admincredentials -eq $null){ throw "A valid Tenant Admin Account is required." } 

                        if($Close) {
    write-verbose "Closing open sessions for Exchange Online and Compliance Center"
    #Close Existing (remote Powershell Sessions)

    Get-PSSession -Name "Exchange Online" -ea SilentlyContinue | Remove-PSSession 
    Get-PSSession -Name "Compliance Center"  -ea SilentlyContinue | Remove-PSSession 
    Get-PSSession -Name "Skype Online" -ea SilentlyContinue| Remove-PSSession 
    }


                            if ( $AAD) {
    write-verbose "Connecting to Azure AD"
    #Imports the installed Azure Active Directory module.
    Import-Module MSOnline -Verbose:$false 
    if (-not (Get-Module MSOnline ) ) { Throw "Module not installed"}
    #Establishes Online Services connection to Office 365 Management Layer.
    Connect-MsolService -Credential $admincredentials
    }

    if ($Skype ){
        write-verbose "Connecting to Skype Online"
        #Imports the installed Skype for Business Online services module.
        Import-Module SkypeOnlineConnector -Verbose:$false  -Force 

        #Remove prior Session
        Get-PSSession -Name "Skype Online" -ea SilentlyContinue| Remove-PSSession 

        #Create a Skype for Business Powershell session using defined credential.
        $SkypeSession = New-CsOnlineSession -Credential $admincredentials -Verbose:$false
        $SkypeSession.Name="Skype Online"

        #Imports Skype for Business session commands into your local Windows PowerShell session.
        Import-PSSession -Session  $SkypeSession -AllowClobber -Verbose:$false

    }


    If ($SharePoint) {
        write-verbose "Connecting to SharePoint Online"
        if (!$AAD) {
            Throw "AAD Connection required"
        } else {
            #get tenant name for AAD Connection
            $tname= (Get-MsolDomain | ?{ $_.IsInitial -eq $true}).Name.Split(".")[0]
        }

        #Imports SharePoint Online session commands into your local Windows PowerShell session.
        Import-Module Microsoft.Online.Sharepoint.PowerShell -DisableNameChecking -Verbose:$false
        #lookup the tenant name based on the intial domain for the tenant
        Connect-SPOService -url https://$tname-admin.sharepoint.com -Credential $admincredentials

        try { 
            write-verbose "Connecting to SharePoint Online PNP"
            import-Module OfficeDevPnP.PowerShell.V16.Commands -DisableNameChecking -Verbose:$false
            Connect-SPOnline -Credential $admincredentials -url "https://$tname.sharepoint.com"
        } catch {
            Write-Warning "Unable to connecto to SharePoint Online using the PNP PowerShell module"
        }
    }


    if ($Exchange ) {
        write-verbose "Connecting to Exchange Online"

        #Remove prior Session
        Get-PSSession -Name "Exchange Online" -ea SilentlyContinue| Remove-PSSession 

        #Creates an Exchange Online session using defined credential.
        $ExchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $admincredentials -Authentication "Basic" -AllowRedirection
        $ExchangeSession.Name = "Exchange Online"
        #This imports the Office 365 session into your active Shell.
        Import-PSSession $ExchangeSession -AllowClobber -Verbose:$false

    }

    if ($Compliance) {
        write-verbose "Connecting to the Unified Compliance Center"
        #Remove prior Session
        Get-PSSession -Name "Compliance Center" -ea SilentlyContinue| Remove-PSSession 

        $PSCompliance = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid/ -Credential $AdminCredentials -Authentication Basic -AllowRedirection
        $PSCompliance.Name = "Compliance Center"
        Import-PSSession $PSCompliance -AllowClobber -Verbose:$false 

    }


    If ($AADRM) {
        write-verbose "Connecting to Azure Rights Management"    
        #Azure RMS

        import-module AADRM -Verbose:$false
        Connect-AadrmService -Credential $admincredentials 
    }

}



if ($install) {

# Get the location of the downloads folder
# Ref : http://stackoverflow.com/questions/25049875/getting-any-special-folder-path-in-powershell-using-folder-guid
Add-Type @"
    using System;
    using System.Runtime.InteropServices;
 
    public static class KnownFolder
    {
        public static readonly Guid Documents = new Guid( "FDD39AD0-238F-46AF-ADB4-6C85480369C7" );
        public static readonly Guid Downloads = new Guid( "374DE290-123F-4565-9164-39C4925E467B" );
    }
    public class shell32
    {
        [DllImport("shell32.dll")]
        private static extern int SHGetKnownFolderPath(
                [MarshalAs(UnmanagedType.LPStruct)]
                Guid rfid,
                uint dwFlags,
                IntPtr hToken,
                out IntPtr pszPath
            );
            public static string GetKnownFolderPath(Guid rfid)
            {
            IntPtr pszPath;
            if (SHGetKnownFolderPath(rfid, 0, IntPtr.Zero, out pszPath) != 0)
                return ""; // add whatever error handling you fancy
            string path = Marshal.PtrToStringUni(pszPath);
            Marshal.FreeCoTaskMem(pszPath);
            return path;
            }
    }
"@
 
    #Lookup downloads location
    if ($Folder -eq $null ) {$folder = [shell32]::GetKnownFolderPath([KnownFolder]::Downloads) }
    write-verbose "Download folder : $folder"

    $Components = ConvertFrom-Json @"
    {
      "AdminComponents": [
        {
            "Tag": "SIA",
            "Name": "Microsoft Online Services Sign-In Assistant for IT Professionals",
            "Source": "http://download.microsoft.com/download/5/0/1/5017D39B-8E29-48C8-91A8-8D0E4968E6D4/en/msoidcli_64.msi",
            "Type": "MSI",
            "ID": "{D8AB93B0-6FBF-44A0-971F-C0669B5AE6DD}"
        } ,
        {
            "Tag" : "AAD",
            "Module" : "MSOnline",
            "Name": "Windows Azure Active Directory Module for Windows PowerShell",
            "Source": "https://bposast.vo.msecnd.net/MSOPMW/Current/amd64/AdministrationConfig-$LANG.msi",
            "Type": "MSI",
            "ID": "{43CC9C53-A217-4850-B5B2-8C347920E500}"
        },
        {
            "Tag": "SKYPE",
            "Module": "SkypeOnlineConnector",
            "Name": "Skype for Business Online, Windows PowerShell Module",
            "Source": "https://download.microsoft.com/download/2/0/5/2050B39B-4DA5-48E0-B768-583533B42C3B/SkypeOnlinePowershell.exe",
            "Type": "EXE",
            "SetupOptions" : "/Install /Passive",
            "ID": "{D7334D5D-0FA2-4DA9-8D8A-883F8C0BD41B}"
        },
        {
            "Tag": "SPO",
            "Module" : "Microsoft.Online.SharePoint.PowerShell",
            "Name": "SharePoint Online Management Shell",
            "Source" : "https://download.microsoft.com/download/0/2/E/02E7E5BA-2190-44A8-B407-BC73CA0D6B87/sharepointonlinemanagementshell_4915-1200_x64_$LangCountry.msi",
            "Type": "MSI",
            "Version" : "16.0.4915.1200",
            "Web": "https://www.microsoft.com/en-us/download/confirmation.aspx?id=35588&6B49FDFB-8E5B-4B07-BC31-15695C5A2143=1",
            "ID": "{95160000-115B-0409-1000-0000000FF1CE}"
        },
        {
            "Tag": "RMS",
            "Module" : "aadrm",
            "Name": "Windows Azure AD Rights Management Administration",
            "Source" : "https://download.microsoft.com/download/1/6/6/166A2668-2FA6-4C8C-BBC5-93409D47B339/WindowsAzureADRightsManagementAdministration_x64.exe",
            "Type": "EXE",
            "Version" : " 1.0.1443.901",
            "Web": "https://www.microsoft.com/en-us/download/confirmation.aspx?id=30339",
            "ID": "{6EACEC8B-7174-4180-B8D6-528D7B2C09F0}"
        },
        {
            "Tag": "PNPPS",
            "Preview" : "Yes",
            "Module": "OfficeDevPnP.PowerShell.V16.Commands",
            "Name": "OfficeDevPnP.PowerShell",
            "Source" : "PSGallery",
            "Type": "Module",
            "Web": "https://github.com/OfficeDev/PnP-PowerShell"
        }
        
        ],
        "Exclude" : [
        {
            "Tag": "WMF5-Preview",
            "Preview" : "Yes",
            "Module" : "WMF5",
            "Type": "MSI",
            "Name": "Windows Management Framework 5.0 Production Preview",
            "Source" : "https://download.microsoft.com/download/3/F/D/3FD04B49-26F9-4D9A-8C34-4533B9D5B020/Win8.1AndW2K12R2-KB3066437-x64.msu",
            "SetupOptions" : "/quiet",
            "OS": "6,8",
            "XVersion" : "",
            "Web": "https://www.microsoft.com/en-us/download/confirmation.aspx?id=30339",
            "ID": "{BE4B4004-DE97-4185-A2B4-C147DAC9AD2C}",
            "Source2" : "https://download.microsoft.com/download/3/F/D/3FD04B49-26F9-4D9A-8C34-4533B9D5B020/W2K12-KB3066438-x64.msu"
        }

      ]
    }
"@



    # (Get-Module aadrm -ListAvailable).Version
    <# use the below in order to update relevant information
    Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | sort -Property DisplayName | select PSChildName, DisplayName, Publisher, DisplayVersion
    #>

    foreach ($c  in $Components.AdminComponents) {
        if ($c.Preview -ieq "Yes" -and $InstallPreview -eq $false) {
                write-host -f Gray "Skip Preview component : $($c.Name)"
                continue; 
        }
        #IF OS Major Specified , and if the current OS Matches the specified OS
        if ($c.OS) { 
            if ( ($c.OS).Split(",") -notcontains $PSVersionTable.BuildVersion.Major) { 
                write-host -f Gray "OS mismatch, Skip component : $($c.Name)"
                continue; 
            } 
        }

        switch ($c.Type.ToUpper() )
        {
            {$_ -in "EXE","MSI"} {
                Write-Verbose "EXE or MSI package"
                $AppInfo = Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$($c.ID)" -ErrorAction SilentlyContinue
                $Installed = $false;
                if ($AppInfo ) { 
                    $Installed = $appInfo.Displayname -ne $null
                }
                
                if (-not $Installed -or $force) {
                    # Filename
                    $file = Split-Path $c.Source -Leaf
                    #path in downloads
                    $msi = join-path $folder $file
                    #remove existing item prior to Downloading
                    Remove-Item $msi -Force -ea SilentlyContinue
                    try { 
                        #download it
                        Write-Verbose "Download package"
                        Invoke-WebRequest $c.Source -OutFile $msi
                        if ( Test-Path $msi ) { 
                            $Sign = Get-AuthenticodeSignature $msi
                            if ( $Sign.Status -eq 'Valid' -and $sign.SignerCertificate.DnsNameList[0].Unicode -eq 'Microsoft Corporation' ) {
                                if ($force) { 
                                    #de-install before re-install
                                    write-host -ForegroundColor Yellow "Removing current $($c.Type) package"
                                    Start-Process -FilePath "msiexec" -ArgumentList "/uninstall $($c.ID) " -Wait
                                }
                                try { 
                                    if ($c.Type -ieq "MSI" ) {
                                        Write-Verbose "Install MSI package : $msi"
                                        Start-Process -FilePath "msiexec" -ArgumentList "/package $msi /passive" -Wait
                                    } else {
                                        $Options = "/Passive"
                                        if ($c.Setup ) { $Options = $c.SetupOptions }
                                        Write-Verbose "Install EXE package : $msi $options"                                        
                                        Start-Process -FilePath $MSI -ArgumentList $options -Wait
                                    }

                                        $AppInfo = Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$($c.ID)" -ErrorAction SilentlyContinue
                                        Write-Host $c.Name "Version :" $AppInfo.DisplayVersion " was installed" -f Green
                                } catch {
                                    Write-warning "$($c.Name) could not be installed"
                                    #Open in browser
                                    if (-not [string]::IsNullOrEmpty($c.Web)){
                                        Start-Process $c.Web
                                    }
                                } 
                            }
                        }
                    } catch {
                        Write-Warning "could not install: $($c.Name)"
                    }
                } 
                else { 
                    Write-Host $c.Name "Version :" $AppInfo.DisplayVersion " is already installed"
                }
            }
            "MODULE" {
                # Add check for PS5 / WMF 5
                if (Get-Command install-module) { 
                    #check for installed version of this module
                    $Current  = Get-Module $c.Module -ListAvailable
                    $Source = Find-Module -Name $c.Module -Repository $c.Source -Verbose:$false
                    if ( $Current -eq $null ) {
                        write-verbose "Preparing to install module $($c.Module)"
                        #Not yet installed , find source
                        #if not installed or newer version avaialble
                        if( $Source -and $Current -eq $null -or  ($Source.Version -GT $Current.Version) ) {
                            #install it
                            $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") 
                            #Check Admin Perms
                            if (-not $IsAdmin) {
                                Write-Verbose "Start PS elevated to admin to run the install"
                                Start-Process "$psHome\powershell.exe" -Verb Runas -ArgumentList "-Command Install-Module -Name $($c.Module) -Repository $($c.Source)" 

                            } else {
                                Install-Module -InputObject $source
                            }
                            $Now = Get-Module $c.Module -ListAvailable -Verbose:$false
                            if ($now){
                                Write-Host "Installed $($now.Name) version : $($Now.Version)"
                            }
                        } else {
                            #Could not be found
                            Write-warning  "The Module $($c.Name) Could not be located"
                            #Open in browser
                            if (-not [string]::IsNullOrEmpty($c.Web)){
                                Start-Process $c.Web
                            }
                        } 
                    } else {
                        #version already installed
                        if ($Source.Version -gt $Current.Version -or $force ) {
                            write-verbose "Updating Module $($c.Module)"
                            if (-not $IsAdmin) {
                                Write-Verbose "Start PS elevated to admin to run the install"
                                Start-Process "$psHome\powershell.exe" -Verb Runas -ArgumentList "-Command update-Module -Name $($c.Module) -Force:$force" 
                            } else {
                                update-Module -InputObject $source -Force:$force
                            }
                        }
                        else 
                        {
                            Write-verbose "$($c.Name) Version : $($Current.Version) is already installed"
                        }
                        $NOW  = Get-Module $c.Module -ListAvailable
                        Write-Host $c.Name "Version :" $NOW.Version" is now installed"
                                       
                    }
                } 
                else 
                { 
                    #No PS5 / WMF 5
                    Write-warning  "The Module $($c.Name) cannot be installed automatically. Please install manually or install WMF 5 (preview)"
                    #Open in browser
                    if (-not [string]::IsNullOrEmpty($c.Web)){
                        Start-Process $c.Web
                    }
                }
            }
        default { Write-Warning "Unknown component type"}
        }
    }
}