Connect-O365.ps1
<#PSScriptInfo
.TITLE Connect-O365 .VERSION 1.6.1 .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 https://github.com/Josverl/Connect-O365 .ICONURI https://raw.githubusercontent.com/Josverl/Connect-O365/master/Connect-O365.png .EXTERNALMODULEDEPENDENCIES MSOnline, Microsoft.Online.SharePoint.PowerShell, AADRM, OfficeDevPnP.PowerShell.V16.Commands .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES V1.6.1 Resolve multiple Aliasses per parameter bug on some PS flavours V1.6.0 move to Github v1.5.9 update install OS version match logic to use [System.Environment]::OSVersion.Version correct DefaultParameterSetName=”Admin" Add -test option to check correct installation V1.5.8 Seperate configuration download info from script, Retrieve Module info from github. V1.5.7 Update to SPO shell build : 5111 1200 (March 2016) v1.5.6 Add -close parameter and fixed parameter sets, added inline help to parameters v1.5.5 Fix Language for MSOnline / AAD module v1.5 Add installation of dependent modules v1.4 Correct 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 any previously opened PS remote sessions (Exchange , Skype , Compliance Center) connect-O365 -close .EXAMPLE #Connect to MSOnline, and store securly store the credentials 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 #Download and Install dependent Modules connect-O365 -install #> [CmdletBinding(DefaultParameterSetName=”Admin")] [Alias("Connect-Office365")] [OutputType([int])] Param ( # Specify the (Admin) Account to authenticate with [Parameter(ParameterSetName="Admin",Mandatory=$true,Position=0)] [ValidateNotNullOrEmpty()] [string]$Account, # Save the account credentials for later use [Parameter(ParameterSetName="Admin",Mandatory=$false)] [switch]$Persist = $false, <# valid for Admin and Close #> #Connect to Azure AD aka MSOnline [Parameter(ParameterSetName="Admin",Mandatory=$false)] [Parameter(ParameterSetName="Close",Mandatory=$false)] [Alias("AzureAD")] [switch]$AAD = $true, #Connect to Exchange Online [Parameter(ParameterSetName="Admin",Mandatory=$false)] [Parameter(ParameterSetName="Close",Mandatory=$false)] [Alias("EXO")] [switch]$Exchange = $false, #Connect to Skype Online [Parameter(ParameterSetName="Admin",Mandatory=$false)] [Parameter(ParameterSetName="Close",Mandatory=$false)] [Alias("CSO")] [Alias("Lync")] [switch]$Skype = $false, #Connecto to SharePoint Online [Parameter(ParameterSetName="Admin",Mandatory=$false)] [Parameter(ParameterSetName="Close",Mandatory=$false)] [Alias("SPO")] [switch]$SharePoint = $false, #Load and connecto to the O365 Compliance center [Parameter(ParameterSetName="Admin",Mandatory=$false)] [Parameter(ParameterSetName="Close",Mandatory=$false)] [switch]$Compliance = $false, #Connect to Azure Rights Management [Parameter(ParameterSetName="Admin",Mandatory=$false)] [Parameter(ParameterSetName="Close",Mandatory=$false)] [Alias("AZRMS")] [Alias("RMS")] [switch]$AADRM = $false, #All Services [Parameter(ParameterSetName="Admin",Mandatory=$false)] [Parameter(ParameterSetName="Close",Mandatory=$false)] [switch]$All = $false, <# parameterset Close #> #Close all open Connections [Parameter(ParameterSetName="Close",Mandatory=$false)] [switch]$Close = $false, <# parameterset INstall #> #Download and Install the supporting Modules [Parameter(ParameterSetName="Install",Mandatory=$true)] [switch]$Install, #Specify the Language code of the modules to download ( not applicable to all modules) #Sample : -Language NL [Parameter(ParameterSetName="Install",Mandatory=$false)] [ValidatePattern("[a-zA-Z]{2}")] [Alias("Lang")] $Language = 'EN', #Specify the Language-Locale code of the modules to download ( not applicable to all modules) #Sample : -Language NL-NL #Sample : -Language EN-US [Parameter(ParameterSetName="Install",Mandatory=$false)] [ValidatePattern("[a-zA-Z]{2}-[a-zA-Z]{2}")] $LangCountry = $Host.CurrentUICulture.Name, #Specify where to download the installable MSI and EXE modules to [Parameter(ParameterSetName="Install",Mandatory=$false)] $Folder = $null, #'C:\Users\Jos\Downloads', # [Parameter(ParameterSetName="Install",Mandatory=$false)] # $InstallPreview = $true, # Save the account credentials for later use [Parameter(ParameterSetName="Test",Mandatory=$false)] [Parameter(ParameterSetName="Install",Mandatory=$false)] [switch]$Test = $false, #Not in a specific parameterset #Force asking for, and optionally force the Perstistance of the credentials. [Parameter(ParameterSetName="Admin",Mandatory=$false)] [Parameter(ParameterSetName="Install",Mandatory=$false)] [switch]$Force = $false ) function global:Store-myCreds ($username){ $Credential = Get-Credential -Credential $username $Store = "$env:USERPROFILE\creds\$USERNAME.txt" MkDir "$env:USERPROFILE\Creds" -ea 0 | Out-Null $Credential.Password | ConvertFrom-SecureString | Set-Content $store Write-Verbose "Saved credentials to $store" return $Credential } function global:Get-myCreds ($UserName , [switch]$Persist, [switch]$Force=$false){ $Store = "$env:USERPROFILE\creds\$USERNAME.txt" if ( (Test-Path $store) -AND $Force -eq $false ) { #use a stored password if found , unless -force is used to ask for and store a new password Write-Verbose "Retrieved credentials from $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 } } } # Write-Verbose -Message 'Connect-O365 Parameters :' $PSBoundParameters.GetEnumerator() | ForEach-Object { Write-Verbose -Message "$($PSItem)" } #Parameter logic for explicit ans implicit -All If ( $PsCmdlet.ParameterSetName -iin "Close","Admin" ) { if ( $all -eq $false -and $exchange -eq $false -and $skype -eq $false -and $Compliance -eq $false -and $SharePoint -eq $false -and $AADRM -eq $false) { Write-Verbose "Online Workload specified, assume all workloads" $all = $true } if ($all) { $AAD = $true $Exchange = $true $Skype= $true $Compliance = $true $SharePoint= $true $AADRM = $TRUE } } If ( $PsCmdlet.ParameterSetName -eq "Close") { write-verbose "Closing open session(s) for :" #Close Existing (remote Powershell Sessions) if ($Exchange) { write-verbose "- Exchange Online" Get-PSSession -Name "Exchange Online" -ea SilentlyContinue | Remove-PSSession } if ($Compliance) { write-verbose "- Compliance Center" Get-PSSession -Name "Compliance Center" -ea SilentlyContinue | Remove-PSSession } if ($Skype) { write-verbose "- Skype Online" Get-PSSession -Name "Skype Online" -ea SilentlyContinue| Remove-PSSession } if ($SharePoint) { write-verbose "- SharePoint Online" Try {Disconnect-SPOService -ErrorAction Ignore } catch{} write-verbose "- Disconnect PNP Powershell" #Also Disconnect PNPPowershell Try { Disconnect-SPOnline -ErrorAction Ignore } catch{} } If($AADRM) { write-verbose "- Azure RMS" Disconnect-AadrmService } return } If ( $PsCmdlet.ParameterSetName -eq "Admin") { $admincredentials = Get-myCreds $account -Persist:$Persist -Force:$Force if ($admincredentials -eq $null){ throw "A valid Tenant Admin Account is required." } 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 } } <# .Synopsis import a .psd1 file from a url ,( Github) .DESCRIPTION import a .psd1 file from a url and perform a safe expansion using a number of predefined variables. #> function Import-DataFile { param ( [Parameter(Mandatory)] [string] $Url ) try { #setup variables to use during configuration expansion $CPU = $env:PROCESSOR_ARCHITECTURE switch ($env:PROCESSOR_ARCHITECTURE) { 'x86' {$xcpu = 'x86' ; $bitness='32';} 'AMD64' {$xcpu = 'x64' ; $bitness='64'; } } $Filename = $URL.Split("/")[-1] try { wget -Uri $URL -OutFile "$env:TEMP\$Filename" } #failsafe if IE never been run catch { wget -Uri $URL -OutFile "$env:TEMP\$Filename" -UseBasicParsing } $content = Get-Content -Path "$env:TEMP\$Filename" -Raw -ErrorAction Stop Remove-Item "$env:TEMP\$Filename" -Force $scriptBlock = [scriptblock]::Create($content) # This list of approved cmdlets and variables is what is used when you import a module manifest [string[]] $allowedCommands = @( 'ConvertFrom-Json', 'Join-Path', 'Write-Verbose', 'Write-Host' ) #list of pedefined variables that can be used [string[]] $allowedVariables = @('language' ,'LangCountry', 'cpu','xcpu' , 'bitness' ) # This is the important line; it makes sure that your file is safe to run before you invoke it. # This protects you from injection attacks / etc, if someone has placed malicious content into # the data file. $scriptBlock.CheckRestrictedLanguage($allowedCommands, $allowedVariables, $true) # return & $scriptBlock } catch { throw } } If ( $PsCmdlet.ParameterSetName -eq "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" #load the required modules from a configuration file on GitHub $Components = Import-DataFile -url 'https://raw.githubusercontent.com/Josverl/Connect-O365/master/RequiredModuleInfo.psd1' #> # (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 [System.Environment]::OSVersion.Version.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 -Name $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)" } 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"} } } } #test is both a parameterset as well as an option for installation if ($test ) { Write-Host "Test Office 365 administrative components" -ForegroundColor DarkYellow $ServiceName ='Microsoft Online Services Sign-in Assistant' Write-Host "Validating Service: $ServiceName" $SignInAssistant = Get-Service -Name msoidsvc if ( $SignInAssistant -eq $null ) { Write-Warning "Service : '$ServiceName' is not installed" } else { if ($SignInAssistant.Status -ine "Running" ) { Write-Warning "Service '$ServiceName' is not running" } else { Write-Host "- OK" -ForegroundColor Green } } #test if all Local modules are installed correctly foreach ($module in @( "MSonline","SkypeOnlineConnector","Microsoft.Online.Sharepoint.PowerShell","OfficeDevPnP.PowerShell.V16.Commands","AADRM" ) ) { Write-Host "Validating Module : $Module" $M = Get-Module -Name $module -ListAvailable if ($m -eq $null) { Write-warning "Module '$Module' Could not be found." } else { Try { Import-Module -Name $module -Force -DisableNameChecking remove-module -Name $module -Force Write-Host "- OK" -ForegroundColor Green } catch { Write-warning "Module '$Module' could not be Loaded." } } } } |